galaxy-commits
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 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
- 15302 discussions
commit/galaxy-central: greg: Add a tool shed container to display successful repository and tool dependency installations and improve statistics and reporting in the tool shed's install and test runs.
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a0e80a712c37/
Changeset: a0e80a712c37
User: greg
Date: 2013-12-19 18:49:50
Summary: Add a tool shed container to display successful repository and tool dependency installations and improve statistics and reporting in the tool shed's install and test runs.
Affected #: 8 files
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 lib/galaxy/model/tool_shed_install/__init__.py
--- a/lib/galaxy/model/tool_shed_install/__init__.py
+++ b/lib/galaxy/model/tool_shed_install/__init__.py
@@ -265,8 +265,7 @@
"""Return the repository's tool dependencies that are currently installed, but possibly in an error state."""
installed_dependencies = []
for tool_dependency in self.tool_dependencies:
- if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED,
- ToolDependency.installation_status.ERROR ]:
+ if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED ]:
installed_dependencies.append( tool_dependency )
return installed_dependencies
@@ -442,6 +441,16 @@
return dependencies_being_installed
@property
+ def tool_dependencies_installed_or_in_error( self ):
+ """Return the repository's tool dependencies that are currently installed, but possibly in an error state."""
+ installed_dependencies = []
+ for tool_dependency in self.tool_dependencies:
+ if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED,
+ ToolDependency.installation_status.ERROR ]:
+ installed_dependencies.append( tool_dependency )
+ return installed_dependencies
+
+ @property
def tool_dependencies_missing_or_being_installed( self ):
dependencies_missing_or_being_installed = []
for tool_dependency in self.tool_dependencies:
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -2733,7 +2733,7 @@
def build_dependency_shell_commands( self ):
"""Return a list of commands to be run to populate the current environment to include this tools requirements."""
if self.tool_shed_repository:
- installed_tool_dependencies = self.tool_shed_repository.installed_tool_dependencies
+ installed_tool_dependencies = self.tool_shed_repository.tool_dependencies_installed_or_in_error
else:
installed_tool_dependencies = None
return self.app.toolbox.dependency_manager.dependency_shell_commands( self.requirements,
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -255,7 +255,7 @@
tool_shed_repository.uninstalled = True
# Remove all installed tool dependencies and tool dependencies stuck in the INSTALLING state, but don't touch any
# repository dependencies.
- tool_dependencies_to_uninstall = tool_shed_repository.installed_tool_dependencies
+ tool_dependencies_to_uninstall = tool_shed_repository.tool_dependencies_installed_or_in_error
tool_dependencies_to_uninstall.extend( tool_shed_repository.tool_dependencies_being_installed )
for tool_dependency in tool_dependencies_to_uninstall:
uninstalled, error_message = tool_dependency_util.remove_tool_dependency( trans.app, tool_dependency )
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 lib/tool_shed/util/container_util.py
--- a/lib/tool_shed/util/container_util.py
+++ b/lib/tool_shed/util/container_util.py
@@ -21,26 +21,29 @@
self.key = key
self.label = label
self.parent = parent
+ self.current_repository_installation_errors = []
+ self.current_repository_successful_installations = []
self.description = None
self.datatypes = []
+ self.failed_tests = []
self.folders = []
+ self.invalid_data_managers = []
self.invalid_repository_dependencies = []
self.invalid_tool_dependencies = []
self.invalid_tools = []
- self.current_repository_installation_errors = []
- self.repository_installation_errors = []
- self.tool_dependency_installation_errors = []
- self.valid_tools = []
- self.valid_data_managers = []
- self.invalid_data_managers = []
- self.tool_dependencies = []
- self.failed_tests = []
self.missing_test_components = []
self.not_tested = []
self.passed_tests = []
+ self.readme_files = []
+ self.repository_dependencies = []
+ self.repository_installation_errors = []
+ self.repository_successful_installations = []
self.test_environments = []
- self.repository_dependencies = []
- self.readme_files = []
+ self.tool_dependencies = []
+ self.tool_dependency_installation_errors = []
+ self.tool_dependency_successful_installations = []
+ self.valid_tools = []
+ self.valid_data_managers = []
self.workflows = []
def contains_folder( self, folder ):
@@ -230,6 +233,17 @@
self.error_message = error_message
+class RepositorySuccessfulInstallation( object ):
+ """Repository installation object"""
+
+ def __init__( self, id=None, tool_shed=None, name=None, owner=None, changeset_revision=None ):
+ self.id = id
+ self.tool_shed = tool_shed
+ self.name = name
+ self.owner = owner
+ self.changeset_revision = changeset_revision
+
+
class TestEnvironment( object ):
"""Tool test environment object"""
@@ -294,6 +308,16 @@
self.error_message = error_message
+class ToolDependencySuccessfulInstallation( object ):
+ """Tool dependency installation object"""
+
+ def __init__( self, id=None, type=None, name=None, version=None, installation_directory=None ):
+ self.id = id
+ self.type = type
+ self.name = name
+ self.version = version
+ self.installation_directory = installation_directory
+
class Workflow( object ):
"""Workflow object."""
@@ -1097,7 +1121,8 @@
# {'python_version': '2.7.4', 'tool_shed_mercurial_version': '2.2.3', 'system': 'Linux 3.8.0-30-generic',
# 'tool_shed_database_version': 21, 'architecture': 'x86_64', 'galaxy_revision': '11573:a62c54ddbe2a',
# 'galaxy_database_version': 117, 'time_tested': '2013-12-03 09:11:48', 'tool_shed_revision': '11556:228156daa575'},
- # 'installation_errors': {'current_repository': [], 'repository_dependencies': [], 'tool_dependencies': []}
+ # 'installation_errors': {'current_repository': [], 'repository_dependencies': [], 'tool_dependencies': []},
+ # 'successful_installations': {'current_repository': [], 'repository_dependencies': [], 'tool_dependencies': []}
# }
test_environment_dict = tool_test_results_dict.get( 'test_environment', None )
if test_environment_dict is None:
@@ -1335,6 +1360,82 @@
version=td_version,
error_message=td_error_message )
tool_dependencies_folder.tool_dependency_installation_errors.append( tool_dependency_installation_error )
+ successful_installation_dict = tool_test_results_dict.get( 'successful_installations', {} )
+ if len( successful_installation_dict ) > 0:
+ # 'successful_installation':
+ # {'current_repository': [],
+ # 'repository_dependencies': [],
+ # 'tool_dependencies':
+ # [{'installation_directory': 'some path' 'type': 'package', 'name': 'MIRA', 'version': '4.0'}]
+ # }
+ current_repository_successful_installation_dicts = successful_installation_dict.get( 'current_repository', [] )
+ repository_dependency_successful_installation_dicts = successful_installation_dict.get( 'repository_dependencies', [] )
+ tool_dependency_successful_installation_dicts = successful_installation_dict.get( 'tool_dependencies', [] )
+ if len( current_repository_successful_installation_dicts ) > 0 or \
+ len( repository_dependency_successful_installation_dicts ) > 0 or \
+ len( tool_dependency_successful_installation_dicts ) > 0:
+ repository_installation_success_id = 0
+ folder_id += 1
+ successful_installation_base_folder = Folder( id=folder_id,
+ key='successful_installations',
+ label='Successful installations',
+ parent=containing_folder )
+ containing_folder.folders.append( successful_installation_base_folder )
+ # Displaying the successful installation of the current repository is not really necessary, so we'll skip it.
+ if len( repository_dependency_successful_installation_dicts ) > 0:
+ folder_id += 1
+ repository_dependencies_folder = Folder( id=folder_id,
+ key='repository_dependency_successful_installations',
+ label='Repository dependencies',
+ parent=successful_installation_base_folder )
+ successful_installation_base_folder.folders.append( repository_dependencies_folder )
+ for repository_dependency_successful_installation_dict in repository_dependency_successful_installation_dicts:
+ repository_installation_success_id += 1
+ try:
+ rd_tool_shed = str( repository_dependency_successful_installation_dict.get( 'tool_shed', '' ) )
+ rd_name = str( repository_dependency_successful_installation_dict.get( 'name', '' ) )
+ rd_owner = str( repository_dependency_successful_installation_dict.get( 'owner', '' ) )
+ rd_changeset_revision = str( repository_dependency_successful_installation_dict.get( 'changeset_revision', '' ) )
+ except Exception, e:
+ rd_tool_shed = 'unknown'
+ rd_name = 'unknown'
+ rd_owner = 'unknown'
+ rd_changeset_revision = 'unknown'
+ repository_installation_success = \
+ RepositoryInstallationSuccess( id=repository_installation_success_id,
+ tool_shed=rd_tool_shed,
+ name=rd_name,
+ owner=rd_owner,
+ changeset_revision=rd_changeset_revision )
+ repository_dependencies_folder.repository_successful_installations.append( repository_installation_success )
+ if len( tool_dependency_successful_installation_dicts ) > 0:
+ # [{'installation_directory': 'some path' 'type': 'package', 'name': 'MIRA', 'version': '4.0'}]
+ folder_id += 1
+ tool_dependencies_folder = Folder( id=folder_id,
+ key='tool_dependency_successful_installations',
+ label='Tool dependencies',
+ parent=successful_installation_base_folder )
+ successful_installation_base_folder.folders.append( tool_dependencies_folder )
+ tool_dependency_error_id = 0
+ for tool_dependency_successful_installation_dict in tool_dependency_successful_installation_dicts:
+ tool_dependency_error_id += 1
+ try:
+ td_type = str( tool_dependency_successful_installation_dict.get( 'type', '' ) )
+ td_name = str( tool_dependency_successful_installation_dict.get( 'name', '' ) )
+ td_version = str( tool_dependency_successful_installation_dict.get( 'version', '' ) )
+ td_installation_directory = tool_dependency_successful_installation_dict.get( 'installation_directory', '' )
+ except Exception, e:
+ td_type = 'unknown'
+ td_name = 'unknown'
+ td_version = 'unknown'
+ td_installation_directory = str( e )
+ tool_dependency_successful_installation = \
+ ToolDependencySuccessfulInstallation( id=tool_dependency_error_id,
+ type=td_type,
+ name=td_name,
+ version=td_version,
+ installation_directory=td_installation_directory )
+ tool_dependencies_folder.tool_dependency_successful_installations.append( tool_dependency_successful_installation )
else:
tool_test_results_root_folder = None
return folder_id, tool_test_results_root_folder
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 templates/webapps/tool_shed/repository/common.mako
--- a/templates/webapps/tool_shed/repository/common.mako
+++ b/templates/webapps/tool_shed/repository/common.mako
@@ -396,17 +396,27 @@
%for tool_dependency_installation_error in folder.tool_dependency_installation_errors:
${render_tool_dependency_installation_error( tool_dependency_installation_error, pad, my_row, row_counter )}
%endfor
- %endif
+ %endif
+ %if folder.tool_dependency_successful_installations:
+ %for tool_dependency_successful_installation in folder.tool_dependency_successful_installations:
+ ${render_tool_dependency_successful_installation( tool_dependency_successful_installation, pad, my_row, row_counter )}
+ %endfor
+ %endif
%if folder.repository_installation_errors:
%for repository_installation_error in folder.repository_installation_errors:
${render_repository_installation_error( repository_installation_error, pad, my_row, row_counter, is_current_repository=False )}
%endfor
- %endif
+ %endif
%if folder.current_repository_installation_errors:
%for repository_installation_error in folder.current_repository_installation_errors:
${render_repository_installation_error( repository_installation_error, pad, my_row, row_counter, is_current_repository=True )}
%endfor
- %endif
+ %endif
+ %if folder.repository_successful_installations:
+ %for repository_successful_installation in folder.repository_successful_installations:
+ ${render_repository_successful_installation( repository_successful_installation, pad, my_row, row_counter )}
+ %endfor
+ %endif
</%def><%def name="render_datatype( datatype, pad, parent, row_counter, row_is_header=False )">
@@ -714,6 +724,36 @@
%></%def>
+<%def name="render_tool_dependency_successful_installation( successful_installation, pad, parent, row_counter, row_is_header=False )">
+ <%
+ encoded_id = trans.security.encode_id( successful_installation.id )
+ %>
+ <tr class="datasetRow"
+ %if parent is not None:
+ parent="${parent}"
+ %endif
+ id="libraryItem-rtdsi-${encoded_id}">
+ <td style="padding-left: ${pad+20}px;">
+ <table id="test_environment">
+ <tr bgcolor="#FFFFCC">
+ <th>Type</th><th>Name</th><th>Version</th>
+ </tr>
+ <tr>
+ <td>${successful_installation.name | h}</td>
+ <td>${successful_installation.type | h}</td>
+ <td>${successful_installation.version | h}</td>
+ </tr>
+ <tr><th>Installation directory</th></tr>
+ <tr><td colspan="3">${successful_installation.installation_directory | h}</td></tr>
+ </table>
+ </td>
+ </tr>
+ <%
+ my_row = row_counter.count
+ row_counter.increment()
+ %>
+</%def>
+
<%def name="render_repository_installation_error( installation_error, pad, parent, row_counter, row_is_header=False, is_current_repository=False )"><%
from galaxy.util import unicodify
@@ -748,6 +788,37 @@
%></%def>
+<%def name="render_repository_successful_installation( successful_installation, pad, parent, row_counter, row_is_header=False, is_current_repository=False )">
+ <%
+ encoded_id = trans.security.encode_id( successful_installation.id )
+ %>
+ <tr class="datasetRow"
+ %if parent is not None:
+ parent="${parent}"
+ %endif
+ id="libraryItem-rrsi-${encoded_id}">
+ <td style="padding-left: ${pad+20}px;">
+ <table id="test_environment">
+ %if not is_current_repository:
+ <tr bgcolor="#FFFFCC">
+ <th>Tool shed</th><th>Name</th><th>Owner</th><th>Changeset revision</th>
+ </tr>
+ <tr>
+ <td>${successful_installation.tool_shed | h}</td>
+ <td>${successful_installation.name | h}</td>
+ <td>${successful_installation.owner | h}</td>
+ <td>${successful_installation.changeset_revision | h}</td>
+ </tr>
+ %endif
+ </table>
+ </td>
+ </tr>
+ <%
+ my_row = row_counter.count
+ row_counter.increment()
+ %>
+</%def>
+
<%def name="render_not_tested( not_tested, pad, parent, row_counter, row_is_header=False )"><%
encoded_id = trans.security.encode_id( not_tested.id )
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -119,7 +119,6 @@
# a definition for that tool shed.
galaxy_tool_shed_url = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_SHED_URL', None )
tool_shed_api_key = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY', None )
-exclude_list_file = os.environ.get( 'GALAXY_INSTALL_TEST_EXCLUDE_REPOSITORIES', 'install_test_exclude.xml' )
if 'GALAXY_INSTALL_TEST_SECRET' not in os.environ:
galaxy_encode_secret = 'changethisinproductiontoo'
@@ -182,6 +181,46 @@
return passed_tests
return []
+def display_repositories_by_owner( repository_dicts ):
+ # Group summary display by repository owner.
+ repository_dicts_by_owner = {}
+ for repository_dict in repository_dicts:
+ name = str( repository_dict[ 'name' ] )
+ owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ if owner in repository_dicts_by_owner:
+ repository_dicts_by_owner[ owner ].append( repository_dict )
+ else:
+ repository_dicts_by_owner[ owner ] = [ repository_dict ]
+ # Display grouped summary.
+ for owner, grouped_repository_dicts in repository_dicts_by_owner.items():
+ print "# "
+ for repository_dict in grouped_repository_dicts:
+ name = str( repository_dict[ 'name' ] )
+ owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ print "# Revision %s of repository %s owned by %s" % ( changeset_revision, name, owner )
+
+def display_tool_dependencies_by_name( tool_dependency_dicts ):
+ # Group summary display by repository owner.
+ tool_dependency_dicts_by_name = {}
+ for tool_dependency_dict in tool_dependency_dicts:
+ name = str( tool_dependency_dict[ 'name' ] )
+ type = str( tool_dependency_dict[ 'type' ] )
+ version = str( tool_dependency_dict[ 'version' ] )
+ if name in tool_dependency_dicts_by_name:
+ tool_dependency_dicts_by_name[ name ].append( tool_dependency_dict )
+ else:
+ tool_dependency_dicts_by_name[ name ] = [ tool_dependency_dict ]
+ # Display grouped summary.
+ for name, grouped_tool_dependency_dicts in tool_dependency_dicts_by_name.items():
+ print "# "
+ for tool_dependency_dict in grouped_tool_dependency_dicts:
+ type = str( tool_dependency_dict[ 'type' ] )
+ name = str( tool_dependency_dict[ 'name' ] )
+ version = str( tool_dependency_dict[ 'version' ] )
+ print "# %s %s version %s" % ( type, name, version )
+
def get_api_url( base, parts=[], params=None ):
if 'api' in parts and parts.index( 'api' ) != 0:
parts.pop( parts.index( 'api' ) )
@@ -474,18 +513,14 @@
def initialize_install_and_test_statistics_dict( test_framework ):
# Initialize a dictionary for the summary that will be printed to stdout.
install_and_test_statistics_dict = {}
+ install_and_test_statistics_dict[ 'total_repositories_processed' ] = 0
+ install_and_test_statistics_dict[ 'successful_repository_installations' ] = []
+ install_and_test_statistics_dict[ 'successful_tool_dependency_installations' ] = []
+ install_and_test_statistics_dict[ 'repositories_with_installation_error' ] = []
+ install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ] = []
if test_framework == REPOSITORIES_WITH_TOOLS:
- install_and_test_statistics_dict[ 'total_repositories_processed' ] = 0
install_and_test_statistics_dict[ 'all_tests_passed' ] = []
install_and_test_statistics_dict[ 'at_least_one_test_failed' ] = []
- install_and_test_statistics_dict[ 'successful_installations' ] = []
- install_and_test_statistics_dict[ 'repositories_with_installation_error' ] = []
- install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ] = []
- elif test_framework == TOOL_DEPENDENCY_DEFINITIONS:
- install_and_test_statistics_dict[ 'total_repositories_processed' ] = 0
- install_and_test_statistics_dict[ 'successful_installations' ] = []
- install_and_test_statistics_dict[ 'repositories_with_installation_error' ] = []
- install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ] = []
return install_and_test_statistics_dict
def initialize_tool_tests_results_dict( app, tool_test_results_dict ):
@@ -516,6 +551,9 @@
tool_test_results_dict[ 'installation_errors' ] = dict( current_repository=[],
repository_dependencies=[],
tool_dependencies=[] )
+ tool_test_results_dict[ 'successful_installations' ] = dict( current_repository=[],
+ repository_dependencies=[],
+ tool_dependencies=[] )
return tool_test_results_dict
def install_repository( app, repository_dict ):
@@ -563,7 +601,17 @@
def parse_exclude_list( xml_filename ):
"""Return a list of repositories to exclude from testing."""
- # This method should return a list with the following structure:
+ # This method expects an xml document that looks something like this:
+ # <?xml version="1.0"?>
+ # <blacklist>
+ # <repositories tool_shed="http://testtoolshed.g2.bx.psu.edu">
+ # <reason>
+ # <text>Some reason</text>
+ # <repository name="some_name" owner="some_owner" />
+ # </reason>
+ # </repositories>
+ # </blacklist>
+ # A list is returned with the following structure:
# [{ 'reason': The default reason or the reason specified in this section,
# 'repositories': [( name, owner, changeset revision if changeset revision else None ),
# ( name, owner, changeset revision if changeset revision else None )]}]
@@ -640,26 +688,6 @@
result = test_runner.run( tests )
return result, test_config.plugins._plugins
-def show_summary_output( repository_dicts ):
- # Group summary display by repository owner.
- repository_dicts_by_owner = {}
- for repository_dict in repository_dicts:
- name = str( repository_dict[ 'name' ] )
- owner = str( repository_dict[ 'owner' ] )
- changeset_revision = str( repository_dict[ 'changeset_revision' ] )
- if owner in repository_dicts_by_owner:
- repository_dicts_by_owner[ owner ].append( repository_dict )
- else:
- repository_dicts_by_owner[ owner ] = [ repository_dict ]
- # Display grouped summary.
- for owner, grouped_repository_dicts in repository_dicts_by_owner.items():
- print "# "
- for repository_dict in grouped_repository_dicts:
- name = str( repository_dict[ 'name' ] )
- owner = str( repository_dict[ 'owner' ] )
- changeset_revision = str( repository_dict[ 'changeset_revision' ] )
- print "# Revision %s of repository %s owned by %s" % ( changeset_revision, name, owner )
-
def uninstall_repository_and_repository_dependencies( app, repository_dict ):
"""Uninstall a repository and all of its repository dependencies."""
# This method assumes that the repositor defined by the received repository_dict is not a repository
@@ -740,7 +768,7 @@
tool_dependency_install_path = tool_dependency.installation_directory( app )
uninstalled, error_message = tool_dependency_util.remove_tool_dependency( app, tool_dependency )
if error_message:
- log.debug( 'There was an error attempting to remove directory: %s' % str( tool_dependency_install_path ) )
+ log.debug( 'Error attempting to remove directory: %s' % str( tool_dependency_install_path ) )
log.debug( error_message )
else:
log.debug( 'Successfully removed tool dependency installation directory: %s' % str( tool_dependency_install_path ) )
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -66,6 +66,9 @@
default_galaxy_locales = 'en'
default_galaxy_test_file_dir = "test-data"
os.environ[ 'GALAXY_INSTALL_TEST_TMP_DIR' ] = galaxy_test_tmp_dir
+# This file is copied to the Galaxy root directory by buildbot.
+# It is managed by cfengine and is not locally available.
+exclude_list_file = os.environ.get( 'GALAXY_INSTALL_TEST_EXCLUDE_REPOSITORIES', 'repositories_with_tools_exclude.xml' )
# This script can be run in such a way that no Tool Shed database records should be changed.
if '-info_only' in sys.argv or 'GALAXY_INSTALL_TEST_INFO_ONLY' in os.environ:
@@ -144,7 +147,7 @@
if error_message:
return None, error_message
# Handle repositories not to be tested.
- if os.path.exists( install_and_test_base_util.exclude_list_file ):
+ if os.path.exists( exclude_list_file ):
# Entries in the exclude_list look something like this.
# { 'reason': The default reason or the reason specified in this section,
# 'repositories':
@@ -157,8 +160,8 @@
# undeleted, this script will then test them the next time it runs. We don't need to check if a repository has been deleted
# here because our call to the Tool Shed API filters by downloadable='true', in which case deleted will always be False.
log.debug( 'Loading the list of repositories excluded from testing from the file %s...' % \
- str( install_and_test_base_util.exclude_list_file ) )
- exclude_list = install_and_test_base_util.parse_exclude_list( install_and_test_base_util.exclude_list_file )
+ str( exclude_list_file ) )
+ exclude_list = install_and_test_base_util.parse_exclude_list( exclude_list_file )
else:
exclude_list = []
# Generate a test method that will use Twill to install each repository into the embedded Galaxy application that was
@@ -299,7 +302,7 @@
test_toolbox.toolbox = app.toolbox
else:
# This repository and all of its dependencies were successfully installed.
- install_and_test_statistics_dict[ 'successful_installations' ].append( repository_identifier_dict )
+ install_and_test_statistics_dict[ 'successful_repository_installations' ].append( repository_identifier_dict )
# Configure and run functional tests for this repository. This is equivalent to sh run_functional_tests.sh -installed
remove_install_tests()
log.debug( 'Installation of %s succeeded, running all defined functional tests.' % str( repository.name ) )
@@ -560,33 +563,33 @@
total_repositories_processed = install_and_test_statistics_dict[ 'total_repositories_processed' ]
all_tests_passed = install_and_test_statistics_dict[ 'all_tests_passed' ]
at_least_one_test_failed = install_and_test_statistics_dict[ 'at_least_one_test_failed' ]
- successful_installations = install_and_test_statistics_dict[ 'successful_installations' ]
+ successful_repository_installations = install_and_test_statistics_dict[ 'successful_repository_installations' ]
repositories_with_installation_error = install_and_test_statistics_dict[ 'repositories_with_installation_error' ]
tool_dependencies_with_installation_error = install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ]
now = time.strftime( "%Y-%m-%d %H:%M:%S" )
print "####################################################################################"
print "# %s - installation script for repositories containing tools completed." % now
print "# Repository revisions processed: %s" % str( total_repositories_processed )
- if successful_installations:
+ if successful_repository_installations:
print "# ----------------------------------------------------------------------------------"
- print "# The following %d revisions with all dependencies were successfully installed:" % len( successful_installations )
- install_and_test_base_util.show_summary_output( successful_installations )
+ print "# The following %d revisions were successfully installed:" % len( successful_repository_installations )
+ install_and_test_base_util.display_repositories_by_owner( successful_repository_installations )
if all_tests_passed:
print '# ----------------------------------------------------------------------------------'
- print "# %d repositories successfully passed all functional tests:" % len( all_tests_passed )
- install_and_test_base_util.show_summary_output( all_tests_passed )
+ print "# The following %d revisions successfully passed all functional tests:" % len( all_tests_passed )
+ install_and_test_base_util.display_repositories_by_owner( all_tests_passed )
if at_least_one_test_failed:
print '# ----------------------------------------------------------------------------------'
- print "# %d repositories failed at least 1 functional test:" % len( at_least_one_test_failed )
- install_and_test_base_util.show_summary_output( at_least_one_test_failed )
+ print "# The following %d revisions failed at least 1 functional test:" % len( at_least_one_test_failed )
+ install_and_test_base_util.display_repositories_by_owner( at_least_one_test_failed )
if repositories_with_installation_error:
print '# ----------------------------------------------------------------------------------'
- print "# %d repositories have installation errors:" % len( repositories_with_installation_error )
- install_and_test_base_util.show_summary_output( repositories_with_installation_error )
+ print "# The following %d revisions have installation errors:" % len( repositories_with_installation_error )
+ install_and_test_base_util.display_repositories_by_owner( repositories_with_installation_error )
if tool_dependencies_with_installation_error:
print "# ----------------------------------------------------------------------------------"
print "# The following %d tool dependencies have installation errors:" % len( tool_dependencies_with_installation_error )
- install_and_test_base_util.show_summary_output( tool_dependencies_with_installation_error )
+ install_and_test_base_util.display_tool_dependencies_by_name( tool_dependencies_with_installation_error )
print "####################################################################################"
log.debug( "Shutting down..." )
# Gracefully shut down the embedded web server and UniverseApplication.
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -64,6 +64,9 @@
default_galaxy_locales = 'en'
default_galaxy_test_file_dir = "test-data"
os.environ[ 'GALAXY_INSTALL_TEST_TMP_DIR' ] = galaxy_test_tmp_dir
+# This file is copied to the Galaxy root directory by buildbot.
+# It is managed by cfengine and is not locally available.
+exclude_list_file = os.environ.get( 'GALAXY_INSTALL_TEST_EXCLUDE_REPOSITORIES', 'tool_dependency_definition_exclude.xml' )
# This script can be run in such a way that no Tool Shed database records should be changed.
if '-info_only' in sys.argv or 'GALAXY_INSTALL_TEST_INFO_ONLY' in os.environ:
@@ -83,7 +86,7 @@
if error_message:
return None, error_message
# Handle repositories not to be tested.
- if os.path.exists( install_and_test_base_util.exclude_list_file ):
+ if os.path.exists( exclude_list_file ):
# Entries in the exclude_list look something like this.
# { 'reason': The default reason or the reason specified in this section,
# 'repositories':
@@ -96,8 +99,8 @@
# undeleted, this script will then test them the next time it runs. We don't need to check if a repository has been deleted
# here because our call to the Tool Shed API filters by downloadable='true', in which case deleted will always be False.
log.debug( 'Loading the list of repositories excluded from testing from the file %s...' % \
- str( install_and_test_base_util.exclude_list_file ) )
- exclude_list = install_and_test_base_util.parse_exclude_list( install_and_test_base_util.exclude_list_file )
+ str( exclude_list_file ) )
+ exclude_list = install_and_test_base_util.parse_exclude_list( exclude_list_file )
else:
exclude_list = []
# Generate a test method that will use Twill to install each repository into the embedded Galaxy application that was
@@ -176,7 +179,8 @@
# Even if the repository failed to install, execute the uninstall method, in case a dependency did succeed.
log.debug( 'Attempting to uninstall revision %s of repository %s owned by %s.' % ( changeset_revision, name, owner ) )
try:
- repository = test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
+ repository = \
+ test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
except Exception, e:
error_message = 'Unable to find revision %s of repository %s owned by %s: %s.' % \
( changeset_revision, name, owner, str( e ) )
@@ -199,9 +203,34 @@
log.debug( 'Installation failed for revision %s of repository %s owned by %s.' % \
( changeset_revision, name, owner ) )
else:
+ # The repository was successfully installed.
log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
( changeset_revision, name, owner ) )
+ install_and_test_statistics_dict[ 'successful_repository_installations' ].append( repository_identifier_dict )
+ tool_test_results_dict[ 'successful_installations' ][ 'current_repository' ]\
+ .append( repository_identifier_dict )
+ params = dict( test_install_error=False,
+ do_not_test=False )
+ if repository.missing_repository_dependencies:
+ params[ 'test_install_error' ] = True
+ # Keep statistics for this repository's repository dependencies that resulted in installation errors.
+ for missing_repository_dependency in repository.missing_repository_dependencies:
+ tool_shed = str( missing_repository_dependency.tool_shed )
+ name = str( missing_repository_dependency.name )
+ owner = str( missing_repository_dependency.owner )
+ changset_revision = str( missing_repository_dependency.changeset_revision )
+ error_message = unicodify( missing_repository_dependency.error_message )
+ missing_repository_dependency_info_dict = dict( tool_shed=tool_shed,
+ name=name,
+ owner=owner,
+ changset_revision=changset_revision,
+ error_message=error_message )
+ install_and_test_statistics_dict[ 'repositories_with_installation_error' ]\
+ .append( missing_repository_dependency_dict )
+ tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ]\
+ .append( missing_repository_dependency_info_dict )
if repository.missing_tool_dependencies:
+ params[ 'test_install_error' ] = True
# Keep statistics for this repository's tool dependencies that resulted in installation errors.
for missing_tool_dependency in repository.missing_tool_dependencies:
name = str( missing_tool_dependency.name )
@@ -212,22 +241,47 @@
name=name,
version=version,
error_message=error_message )
- install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ].append( missing_tool_dependency_info_dict )
- else:
- # This repository and all of its dependencies were successfully installed.
- install_and_test_statistics_dict[ 'successful_installations' ].append( repository_identifier_dict )
- tool_test_results_dict[ 'passed_tests' ].append( repository_identifier_dict )
- params = dict( test_install_error=False,
- do_not_test=False )
- # TODO: do something useful with response_dict
- response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params,
- can_update_tool_shed )
-
- install_and_test_statistics_dict[ 'total_repositories_processed' ] = total_repositories_processed
+ install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ]\
+ .append( missing_tool_dependency_info_dict )
+ tool_test_results_dict[ 'installation_errors' ][ 'tool_dependencies' ]\
+ .append( missing_tool_dependency_info_dict )
+ if repository.installed_repository_dependencies:
+ # Keep statistics for this repository's tool dependencies that resulted in successful installations.
+ for repository_dependency in repository.installed_repository_dependencies:
+ tool_shed = str( repository_dependency.tool_shed )
+ name = str( repository_dependency.name )
+ owner = str( repository_dependency.owner )
+ changeset_revision = str( repository_dependency.changeset_revision )
+ repository_dependency_info_dict = dict( tool_shed=tool_shed,
+ name=name,
+ owner=owner,
+ changeset_revision=changeset_revision )
+ install_and_test_statistics_dict[ 'successful_repository_installations' ]\
+ .append( repository_dependency_info_dict )
+ tool_test_results_dict[ 'successful_installations' ][ 'repository_dependencies' ]\
+ .append( repository_dependency_info_dict )
+ if repository.installed_tool_dependencies:
+ # Keep statistics for this repository's tool dependencies that resulted in successful installations.
+ for tool_dependency in repository.installed_tool_dependencies:
+ name = str( tool_dependency.name )
+ type = str( tool_dependency.type )
+ version = str( tool_dependency.version )
+ installation_directory = tool_dependency.installation_directory( app )
+ tool_dependency_info_dict = dict( type=type,
+ name=name,
+ version=version,
+ installation_directory=installation_directory )
+ install_and_test_statistics_dict[ 'successful_tool_dependency_installations' ]\
+ .append( tool_dependency_info_dict )
+ tool_test_results_dict[ 'successful_installations' ][ 'tool_dependencies' ]\
+ .append( tool_dependency_info_dict )
+ # TODO: do something useful with response_dict
+ response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
return install_and_test_statistics_dict, error_message
def main():
@@ -446,25 +500,30 @@
log.debug( error_message )
else:
total_repositories_processed = install_and_test_statistics_dict[ 'total_repositories_processed' ]
- successful_installations = install_and_test_statistics_dict[ 'successful_installations' ]
+ successful_repository_installations = install_and_test_statistics_dict[ 'successful_repository_installations' ]
+ successful_tool_dependency_installations = install_and_test_statistics_dict[ 'successful_tool_dependency_installations' ]
repositories_with_installation_error = install_and_test_statistics_dict[ 'repositories_with_installation_error' ]
tool_dependencies_with_installation_error = install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ]
now = time.strftime( "%Y-%m-%d %H:%M:%S" )
print "####################################################################################"
print "# %s - installation script for repositories of type tool_dependency_definition completed." % now
print "# Repository revisions processed: %s" % str( total_repositories_processed )
- if successful_installations:
+ if successful_repository_installations:
print "# ----------------------------------------------------------------------------------"
- print "# The following %d revisions with all dependencies were successfully installed:" % len( successful_installations )
- install_and_test_base_util.show_summary_output( successful_installations )
+ print "# The following %d revisions were successfully installed:" % len( successful_repository_installations )
+ install_and_test_base_util.display_repositories_by_owner( successful_repository_installations )
if repositories_with_installation_error:
print "# ----------------------------------------------------------------------------------"
print "# The following %d revisions have installation errors:" % len( repositories_with_installation_error )
- install_and_test_base_util.show_summary_output( repositories_with_installation_error )
+ install_and_test_base_util.successful_repository_installations( repositories_with_installation_error )
+ if successful_tool_dependency_installations:
+ print "# ----------------------------------------------------------------------------------"
+ print "# The following %d tool dependencies were successfully installed:" % len( successful_tool_dependency_installations )
+ install_and_test_base_util.display_tool_dependencies_by_name( successful_tool_dependency_installations )
if tool_dependencies_with_installation_error:
print "# ----------------------------------------------------------------------------------"
print "# The following %d tool dependencies have installation errors:" % len( tool_dependencies_with_installation_error )
- install_and_test_base_util.show_summary_output( tool_dependencies_with_installation_error )
+ install_and_test_base_util.display_tool_dependencies_by_name( tool_dependencies_with_installation_error )
print "####################################################################################"
log.debug( "Shutting down..." )
# Gracefully shut down the embedded web server and UniverseApplication.
@@ -506,6 +565,36 @@
# "architecture": "x86_64",
# "system": "Darwin 12.2.0"
# },
+ # "successful_installation":
+ # {
+ # 'tool_dependencies':
+ # [
+ # {
+ # 'type': 'Type of tool dependency, e.g. package, set_environment, etc.',
+ # 'name': 'Name of the tool dependency.',
+ # 'version': 'Version if this is a package, otherwise blank.',
+ # 'installation_directory': 'The installation directory path.'
+ # },
+ # ],
+ # 'repository_dependencies':
+ # [
+ # {
+ # 'tool_shed': 'The tool shed that this repository was installed from.',
+ # 'name': 'The name of the repository that failed to install.',
+ # 'owner': 'Owner of the failed repository.',
+ # 'changeset_revision': 'Changeset revision of the failed repository.'
+ # },
+ # ],
+ # 'current_repository':
+ # [
+ # {
+ # 'tool_shed': 'The tool shed that this repository was installed from.',
+ # 'name': 'The name of the repository that failed to install.',
+ # 'owner': 'Owner of the failed repository.',
+ # 'changeset_revision': 'Changeset revision of the failed repository.'
+ # },
+ # ],
+ # }
# "installation_errors":
# {
# 'tool_dependencies':
@@ -537,12 +626,6 @@
# 'error_message': 'The error message that was returned when the repository failed to install.',
# },
# ],
- # {
- # "name": "The name of the repository.",
- # "owner": "The owner of the repository.",
- # "changeset_revision": "The changeset revision of the repository.",
- # "error_message": "The message stored in tool_dependency.error_message."
- # },
# }
# }
sys.exit( main() )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
38 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/57d618633209/
Changeset: 57d618633209
Branch: data_library
User: martenson
Date: 2013-10-08 22:41:08
Summary: start data_library branch
Affected #: 3 files
diff -r b61927f340f30a636204ec560522acf9b76fd262 -r 57d618633209a35342f25a13a220fedce71f7a3f .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -115,3 +115,6 @@
*.rej
*~
+
+syntax: regexp
+^static/AAA_scratch$
\ No newline at end of file
diff -r b61927f340f30a636204ec560522acf9b76fd262 -r 57d618633209a35342f25a13a220fedce71f7a3f 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
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a library.
+API operations on the contents of a folder.
"""
import logging, os, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
@@ -11,12 +11,17 @@
log = logging.getLogger( __name__ )
class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ):
+ """
+ Class controls retrieval, creation and updating of folder contents.
+ """
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
+
Displays a collection (list) of a folder's contents (files and folders).
+
The /api/library_contents/{encoded_library_id}/contents
lists everything in a library recursively, which is not what
we want here. We could add a parameter to use the recursive
@@ -25,7 +30,11 @@
rval = []
current_user_roles = trans.get_current_user_roles()
+
def traverse( folder ):
+ """
+ Load contents of the folder (folders and datasets).
+ """
admin = trans.user_is_admin()
rval = []
for subfolder in folder.active_folders:
@@ -50,6 +59,8 @@
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
+# log.debug("XXXXXXXXXXXXXXXXXXXXXXXXXXX folder.parent_library" + str(folder.parent_library.id))
+# log.debug("XXXXXXXXXXXXXXXXXXXXXXXXXXX folder.parent_id" + str(folder.parent_id))
parent_library = folder.parent_library
except:
folder = None
@@ -62,15 +73,43 @@
if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
+ # TODO MARTEN Can it be that predecessors of current folder have different access rights? aka user shouldn't see them?
+
+ # Search the path upwards and load the whole route of names and ids for breadcrumb purposes.
+ path_to_library = []
+
+ def build_path ( folder ):
+# log.debug("XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id BEFORE " + str(folder.parent_id))
+# log.debug("XXXXXXXXXXXXXXXXXXXXXXX folder.parent_library.id BEFORE " + str(folder.parent_library.id))
+ if ( folder.parent_id != folder.parent_library.id ) and ( folder.parent_id is not None ):
+ log.debug("XXXXXXXXXXXXXXXXXXXXXXX LOADING UPPER FOLDER WITH ID: " + str(folder.parent_id))
+ upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
+ log.debug("XXXXXXXXXXXXXXXXXXXXXXX upper_folder.id " + str(upper_folder.id))
+ log.debug("XXXXXXXXXXXXXXXXXXXXXXX upper_folder.name " + str(upper_folder.name))
+ path_to_library.append( ( upper_folder.id, upper_folder.name ) )
+ path_to_library.extend( build_path( upper_folder ) )
+ else:
+ pass
+ return path_to_library
+
+ full_path = build_path( folder )
for content in traverse( folder ):
+ return_item = {}
encoded_id = trans.security.encode_id( content.id )
if content.api_type == 'folder':
+ encoded_parent_library_id = trans.security.encode_id( content.parent_library.id )
encoded_id = 'F' + encoded_id
- rval.append( dict( id = encoded_id,
+ if content.parent_id is not None: # For folder return its parent's id for browsing back.
+ encoded_parent_id = 'F' + trans.security.encode_id( content.parent_id )
+ return_item.update ( dict ( parent_id = encoded_parent_id ) )
+ return_item.update( dict( id = encoded_id,
type = content.api_type,
name = content.name,
+ library_id = encoded_parent_library_id,
+ full_path = full_path,
url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
+ rval.append( return_item )
return rval
@web.expose_api
diff -r b61927f340f30a636204ec560522acf9b76fd262 -r 57d618633209a35342f25a13a220fedce71f7a3f lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -49,7 +49,7 @@
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
rval = []
for library in query:
- item = library.to_dict()
+ item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
item['id'] = trans.security.encode_id( item['id'] )
rval.append( item )
@@ -131,6 +131,9 @@
rval['name'] = name
rval['id'] = encoded_id
return rval
+
+ def edit( self, trans, payload, **kwd ):
+ return "Not implemented yet"
@web.expose_api
def delete( self, trans, id, **kwd ):
https://bitbucket.org/galaxy/galaxy-central/commits/db8fd059cf4c/
Changeset: db8fd059cf4c
Branch: data_library
User: martenson
Date: 2013-10-17 18:56:34
Summary: working reverse traversing towards root library for every folder = good for path building
Affected #: 1 file
diff -r 57d618633209a35342f25a13a220fedce71f7a3f -r db8fd059cf4cae9a6b7dc8bdbe7be5d071325d09 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
@@ -27,11 +27,11 @@
we want here. We could add a parameter to use the recursive
style, but this is meant to act similar to an "ls" directory listing.
"""
- rval = []
+ folder_contents = []
current_user_roles = trans.get_current_user_roles()
- def traverse( folder ):
+ def load_folder_contents( folder ):
"""
Load contents of the folder (folders and datasets).
"""
@@ -76,41 +76,55 @@
# TODO MARTEN Can it be that predecessors of current folder have different access rights? aka user shouldn't see them?
# Search the path upwards and load the whole route of names and ids for breadcrumb purposes.
- path_to_library = []
+ path_to_root = []
def build_path ( folder ):
-# log.debug("XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id BEFORE " + str(folder.parent_id))
-# log.debug("XXXXXXXXXXXXXXXXXXXXXXX folder.parent_library.id BEFORE " + str(folder.parent_library.id))
- if ( folder.parent_id != folder.parent_library.id ) and ( folder.parent_id is not None ):
- log.debug("XXXXXXXXXXXXXXXXXXXXXXX LOADING UPPER FOLDER WITH ID: " + str(folder.parent_id))
+ path_to_root = []
+ # We are almost in root
+ log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id: " + str( folder.parent_id ) )
+ log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_library.id: " + str( folder.parent_library.id ) )
+ if folder.parent_id is None:
+ log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING: " + str( folder.name ) )
+ path_to_root.append( ( folder.id, folder.name ) )
+# upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_library.id )
+# path_to_root.append( ( upper_folder.id, upper_folder.name ) )
+ else:
+ # We add the current folder and traverse up one folder.
+ log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ADDING THIS FOLDER AND TRAVERSING UP: " + str( folder.name ) )
+ path_to_root.append( ( folder.id, folder.name ) )
upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
- log.debug("XXXXXXXXXXXXXXXXXXXXXXX upper_folder.id " + str(upper_folder.id))
- log.debug("XXXXXXXXXXXXXXXXXXXXXXX upper_folder.name " + str(upper_folder.name))
- path_to_library.append( ( upper_folder.id, upper_folder.name ) )
- path_to_library.extend( build_path( upper_folder ) )
- else:
- pass
- return path_to_library
+ path_to_root.extend( build_path( upper_folder ) )
+# path_to_root = path_to_root.reverse()
+# return path_to_root[::-1]
+ return path_to_root
- full_path = build_path( folder )
+ # Return the reversed path so it starts with the library node.
+ full_path = build_path( folder )[::-1]
- for content in traverse( folder ):
+ # Go through every item in the folder and include its meta-data.
+ for content_item in load_folder_contents( folder ):
return_item = {}
- encoded_id = trans.security.encode_id( content.id )
- if content.api_type == 'folder':
- encoded_parent_library_id = trans.security.encode_id( content.parent_library.id )
+ encoded_id = trans.security.encode_id( content_item.id )
+
+ # For folder return also hierarchy values
+ if content_item.api_type == 'folder':
+ encoded_parent_library_id = trans.security.encode_id( content_item.parent_library.id )
encoded_id = 'F' + encoded_id
- if content.parent_id is not None: # For folder return its parent's id for browsing back.
- encoded_parent_id = 'F' + trans.security.encode_id( content.parent_id )
+ if content_item.parent_id is not None: # Return folder's parent id for browsing back.
+ encoded_parent_id = 'F' + trans.security.encode_id( content_item.parent_id )
return_item.update ( dict ( parent_id = encoded_parent_id ) )
+
+ # For every item return also the default meta-data
return_item.update( dict( id = encoded_id,
- type = content.api_type,
- name = content.name,
+ type = content_item.api_type,
+ name = content_item.name,
library_id = encoded_parent_library_id,
full_path = full_path,
url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
- rval.append( return_item )
- return rval
+ folder_contents.append( return_item )
+ if len( folder_contents ) == 0:
+ folder_contents.append( dict( full_path = full_path ) )
+ return folder_contents
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
https://bitbucket.org/galaxy/galaxy-central/commits/1eda20e058eb/
Changeset: 1eda20e058eb
Branch: data_library
User: martenson
Date: 2013-10-21 18:31:38
Summary: initial frontend commit
Affected #: 3 files
diff -r db8fd059cf4cae9a6b7dc8bdbe7be5d071325d09 -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb 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
@@ -27,7 +27,7 @@
we want here. We could add a parameter to use the recursive
style, but this is meant to act similar to an "ls" directory listing.
"""
- folder_contents = []
+ folder_container = []
current_user_roles = trans.get_current_user_roles()
@@ -100,7 +100,8 @@
# Return the reversed path so it starts with the library node.
full_path = build_path( folder )[::-1]
-
+ folder_container.append( dict( full_path = full_path ) )
+ folder_contents = []
# Go through every item in the folder and include its meta-data.
for content_item in load_folder_contents( folder ):
return_item = {}
@@ -118,13 +119,11 @@
return_item.update( dict( id = encoded_id,
type = content_item.api_type,
name = content_item.name,
- library_id = encoded_parent_library_id,
- full_path = full_path,
- url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
+ library_id = encoded_parent_library_id
+ ) )
folder_contents.append( return_item )
- if len( folder_contents ) == 0:
- folder_contents.append( dict( full_path = full_path ) )
- return folder_contents
+ folder_container.append( dict( folder_contents = folder_contents ) )
+ return folder_container
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
diff -r db8fd059cf4cae9a6b7dc8bdbe7be5d071325d09 -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb static/AAA_scratch/data_library/index.html
--- /dev/null
+++ b/static/AAA_scratch/data_library/index.html
@@ -0,0 +1,265 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Data Library</title>
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap…">
+</head>
+<body>
+
+
+ <div class="container">
+ <h1><a onclick="router.navigate('', {trigger: true})">Data Library</a></h1>
+ <hr />
+ <div class="page"></div>
+ </div>
+
+<!-- LIBRARY LIST TEMPLATE -->
+ <script type="text/template" id="library-list-template">
+ <table class="table striped">
+ <thead>
+ <th>name</th>
+ <th>description</th>
+ <th>synopsis</th>
+ <th>model type</th>
+ <th>id</th>
+
+ </thead>
+ <tbody>
+ <% _.each(libraries, function(library) { %>
+ <tr>
+ <td><a href="#/folders/<%- library.id %>"><%- library.get('name') %></a></td>
+ <td><%= htmlEncode(library.get('description')) %></td>
+ <td><%= htmlEncode(library.get('synopsis')) %></td>
+ <td><%= htmlEncode(library.get('model_class')) %></td>
+ <td><a href="#/folders/<%- library.id %>"><%= htmlEncode(library.get('id')) %></a></td>
+ </tr>
+ <% }); %>
+ </tbody>
+ </table>
+ </script>
+
+
+<!-- FOLDERS LIST TEMPLATE -->
+ <script type="text/template" id="folder-content-template">
+ <table class="table striped">
+ <thead>
+ <th>name</th>
+ <th>type</th>
+ <th>parent id</th>
+ <th>id</th>
+ <th>library id</th>
+ </thead>
+ <tbody>
+ <% _.each(items, function(content_item) { %>
+ <tr>
+ <% if (content_item.get('type') === 'folder') { %>
+ <td><a href="#/folders/<%- content_item.id %>?library_id=<%- content_item.get('library_id') %>"><%- content_item.get('name') %></a></td>
+ <% } else { %>
+ <td><%- content_item.get('name') %></td>
+ <% } %>
+ <td><%= htmlEncode(content_item.get('type')) %></td>
+ <td><%= htmlEncode(content_item.get('parent_id')) %></td>
+ <td><a href="#/folders/<%- content_item.id %>"><%= htmlEncode(content_item.get('id')) %></a></td>
+ <td><a href="#/folders/<%- content_item.get('library_id') %>"><%= htmlEncode(content_item.get('library_id')) %></a></td>
+ </tr>
+ <% }); %>
+ </tbody>
+ </table>
+
+ <hr/>
+ <div>
+ <% if ( typeof libraries !== undefined ) { %>
+ <% _.each(libraries.models, function(library) { %>
+ <%- library.get('name') %><br/>
+ <% }); %>
+ <% } else { %>
+ libraries are undefined
+ <% } %>
+ </div>
+ </script>
+
+
+ <script src="jquery-1.10.2.js" type="text/javascript"></script>
+ <script src="underscore.js" type="text/javascript"></script>
+ <script src="backbone.js" type="text/javascript"></script>
+ <script src="backbone.queryparams.js" type="text/javascript"></script>
+
+
+ <script>
+
+ // ENCODE HTML
+ function htmlEncode(value){
+ return $('<div/>').text(value).html();
+ }
+
+ // NESTED COLLECTIONS
+ function nestCollection(model, attributeName, nestedCollection) {
+ //setup nested references
+ for (var i = 0; i < nestedCollection.length; i++) {
+ model.attributes[attributeName][i] = nestedCollection.at(i).attributes;
+ }
+
+ //create empty arrays if none
+ nestedCollection.bind('add', function (initiative) {
+ if (!model.get(attributeName)) {
+ model.attributes[attributeName] = [];
+ }
+ model.get(attributeName).push(initiative.attributes);
+ });
+
+ nestedCollection.bind('remove', function (initiative) {
+ var updateObj = {};
+ updateObj[attributeName] = _.without(model.get(attributeName), initiative.attributes);
+ model.set(updateObj);
+ });
+ return nestedCollection;
+ }
+
+ var api_key = 'e48b2b2589b242a50538f8f9fa8e5ee9';
+
+ // MODIFICATIONS DONE JUST BEFORE THE REQUEST ITSELF
+ $.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
+ options.url = 'http://martenson.bx.psu.edu:8080' + options.url + '?key=' + api_key;
+ });
+
+ // SERIALIZE JSON
+ $.fn.serializeObject = function() {
+ var o = {};
+ var a = this.serializeArray();
+ $.each(a, function() {
+ if (o[this.name] !== undefined) {
+ if (!o[this.name].push) {
+ o[this.name] = [o[this.name]];
+ }
+ o[this.name].push(this.value || '');
+ } else {
+ o[this.name] = this.value || '';
+ }
+ });
+ return o;
+ };
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries',
+ });
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // Container including metadata
+ var FolderContainer = Backbone.Model.extend({
+ defaults: {
+ folder: new Folder(),
+ full_path: "unknown",
+ urlRoot: "/api/folders/",
+ id: "unknown"
+ },
+ parse: function(obj) {
+ full_path = obj[0].full_path;
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+
+ // this mightn't be necessary
+ // delete obj.folder;
+
+ return obj;
+ }
+ })
+
+ var LibraryListView = Backbone.View.extend({
+ el: '.page',
+ render: function () {
+ var that = this;
+ if (typeof libraries === "undefined") {
+ libraries = new Libraries();
+ }
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template($('#library-list-template').html(), {libraries: libraries.models});
+ that.$el.html(template);
+ }
+ })
+ }
+ });
+
+
+ var FolderContentView = Backbone.View.extend({
+ el: '.page',
+ render: function (options, params) {
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+ // var folder = new Folder({id: options.id});
+
+ folderContainer.fetch({
+ success: function (container) {
+ folderContainer.attributes.folder = container.attributes.folder;
+ var template = _.template($('#folder-content-template').html(), {items: folderContainer.attributes.folder.models, id: options.id});
+ that.$el.html(template);
+ }
+ })
+
+ // folder.fetch({
+ // success: function (folder) {
+ // folderContainer.folder = folder;
+ // var template = _.template($('#folder-content-template').html(), {items: folderContainer.folder.models, id: options.id});
+ // that.$el.html(template);
+ // }
+ // })
+
+ // var folder = new Folder({id: options.id});
+ // folder.url = folder.urlRoot + options.id + '/contents';
+ // folder.fetch({
+ // success: function (folder) {
+ // // console.log(JSON.stringify(folder))
+ // //create the template
+ // var template = _.template($('#folder-content-template').html(), {items: folder.models, id: options.id});
+ // that.$el.html(template);
+ // }
+ // })
+
+ },
+ });
+
+
+ var libraryListView = new LibraryListView();
+ var folderContentView = new FolderContentView();
+
+ var Router = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content"
+ }
+ });
+
+ var router = new Router;
+
+ router.on('route:libraries', function() {
+ // render libraries list
+ libraryListView.render();
+ })
+
+ router.on('route:folder_content', function(id, params) {
+ // render folder's content
+ folderContentView.render({id: id, params: params});
+ })
+
+ Backbone.history.start();
+ </script>
+
+
+</body>
+</html>
diff -r db8fd059cf4cae9a6b7dc8bdbe7be5d071325d09 -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb static/AAA_scratch/data_library/index_working.html
--- /dev/null
+++ b/static/AAA_scratch/data_library/index_working.html
@@ -0,0 +1,261 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Data Library</title>
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap…">
+</head>
+<body>
+
+
+ <div class="container">
+ <h1>Data Library</h1>
+ <hr />
+ <div class="page"></div>
+ </div>
+
+<!-- LIBRARY LIST TEMPLATE -->
+ <script type="text/template" id="library-list-template">
+ <a href="#/new" class="btn btn-primary">New Library</a>
+ <hr />
+ <table class="table striped">
+ <thead>
+ <th>name</th>
+ <th>description</th>
+ <th>synopsis</th>
+ <th></th>
+ <th></th>
+ </thead>
+ <tbody>
+ <% _.each(libraries, function(library) { %>
+ <tr>
+ <td><a href="#/folders/<%- library.id %>"><%- library.get('name') %></a></td>
+ <td><%= htmlEncode(library.get('description')) %></td>
+ <td><%= htmlEncode(library.get('synopsis')) %></td>
+ <td><a href="#/edit/<%= library.id %>" class="btn">Edit</td>
+ <td><button data-library-id="<%- library.id %>" class="btn btn-danger delete">Delete</button></td>
+ </tr>
+ <% }); %>
+ </tbody>
+ </table>
+ </script>
+
+<!-- LIBRARY EDIT/CREATE TEMPLATE -->
+ <script type="text/template" id="library-edit-template">
+ <form class="library-edit-form">
+ <legend>Create Library</legend>
+ <label>Name of the Library</label>
+ <input type="text" name="name" />
+ <label>Description</label>
+ <input type="text" name="description" />
+ <label>Synopsis</label>
+ <input type="text" name="synopsis" />
+ <hr />
+ <button type="submit" class="btn">Create Library</button>
+ </form>
+ </script>
+
+
+<!-- FOLDERS LIST TEMPLATE -->
+ <script type="text/template" id="folders-list-template">
+ <table class="table striped">
+ <thead>
+ <th>name</th>
+ <th>type</th>
+ </thead>
+ <tbody>
+ <% _.each(folders, function(folder) { %>
+ <tr>
+ <% if (folder.get('type') === 'folder') { %>
+ <td><a href="#/folders/<%- folder.id %>"><%- folder.get('name') %></a></td>
+ <% } else { %>
+ <td><%- folder.get('name') %></td>
+ <% } %>
+ <td><%= htmlEncode(folder.get('type')) %></td>
+ </tr>
+ <% }); %>
+ </tbody>
+ </table>
+ </script>
+
+
+ <script src="jquery-1.10.2.js" type="text/javascript"></script>
+ <script src="underscore.js" type="text/javascript"></script>
+ <script src="backbone.js" type="text/javascript"></script>
+
+
+ <script>
+
+ // ENCODE HTML
+ function htmlEncode(value){
+ return $('<div/>').text(value).html();
+ }
+
+ var api_key = 'e48b2b2589b242a50538f8f9fa8e5ee9';
+
+ // MODIFICATIONS DONE JUST BEFORE THE REQUEST ITSELF
+ $.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
+ options.url = 'http://martenson.bx.psu.edu:8080' + options.url + '?key=' + api_key;
+ });
+
+ // SERIALIZE JSON
+ $.fn.serializeObject = function() {
+ var o = {};
+ var a = this.serializeArray();
+ $.each(a, function() {
+ if (o[this.name] !== undefined) {
+ if (!o[this.name].push) {
+ o[this.name] = [o[this.name]];
+ }
+ o[this.name].push(this.value || '');
+ } else {
+ o[this.name] = this.value || '';
+ }
+ });
+ return o;
+ };
+
+// LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries'
+ });
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries'
+ });
+// FOLDER
+ var Folder = Backbone.Model.extend({
+ urlRoot: '/api/folders/'
+ })
+ var Folders = Backbone.Collection.extend({
+ model: Folder,
+ urlRoot: '/api/folders/'
+
+ })
+
+ var LibraryEditView = Backbone.View.extend({
+ el: '.page',
+ events: {
+ 'submit .library-edit-form': 'saveLibrary'
+ },
+ saveLibrary: function (ev) {
+ var libraryDetails = $(ev.currentTarget).serializeObject();
+ var library = new Library();
+ library.save(libraryDetails, {
+ success: function (library) {
+ router.navigate('', {trigger: true})
+ }
+ });
+ console.log(libraryDetails);
+ return false;
+ },
+ render: function (options) {
+ var that = this;
+ if (options.id) {
+ this.library = new Library({id: options.id});
+ this.library.fetch({
+ success: function(library){
+ var template = _.template($('#library-edit-template').html(), {library: library});
+ that.$el.html(template);
+ }
+ });
+ } else {
+ var template = _.template($('#library-edit-template').html(), {user: null});
+ this.$el.html(template);
+ }
+ }
+ });
+
+
+ var LibraryListView = Backbone.View.extend({
+ el: '.page',
+ events: {
+ 'click .delete' : 'deleteLibrary'
+ },
+ render: function () {
+ var that = this;
+ var libraries = new Libraries();
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template($('#library-list-template').html(), {libraries: libraries.models});
+ that.$el.html(template);
+ }
+ })
+ },
+ deleteLibrary : function (ev) {
+ // DELETE /libraries/{id}
+ var id_to_delete = $(ev.target).attr('data-library-id');
+ var libraryToDelete = new Library({id: id_to_delete})
+ libraryToDelete.destroy({
+ success: function () {
+ libraryListView.render();
+ }
+ })
+ return false
+ },
+ createLibrary : function (ev) {
+ // PUT /libraries
+
+ }
+ });
+
+
+ var FoldersListView = Backbone.View.extend({
+ el: '.page',
+ render: function (options) {
+ var that = this;
+ var folders = new Folders({id: options.id});
+ folders.url = folders.urlRoot + options.id + '/contents';
+ folders.fetch({
+ success: function (folders) {
+ var template = _.template($('#folders-list-template').html(), {folders: folders.models, id: options.id});
+ that.$el.html(template);
+ }
+ })
+ },
+ });
+
+
+ var libraryListView = new LibraryListView();
+ var libraryEditView = new LibraryEditView();
+ var foldersListView = new FoldersListView();
+
+
+ var Router = Backbone.Router.extend({
+ routes: {
+ "" : "home",
+ "new" : "edit_library",
+ "edit/:id" : "edit_library",
+ "delete/:id" : "delete_library",
+ "folders/:id" : "folders"
+ }
+ });
+
+ var router = new Router;
+
+ router.on('route:home', function() {
+ // render libraries list
+ libraryListView.render();
+ })
+ router.on('route:edit_library', function(id) {
+ // render library edit form
+ libraryEditView.render({id: id});
+ })
+ router.on('route:delete_library', function() {
+ // render delete library form
+ libraryDeleteView.render();
+ })
+ // router.on('route:folders', function(parent_id, top_level) {
+ // // render folders list
+ // foldersListView.render({id: parent_id, top_level: true});
+ // })
+ router.on('route:folders', function(id) {
+ // render folders list
+ foldersListView.render({id: id});
+ })
+
+ Backbone.history.start();
+ </script>
+
+
+</body>
+</html>
https://bitbucket.org/galaxy/galaxy-central/commits/dc735a38cf76/
Changeset: dc735a38cf76
Branch: data_library
User: martenson
Date: 2013-10-24 20:22:26
Summary: refactoring, modals start
Affected #: 4 files
diff -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1835,7 +1835,7 @@
return name
class LibraryFolder( object, Dictifiable ):
- dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
+ dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time' )
def __init__( self, name=None, description=None, item_count=0, order_id=None ):
self.name = name or "Unnamed folder"
self.description = description
diff -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 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
@@ -15,42 +15,37 @@
Class controls retrieval, creation and updating of folder contents.
"""
+ def load_folder_contents( self, trans, folder ):
+ """
+ Loads all contents of the folder (folders and data sets) but only in the first level.
+ """
+ current_user_roles = trans.get_current_user_roles()
+ is_admin = trans.user_is_admin()
+ content_items = []
+ for subfolder in folder.active_folders:
+ if not is_admin:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
+ if (is_admin or can_access) and not subfolder.deleted:
+ subfolder.api_type = 'folder'
+ content_items.append( subfolder )
+ for dataset in folder.datasets:
+ if not is_admin:
+ can_access = trans.app.security_agent.can_access_dataset( current_user_roles, dataset.library_dataset_dataset_association.dataset )
+ if (is_admin or can_access) and not dataset.deleted:
+ dataset.api_type = 'file'
+ content_items.append( dataset )
+ return content_items
+
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
-
Displays a collection (list) of a folder's contents (files and folders).
-
- The /api/library_contents/{encoded_library_id}/contents
- lists everything in a library recursively, which is not what
- we want here. We could add a parameter to use the recursive
- style, but this is meant to act similar to an "ls" directory listing.
+ Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
"""
folder_container = []
current_user_roles = trans.get_current_user_roles()
-
- def load_folder_contents( folder ):
- """
- Load contents of the folder (folders and datasets).
- """
- admin = trans.user_is_admin()
- rval = []
- for subfolder in folder.active_folders:
- if not admin:
- can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
- if (admin or can_access) and not subfolder.deleted:
- subfolder.api_type = 'folder'
- rval.append( subfolder )
- for ld in folder.datasets:
- if not admin:
- can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ld.library_dataset_dataset_association.dataset )
- if (admin or can_access) and not ld.deleted:
- ld.api_type = 'file'
- rval.append( ld )
- return rval
-
try:
decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
except TypeError:
@@ -59,69 +54,68 @@
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
-# log.debug("XXXXXXXXXXXXXXXXXXXXXXXXXXX folder.parent_library" + str(folder.parent_library.id))
-# log.debug("XXXXXXXXXXXXXXXXXXXXXXXXXXX folder.parent_id" + str(folder.parent_id))
parent_library = folder.parent_library
except:
folder = None
- log.error( "FolderContentsController.index: Unable to retrieve folder %s"
- % folder_id )
+ log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
- # TODO: Find the API's path to this folder if necessary.
- # This was needed in recursive descent, but it's not needed
- # for "ls"-style content checking:
- if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ # We didn't find the folder or user does not have an access to it.
+ if not folder:
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
- # TODO MARTEN Can it be that predecessors of current folder have different access rights? aka user shouldn't see them?
- # Search the path upwards and load the whole route of names and ids for breadcrumb purposes.
+ if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, folder.id ) )
+ trans.response.status = 400
+ return "Invalid folder id ( %s ) specified." % str( folder_id )
+
path_to_root = []
-
def build_path ( folder ):
+ """
+ Search the path upwards recursively and load the whole route of names and ids for breadcrumb purposes.
+ """
path_to_root = []
# We are almost in root
- log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id: " + str( folder.parent_id ) )
- log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_library.id: " + str( folder.parent_library.id ) )
if folder.parent_id is None:
log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING: " + str( folder.name ) )
- path_to_root.append( ( folder.id, folder.name ) )
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
# upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_library.id )
# path_to_root.append( ( upper_folder.id, upper_folder.name ) )
else:
# We add the current folder and traverse up one folder.
log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ADDING THIS FOLDER AND TRAVERSING UP: " + str( folder.name ) )
- path_to_root.append( ( folder.id, folder.name ) )
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
path_to_root.extend( build_path( upper_folder ) )
-# path_to_root = path_to_root.reverse()
-# return path_to_root[::-1]
return path_to_root
# Return the reversed path so it starts with the library node.
full_path = build_path( folder )[::-1]
folder_container.append( dict( full_path = full_path ) )
+
folder_contents = []
# Go through every item in the folder and include its meta-data.
- for content_item in load_folder_contents( folder ):
+ for content_item in self.load_folder_contents( trans, folder ):
return_item = {}
encoded_id = trans.security.encode_id( content_item.id )
# For folder return also hierarchy values
if content_item.api_type == 'folder':
- encoded_parent_library_id = trans.security.encode_id( content_item.parent_library.id )
+# encoded_parent_library_id = trans.security.encode_id( content_item.parent_library.id )
encoded_id = 'F' + encoded_id
- if content_item.parent_id is not None: # Return folder's parent id for browsing back.
- encoded_parent_id = 'F' + trans.security.encode_id( content_item.parent_id )
- return_item.update ( dict ( parent_id = encoded_parent_id ) )
+# if content_item.parent_id is not None: # Return folder's parent id for browsing back.
+# encoded_parent_id = 'F' + trans.security.encode_id( content_item.parent_id )
+ last_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count, last_updated = last_updated ) )
# For every item return also the default meta-data
return_item.update( dict( id = encoded_id,
type = content_item.api_type,
- name = content_item.name,
- library_id = encoded_parent_library_id
+ name = content_item.name
+
) )
folder_contents.append( return_item )
+ # Put the data in the container
folder_container.append( dict( folder_contents = folder_contents ) )
return folder_container
diff -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -51,7 +51,7 @@
for library in query:
item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
- item['id'] = trans.security.encode_id( item['id'] )
+ item['id'] = 'F' + trans.security.encode_id( item['id'] )
rval.append( item )
return rval
diff -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 static/AAA_scratch/data_library/index.html
--- a/static/AAA_scratch/data_library/index.html
+++ b/static/AAA_scratch/data_library/index.html
@@ -1,10 +1,13 @@
-<!doctype html>
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Data Library</title>
- <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap…">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <!-- Bootstrap -->
+ <link href="css/bootstrap.min.css" rel="stylesheet" media="screen"></head>
+
<body>
@@ -16,7 +19,7 @@
<!-- LIBRARY LIST TEMPLATE --><script type="text/template" id="library-list-template">
- <table class="table striped">
+ <table class="table table-striped"><thead><th>name</th><th>description</th>
@@ -42,45 +45,47 @@
<!-- FOLDERS LIST TEMPLATE --><script type="text/template" id="folder-content-template">
- <table class="table striped">
+
+ <% _.each(path, function(path_item) { %>
+ <% if (path_item[0] != id) { %>
+ <a href='#/folders/<%- path_item[0] %>'><%- path_item[1] %></a> |
+ <% } else { %>
+ <%- path_item[1] %>
+ <% } %>
+ <% }); %>
+
+ <table class="table table-hover table-condensed"><thead>
+ <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th><th>name</th><th>type</th>
- <th>parent id</th>
- <th>id</th>
- <th>library id</th></thead><tbody><% _.each(items, function(content_item) { %>
- <tr>
+ <tr class="folder_row" id="<%- content_item.id %>">
+ <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td><% if (content_item.get('type') === 'folder') { %>
- <td><a href="#/folders/<%- content_item.id %>?library_id=<%- content_item.get('library_id') %>"><%- content_item.get('name') %></a></td>
+ <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get('name') %></a>
+ <% if (content_item.get('item_count') === 0) { %>
+ <span class="muted">(empty folder)</span>
+ <% } %>
+ </td><% } else { %><td><%- content_item.get('name') %></td><% } %><td><%= htmlEncode(content_item.get('type')) %></td>
- <td><%= htmlEncode(content_item.get('parent_id')) %></td>
- <td><a href="#/folders/<%- content_item.id %>"><%= htmlEncode(content_item.get('id')) %></a></td>
- <td><a href="#/folders/<%- content_item.get('library_id') %>"><%= htmlEncode(content_item.get('library_id')) %></a></td></tr><% }); %></tbody></table>
- <hr/>
- <div>
- <% if ( typeof libraries !== undefined ) { %>
- <% _.each(libraries.models, function(library) { %>
- <%- library.get('name') %><br/>
- <% }); %>
- <% } else { %>
- libraries are undefined
- <% } %>
- </div>
+
</script>
+ <script src="require.js"></script><script src="jquery-1.10.2.js" type="text/javascript"></script>
+ <script src="js/bootstrap.min.js"></script><script src="underscore.js" type="text/javascript"></script><script src="backbone.js" type="text/javascript"></script><script src="backbone.queryparams.js" type="text/javascript"></script>
@@ -88,34 +93,27 @@
<script>
+ // configure require
+ // require.config({
+ // baseUrl: "/",
+ // shim: {
+ // "libs/underscore": { exports: "_" },
+ // "libs/backbone/backbone": { exports: "Backbone" },
+ // "libs/backbone/backbone-relational": ["libs/backbone/backbone"]
+ // }
+ // });
+
+ // require(['galaxy.modal'], function(modal)
+ // {
+ // Galaxy.modal = new modal.GalaxyModal();
+ // });
+
+
// ENCODE HTML
function htmlEncode(value){
return $('<div/>').text(value).html();
}
- // NESTED COLLECTIONS
- function nestCollection(model, attributeName, nestedCollection) {
- //setup nested references
- for (var i = 0; i < nestedCollection.length; i++) {
- model.attributes[attributeName][i] = nestedCollection.at(i).attributes;
- }
-
- //create empty arrays if none
- nestedCollection.bind('add', function (initiative) {
- if (!model.get(attributeName)) {
- model.attributes[attributeName] = [];
- }
- model.get(attributeName).push(initiative.attributes);
- });
-
- nestedCollection.bind('remove', function (initiative) {
- var updateObj = {};
- updateObj[attributeName] = _.without(model.get(attributeName), initiative.attributes);
- model.set(updateObj);
- });
- return nestedCollection;
- }
-
var api_key = 'e48b2b2589b242a50538f8f9fa8e5ee9';
// MODIFICATIONS DONE JUST BEFORE THE REQUEST ITSELF
@@ -167,13 +165,10 @@
id: "unknown"
},
parse: function(obj) {
- full_path = obj[0].full_path;
+ this.full_path = obj[0].full_path;
+ // this.folder.reset(obj[1].folder_contents);
// update the inner collection
this.get("folder").reset(obj[1].folder_contents);
-
- // this mightn't be necessary
- // delete obj.folder;
-
return obj;
}
})
@@ -197,41 +192,39 @@
var FolderContentView = Backbone.View.extend({
el: '.page',
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked'
+ },
render: function (options, params) {
var that = this;
var folderContainer = new FolderContainer({id: options.id});
folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
- // var folder = new Folder({id: options.id});
folderContainer.fetch({
success: function (container) {
- folderContainer.attributes.folder = container.attributes.folder;
- var template = _.template($('#folder-content-template').html(), {items: folderContainer.attributes.folder.models, id: options.id});
+ // folderContainer.attributes.folder = container.attributes.folder;
+ var template = _.template($('#folder-content-template').html(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
that.$el.html(template);
}
})
-
- // folder.fetch({
- // success: function (folder) {
- // folderContainer.folder = folder;
- // var template = _.template($('#folder-content-template').html(), {items: folderContainer.folder.models, id: options.id});
- // that.$el.html(template);
- // }
- // })
-
- // var folder = new Folder({id: options.id});
- // folder.url = folder.urlRoot + options.id + '/contents';
- // folder.fetch({
- // success: function (folder) {
- // // console.log(JSON.stringify(folder))
- // //create the template
- // var template = _.template($('#folder-content-template').html(), {items: folder.models, id: options.id});
- // that.$el.html(template);
- // }
- // })
-
},
+ selectAll : function (ev) {
+ var selected = ev.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ },
+ selectClicked : function (ev) {
+ var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ } else {
+ checkbox[0].checked = 'selected';
+ }
+ }
+ }
});
@@ -258,8 +251,7 @@
})
Backbone.history.start();
+
</script>
-
-
</body></html>
https://bitbucket.org/galaxy/galaxy-central/commits/518ded2c3f30/
Changeset: 518ded2c3f30
Branch: data_library
User: martenson
Date: 2013-11-06 23:15:41
Summary: work in progress on data libraries, integrated within galaxy framework with require, modularized
Affected #: 7 files
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -76,6 +76,22 @@
library_list_grid = LibraryListGrid()
+
+ @web.expose
+ def list( self, trans, **kwd ):
+ params = util.Params( kwd )
+
+ # define app configuration for generic mako template
+ app = {
+ 'jscript' : "galaxy.library"
+ }
+
+ # fill template
+ return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
+
+
+# return trans.fill_template( "/library/list.mako")
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 static/AAA_scratch/data_library/index.html
--- a/static/AAA_scratch/data_library/index.html
+++ b/static/AAA_scratch/data_library/index.html
@@ -83,7 +83,7 @@
</script>
- <script src="require.js"></script>
+
<script src="jquery-1.10.2.js" type="text/javascript"></script><script src="js/bootstrap.min.js"></script><script src="underscore.js" type="text/javascript"></script>
@@ -93,22 +93,6 @@
<script>
- // configure require
- // require.config({
- // baseUrl: "/",
- // shim: {
- // "libs/underscore": { exports: "_" },
- // "libs/backbone/backbone": { exports: "Backbone" },
- // "libs/backbone/backbone-relational": ["libs/backbone/backbone"]
- // }
- // });
-
- // require(['galaxy.modal'], function(modal)
- // {
- // Galaxy.modal = new modal.GalaxyModal();
- // });
-
-
// ENCODE HTML
function htmlEncode(value){
return $('<div/>').text(value).html();
@@ -245,9 +229,9 @@
libraryListView.render();
})
- router.on('route:folder_content', function(id, params) {
+ router.on('route:folder_content', function(id) {
// render folder's content
- folderContentView.render({id: id, params: params});
+ folderContentView.render({id: id});
})
Backbone.history.start();
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 static/scripts/galaxy.library.js
--- /dev/null
+++ b/static/scripts/galaxy.library.js
@@ -0,0 +1,333 @@
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+// === GALAXY LIBRARY MODULE ====
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+
+// global variables
+var view = null;
+var library_router = null;
+
+// dependencies
+define(["galaxy.modal", "galaxy.master", "galaxy.library.router"], function(mod_modal, mod_master, router) {
+
+// MMMMMMMMMMMMMMM
+// === Models ====
+// MMMMMMMMMMMMMMM
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries',
+ });
+
+ // LIBRARIES
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // Container for folder contents (folders, items and metadata).
+ var FolderContainer = Backbone.Model.extend({
+ defaults: {
+ folder: new Folder(),
+ full_path: "unknown",
+ urlRoot: "/api/folders/",
+ id: "unknown"
+ },
+ parse: function(obj) {
+ this.full_path = obj[0].full_path;
+ // this.folder.reset(obj[1].folder_contents);
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+ return obj;
+ }
+ })
+
+// MMMMMMMMMMMMMM
+// === Views ====
+// MMMMMMMMMMMMMM
+
+// galaxy folder
+var FolderContentView = Backbone.View.extend({
+ el : '#center',
+ // initialize
+ initialize : function(){
+ // view = this;
+ //set up the library router
+ // this.set_up_router();
+
+ //render
+ // this.render();
+ },
+ // set up router
+ set_up_router : function(){
+ if (library_router === null){
+ library_router = new router.LibraryRouter();
+ Backbone.history.start();
+ }
+ },
+ template_folder : function (){
+ tmpl_array = [];
+
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> |');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<%- path_item[1] %>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('<table class="table table-hover table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>type</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>');
+ tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>');
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>');
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' ');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ return tmpl_array.join('');
+ },
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked'
+ },
+ render: function (options) {
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+
+ folderContainer.fetch({
+ success: function (container) {
+ // folderContainer.attributes.folder = container.attributes.folder;
+ var template = _.template(that.template_folder(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
+ that.$el.html(template);
+ }
+ })
+ },
+ selectAll : function (ev) {
+ var selected = ev.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ },
+ selectClicked : function (ev) {
+ var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ } else {
+ checkbox[0].checked = 'selected';
+ }
+ }
+ }
+});
+
+
+// galaxy library
+var GalaxyLibraryview = Backbone.View.extend({
+ el: '#center',
+
+ events: {
+ 'click #create_new_library_btn' : 'show_library_modal'
+ },
+
+
+ // initialize
+ initialize : function(){
+ // view = this;
+ //set up the libray router
+ // this.set_up_router();
+
+ //render
+ // this.render();
+ },
+
+ // set up router
+ set_up_router : function(){
+ if (library_router === null){
+ library_router = new router.LibraryRouter();
+ Backbone.history.start();
+ }
+ },
+
+ // template
+ template_library_list : function (){
+ tmpl_array = [];
+
+ tmpl_array.push('');
+ tmpl_array.push('<h1>Welcome to the data libraries</h1>');
+ tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary">New Library</a>');
+ tmpl_array.push('<table class="table table-striped">');
+ tmpl_array.push(' <thead>');
+ 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(' <th>id</th> ');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(libraries, function(library) { %>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%- library.get("name") %></a></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(' <td><a href="#/folders/<%- library.id %>"><%= _.escape(library.get("id")) %></a></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('');
+ tmpl_array.push('');
+ tmpl_array.push('');
+ tmpl_array.push('');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ return tmpl_array.join('');
+ },
+
+ // render
+ render: function () {
+ var that = this;
+ // if (typeof libraries === "undefined") {
+ libraries = new Libraries();
+ // }
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template(that.template_library_list(), {libraries: libraries.models});
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // own modal
+ modal : null,
+
+ // show/hide create library modal
+ show_library_modal : function (e)
+ {
+ // prevent default
+ e.preventDefault();
+
+ // create modal
+ if (!this.modal){
+ // make modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal(
+ {
+ title : 'Create New Library',
+ body : this.template_new_library(),
+ buttons : {
+ 'Create' : function() {self.create_new_library_event()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+ }
+
+ // show modal
+ this.modal.show();
+ },
+ create_new_library_event: function(){
+ var libraryDetails = this.serialize_new_library();
+ var library = new Library();
+ library.save(libraryDetails, {
+ success: function (library) {
+
+ library_router.navigate('', {trigger: true})
+ }
+ });
+ // console.log(libraryDetails);
+ return false;
+ },
+ serialize_new_library : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val(),
+ synopsis: $("input[name='Synopsis']").val()
+ };
+ },
+ // load html template
+ 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('<label>Synopsis</label>');
+ tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');
+ tmpl_array.push('');
+ tmpl_array.push('</form>');
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+
+
+ // return '<div id="'+ id +'"></div>';
+ }
+});
+
+// galaxy library wrapper View
+var GalaxyLibrary = Backbone.View.extend({
+ folderContentView : null,
+ galaxyLibraryview : null,
+ initialize : function(){
+
+ folderContentView = new FolderContentView();
+ galaxyLibraryview = new GalaxyLibraryview();
+
+ library_router = new router.LibraryRouter();
+
+ library_router.on('route:libraries', function() {
+ // render libraries list
+ galaxyLibraryview.render();
+ })
+
+ library_router.on('route:folder_content', function(id) {
+ // render folder's contents
+ folderContentView.render({id: id});
+
+ })
+
+ // library_router.on('route:show_library_modal', function() {
+ // // render folder's contents
+ // galaxyLibraryview.show_library_modal();
+
+ // })
+
+ Backbone.history.start();
+
+ return this
+ }
+});
+
+// return
+return {
+ GalaxyApp: GalaxyLibrary
+};
+
+});
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 static/scripts/galaxy.library.router.js
--- /dev/null
+++ b/static/scripts/galaxy.library.router.js
@@ -0,0 +1,21 @@
+define( [], function() {
+
+/**
+ * -- Routers --
+ */
+
+/**
+ * Router for library browser.
+ */
+var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content"
+ }
+});
+
+return {
+ LibraryRouter: LibraryRouter,
+};
+
+})
\ No newline at end of file
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster' ];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'new_library', 'trackster' ];
var _ = grunt.util._;
var fmt = _.sprintf;
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 templates/base/base_panels.mako
--- a/templates/base/base_panels.mako
+++ b/templates/base/base_panels.mako
@@ -102,12 +102,13 @@
});
## load galaxy js-modules
- require(['galaxy.master', 'galaxy.frame', 'galaxy.modal', 'galaxy.upload'], function(master, frame, modal, upload)
+ require(['galaxy.master', 'galaxy.frame', 'galaxy.modal', 'galaxy.upload', 'galaxy.library'], function(master, frame, modal, upload, library)
{
Galaxy.master = new master.GalaxyMaster();
Galaxy.frame_manager = new frame.GalaxyFrameManager();
Galaxy.modal = new modal.GalaxyModal();
- ##Galaxy.upload = new upload.GalaxyUpload();
+ Galaxy.upload = new upload.GalaxyUpload();
+ ##Galaxy.library = new library.GalaxyLibrary();
});
</script></%def>
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 templates/webapps/galaxy/base_panels.mako
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -126,6 +126,7 @@
## 'Shared Items' or Libraries tab.
<%
menu_options = [
+ [ _('New Libraries'), h.url_for( controller='/library', action='list') ],
[ _('Data Libraries'), h.url_for( controller='/library', action='index') ],
None,
[ _('Published Histories'), h.url_for( controller='/history', action='list_published' ) ],
https://bitbucket.org/galaxy/galaxy-central/commits/3ea8be4161ab/
Changeset: 3ea8be4161ab
Branch: data_library
User: martenson
Date: 2013-11-12 22:48:56
Summary: transfer into modal window, requirejs, notifications, import to history, galaxy.modal changes, API for library datasets, start of API versioning
Affected #: 6 files
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -2000,6 +2000,7 @@
genome_build = ldda.dbkey,
misc_info = ldda.info,
misc_blurb = ldda.blurb,
+ peek = ( lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None )( ldda ),
template_data = template_data )
if ldda.dataset.uuid is None:
rval['uuid'] = None
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -0,0 +1,38 @@
+"""
+API operations on the contents of a dataset from library.
+"""
+from galaxy import web
+from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
+
+import logging
+log = logging.getLogger( __name__ )
+
+class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
+
+ @web.expose_api
+ def index( self, trans, **kwd ):
+ """
+ GET /api/libraries/datasets
+ """
+ trans.response.status = 501
+ return 'not implemented'
+
+ @web.expose_api
+ def show( self, trans, id, **kwd ):
+ """
+ GET /api/libraries/datasets/{encoded_dataset_id}
+ Displays information about and/or content of a dataset identified by the lda ID.
+ """
+ # Get dataset.
+ try:
+ dataset = self.get_library_dataset( trans, id = id )
+ except Exception, e:
+ return str( e )
+ try:
+ # Default: return dataset as dict.
+ rval = dataset.to_dict()
+ except Exception, e:
+ rval = "Error in dataset API at listing contents: " + str( e )
+ log.error( rval + ": %s" % str(e), exc_info=True )
+ trans.response.status = 500
+ return rval
\ No newline at end of file
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,18 +46,19 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
- webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
- controller='admin_toolshed',
- action='display_image_in_repository',
- repository_id=None,
- image_file=None )
+
+ # Add the controllers folder
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
+ # Add the api folder
+ webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
+ # Add the api folder VERSION 2
+ webapp.add_api_controllers( 'galaxy.webapps.galaxy.api.v2', app )
+
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
# Force /activate to go to the controller
webapp.add_route( '/activate', controller='user', action='activate' )
- # These two routes handle our simple needs at the moment
+
webapp.add_route( '/async/:tool_id/:data_id/:data_secret', controller='async', action='index', tool_id=None, data_id=None, data_secret=None )
webapp.add_route( '/:controller/:action', action='index' )
webapp.add_route( '/:action', controller='root', action='index' )
@@ -66,8 +67,8 @@
webapp.add_route( '/datasets/:dataset_id/display/{filename:.+?}', controller='dataset', action='display', dataset_id=None, filename=None)
webapp.add_route( '/datasets/:dataset_id/:action/:filename', controller='dataset', action='index', dataset_id=None, filename=None)
webapp.add_route( '/display_application/:dataset_id/:app_name/:link_name/:user_id/:app_action/:action_param',
- controller='dataset', action='display_application', dataset_id=None, user_id=None,
- app_name = None, link_name = None, app_action = None, action_param = None )
+ controller='dataset', action='display_application', dataset_id=None, user_id=None,
+ app_name = None, link_name = None, app_action = None, action_param = None )
webapp.add_route( '/u/:username/d/:slug/:filename', controller='dataset', action='display_by_username_and_slug', filename=None )
webapp.add_route( '/u/:username/p/:slug', controller='page', action='display_by_username_and_slug' )
webapp.add_route( '/u/:username/h/:slug', controller='history', action='display_by_username_and_slug' )
@@ -75,22 +76,6 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
webapp.add_route( '/search', controller='search', action='index' )
- # Add the web API
- webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
@@ -102,10 +87,6 @@
controller="datasets",
action="display",
conditions=dict(method=["GET"]))
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'user',
'users',
controller='group_users',
@@ -127,11 +108,6 @@
_add_item_tags_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
-
_add_item_annotation_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -147,31 +123,58 @@
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
webapp.mapper.resource( 'dataset', 'datasets', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
webapp.mapper.resource( 'sample', 'samples', path_prefix='/api' )
webapp.mapper.resource( 'request', 'requests', path_prefix='/api' )
webapp.mapper.resource( 'form', 'forms', path_prefix='/api' )
webapp.mapper.resource( 'request_type', 'request_types', path_prefix='/api' )
webapp.mapper.resource( 'role', 'roles', path_prefix='/api' )
webapp.mapper.resource( 'group', 'groups', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
webapp.mapper.resource( 'tool', 'tools', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' )
webapp.mapper.resource( 'genome', 'genomes', path_prefix='/api' )
webapp.mapper.resource( 'visualization', 'visualizations', path_prefix='/api' )
webapp.mapper.resource( 'workflow', 'workflows', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' )
webapp.mapper.resource( 'configuration', 'configuration', path_prefix='/api' )
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' )
+
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
+
+ webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
+ webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
+
+ webapp.mapper.resource( 'content', 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
+
+
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
- webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
- controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
+ webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current", controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
# visualizations registry generic template renderer
- webapp.add_route( '/visualization/show/:visualization_name',
- controller='visualization', action='render', visualization_name=None )
+ webapp.add_route( '/visualization/show/:visualization_name', controller='visualization', action='render', visualization_name=None )
# "POST /api/workflows/import" => ``workflows.import_workflow()``.
# Defines a named route "import_workflow".
@@ -179,6 +182,15 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+
+ # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
+ webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
+ controller='admin_toolshed',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
@@ -191,6 +203,7 @@
path_prefix='/api',
new={ 'install_repository_revision' : 'POST' },
parent_resources=dict( member_name='tool_shed_repository', collection_name='tool_shed_repositories' ) )
+
# Connect logger from app
if app.trace_logger:
webapp.trace_logger = app.trace_logger
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -80,18 +80,13 @@
@web.expose
def list( self, trans, **kwd ):
params = util.Params( kwd )
-
# define app configuration for generic mako template
app = {
'jscript' : "galaxy.library"
}
-
# fill template
return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
-
-
-# return trans.fill_template( "/library/list.mako")
-
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -7,7 +7,7 @@
var library_router = null;
// dependencies
-define(["galaxy.modal", "galaxy.master", "galaxy.library.router"], function(mod_modal, mod_master, router) {
+define(["galaxy.modal", "galaxy.master"], function(mod_modal, mod_master) {
// MMMMMMMMMMMMMMM
// === Models ====
@@ -15,40 +15,65 @@
// LIBRARY
var Library = Backbone.Model.extend({
- urlRoot: '/api/libraries',
- });
+ urlRoot: '/api/libraries'
+ });
// LIBRARIES
var Libraries = Backbone.Collection.extend({
url: '/api/libraries',
model: Library
- });
+ });
// ITEM
var Item = Backbone.Model.extend({
+ urlRoot : '/api/libraries/datasets'
})
// FOLDER
var Folder = Backbone.Collection.extend({
model: Item
- })
+ })
- // Container for folder contents (folders, items and metadata).
+ // CONTAINER for folder contents (folders, items and metadata).
var FolderContainer = Backbone.Model.extend({
- defaults: {
- folder: new Folder(),
- full_path: "unknown",
- urlRoot: "/api/folders/",
- id: "unknown"
- },
- parse: function(obj) {
- this.full_path = obj[0].full_path;
- // this.folder.reset(obj[1].folder_contents);
+ defaults : {
+ folder : new Folder(),
+ full_path : "unknown",
+ urlRoot : "/api/folders/",
+ id : "unknown"
+ },
+ parse : function(obj) {
+ this.full_path = obj[0].full_path;
// update the inner collection
this.get("folder").reset(obj[1].folder_contents);
return obj;
+ }
+ })
+
+ // HISTORY ITEM
+ var HistoryItem = Backbone.Model.extend({
+ urlRoot : '/api/histories/'
+ });
+
+ // HISTORY
+ var GalaxyHistory = Backbone.Model.extend({
+ url : '/api/histories/'
+ });
+
+ // HISTORIES
+ var GalaxyHistories = Backbone.Collection.extend({
+ url : '/api/histories',
+ model : GalaxyHistory
+ });
+
+ //ROUTER
+ var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content"
}
- })
+ });
+
// MMMMMMMMMMMMMM
// === Views ====
@@ -56,67 +81,155 @@
// galaxy folder
var FolderContentView = Backbone.View.extend({
- el : '#center',
+ //main element
+ el : '#center',
// initialize
initialize : function(){
- // view = this;
- //set up the library router
- // this.set_up_router();
+ this.folders = [];
+ },
+ // set up
+ templateFolder : function (){
+ var tmpl_array = [];
- //render
- // this.render();
- },
- // set up router
- set_up_router : function(){
- if (library_router === null){
- library_router = new router.LibraryRouter();
- Backbone.history.start();
- }
+ tmpl_array.push('<a href="#">Libraries</a> | ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>'); //breadcrumb
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> |');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<%- path_item[1] %>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('<table class="table table-hover table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>type</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); //folder
+ tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); //empty folder
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td><a class="library-dataset" href="#"><%- content_item.get("name") %></a></td>'); //dataset
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' ');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ return tmpl_array.join('');
},
- template_folder : function (){
- tmpl_array = [];
+ templateDatasetModal : function(){
+ var tmpl_array = [];
- tmpl_array.push('<% _.each(path, function(path_item) { %>');
- tmpl_array.push('<% if (path_item[0] != id) { %>');
- tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> |');
- tmpl_array.push('<% } else { %>');
- tmpl_array.push('<%- path_item[1] %>');
- tmpl_array.push('<% } %>');
- tmpl_array.push('<% }); %>');
- tmpl_array.push('<table class="table table-hover table-condensed">');
- tmpl_array.push(' <thead>');
- tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
- tmpl_array.push(' <th>name</th>');
- tmpl_array.push(' <th>type</th>');
- tmpl_array.push(' </thead>');
- tmpl_array.push(' <tbody>');
- tmpl_array.push(' <% _.each(items, function(content_item) { %>');
- tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
- tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
- tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>');
- tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
- tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>');
- tmpl_array.push(' <span class="muted">(empty folder)</span>');
- tmpl_array.push(' <% } %>');
- tmpl_array.push(' </td>');
- tmpl_array.push(' <% } else { %>');
- tmpl_array.push(' <td><%- content_item.get("name") %></td>');
- tmpl_array.push(' <% } %> ');
- tmpl_array.push(' <td><%= _.escape(content_item.get("type")) %></td>');
- tmpl_array.push(' </tr>');
- tmpl_array.push(' <% }); %>');
- tmpl_array.push(' ');
- tmpl_array.push(' ');
- tmpl_array.push(' ');
- tmpl_array.push(' </tbody>');
- tmpl_array.push('</table>');
+ tmpl_array.push('<div id="dataset_info_modal">');
+ tmpl_array.push(' <table class="table table-striped table-condensed">');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Name</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Data type</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("data_type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Genome build</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("genome_build")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Size</th>');
+ tmpl_array.push(' <td><%= _.escape(size) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Date uploaded</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Uploaded by</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
+ tmpl_array.push(' </tr>');
+ // tmpl_array.push(' </table>');
+ // tmpl_array.push(' <hr/>');
+ // tmpl_array.push(' <table class="table table-striped">');
+ tmpl_array.push(' <tr scope="row">');
+ tmpl_array.push(' <th scope="row">Data Lines</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Comment Lines</th>');
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Number of Columns</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Column Types</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </table>');
+ // tmpl_array.push(' <hr/>');
+ tmpl_array.push(' <pre class="peek">');
+ tmpl_array.push(' </pre>');
+ tmpl_array.push('</div>');
- return tmpl_array.join('');
- },
- events: {
- 'click #select-all-checkboxes' : 'selectAll',
- 'click .folder_row' : 'selectClicked'
- },
+ return tmpl_array.join('');
+ },
+ templateHistorySelectInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_footer" style="width:60%;">');
+ tmpl_array.push('<select name="history_import" style="width:60%; margin-left: 2em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</span>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+ // to string
+ size_to_string : function (size)
+ {
+ // identify unit
+ var unit = "";
+ if (size >= 100000000000) { size = size / 100000000000; unit = "TB"; } else
+ if (size >= 100000000) { size = size / 100000000; unit = "GB"; } else
+ if (size >= 100000) { size = size / 100000; unit = "MB"; } else
+ if (size >= 100) { size = size / 100; unit = "KB"; } else
+ { size = size * 10; unit = "b"; }
+ // return formatted string
+ return (Math.round(size) / 10) + unit;
+ },
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked',
+ 'click .library-dataset' : 'showDatasetDetails'
+ },
+ //self modal
+ modal : null,
+ //loaded folders
+ folders : null,
+ //render the view
render: function (options) {
var that = this;
@@ -125,80 +238,131 @@
folderContainer.fetch({
success: function (container) {
- // folderContainer.attributes.folder = container.attributes.folder;
- var template = _.template(that.template_folder(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
- that.$el.html(template);
+ // folderContainer.attributes.folder = container.attributes.folder;
+ var template = _.template(that.templateFolder(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
+ that.$el.html(template);
}
- })
- },
- selectAll : function (ev) {
- var selected = ev.target.checked;
- // Iterate each checkbox
- $(':checkbox').each(function () { this.checked = selected; });
- },
- selectClicked : function (ev) {
- var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
- if (checkbox[0] != undefined) {
- if (checkbox[0].checked){
- checkbox[0].checked = '';
- } else {
- checkbox[0].checked = 'selected';
- }
+ })
+ },
+ //show modal with dataset info
+ showDatasetDetails : function(e){
+ // prevent default
+ e.preventDefault();
+
+ //TODO check whether we already have the data
+
+ //load the ID of the row
+ var id = $(e.target).parent().parent().attr('id');
+
+ //create new item
+ var item = new Item();
+ var histories = new GalaxyHistories();
+ item.id = id;
+ var self = this;
+ //fetch the dataset info
+ item.fetch({
+ success: function (item) {
+ //fetch user histories for import
+ histories.fetch({
+ success: function (histories){self.modalFetchSuccess(item, histories)}
+ });
+ }
+ });
+ },
+
+ modalFetchSuccess : function(item, histories){
+ var histories_modal = this.templateHistorySelectInModal();
+ var size = this.size_to_string(item.get('file_size'));
+ var template = _.template(this.templateDatasetModal(), { item : item, size : size });
+ // make modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal({
+ title : 'Dataset Details',
+ body : template,
+ buttons : {
+ 'Import' : function() {self.importIntoHistory()},
+ // 'Notify' : function() {self.modal.showNotification("TEST")},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+ $(".peek").html(item.get("peek"));
+ // this.modal.hideButton('Import');
+ // $(this.modal.elMain).find('.modal-footer').prepend("<div>BUBUBUBU" + "</div>");
+ var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+
+ // show the prepared modal
+ this.modal.show();
+ },
+
+ importIntoHistory : function(){
+ var history_id = 'a0c15f4d91084599';
+ var library_dataset_id = '03501d7626bd192f';
+
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ console.log(historyItem);
+ historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
+ self.modal.showNotification('Dataset imported', 3000, '#e1f4e0', '#32a427');
+ }
+ });
+ },
+
+ selectAll : function (ev) {
+ var selected = ev.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ },
+ selectClicked : function (ev) {
+ var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ } else {
+ checkbox[0].checked = 'selected';
+ }
+ }
}
- }
-});
+ });
// galaxy library
var GalaxyLibraryview = Backbone.View.extend({
el: '#center',
- events: {
+ events: {
'click #create_new_library_btn' : 'show_library_modal'
- },
+ },
-
// initialize
initialize : function(){
- // view = this;
- //set up the libray router
- // this.set_up_router();
-
- //render
- // this.render();
},
- // set up router
- set_up_router : function(){
- if (library_router === null){
- library_router = new router.LibraryRouter();
- Backbone.history.start();
- }
- },
-
// template
template_library_list : function (){
tmpl_array = [];
tmpl_array.push('');
tmpl_array.push('<h1>Welcome to the data libraries</h1>');
- tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary">New Library</a>');
+ tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary icon-file ">New Library</a>');
tmpl_array.push('<table class="table table-striped">');
tmpl_array.push(' <thead>');
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(' <th>id</th> ');
+ // tmpl_array.push(' <th>id</th> ');
tmpl_array.push(' </thead>');
tmpl_array.push(' <tbody>');
tmpl_array.push(' <% _.each(libraries, function(library) { %>');
- tmpl_array.push(' <tr>');
- tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%- library.get("name") %></a></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(' <td><a href="#/folders/<%- library.id %>"><%= _.escape(library.get("id")) %></a></td>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%- library.get("name") %></a></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(' <td><a href="#/folders/<%- library.id %>"><%= _.escape(library.get("id")) %></a></td>');
tmpl_array.push(' </tr>');
tmpl_array.push(' <% }); %>');
tmpl_array.push('');
@@ -219,18 +383,17 @@
// }
libraries.fetch({
success: function (libraries) {
- var template = _.template(that.template_library_list(), {libraries: libraries.models});
+ var template = _.template(that.template_library_list(), { libraries : libraries.models });
that.$el.html(template);
- }
- })
+ }
+ })
},
// own modal
modal : null,
// show/hide create library modal
- show_library_modal : function (e)
- {
+ show_library_modal : function (e){
// prevent default
e.preventDefault();
@@ -255,15 +418,21 @@
create_new_library_event: function(){
var libraryDetails = this.serialize_new_library();
var library = new Library();
+ var self = this;
library.save(libraryDetails, {
success: function (library) {
-
- library_router.navigate('', {trigger: true})
- }
- });
- // console.log(libraryDetails);
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ }
+ });
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(),
@@ -279,7 +448,6 @@
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('<label>Synopsis</label>');
tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');
tmpl_array.push('');
tmpl_array.push('</form>');
@@ -300,18 +468,18 @@
folderContentView = new FolderContentView();
galaxyLibraryview = new GalaxyLibraryview();
- library_router = new router.LibraryRouter();
-
+ library_router = new LibraryRouter();
+
library_router.on('route:libraries', function() {
// render libraries list
galaxyLibraryview.render();
- })
-
+ })
+
library_router.on('route:folder_content', function(id) {
// render folder's contents
folderContentView.render({id: id});
-
- })
+
+ })
// library_router.on('route:show_library_modal', function() {
// // render folder's contents
@@ -319,10 +487,10 @@
// })
- Backbone.history.start();
+Backbone.history.start();
- return this
- }
+return this
+}
});
// return
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -82,6 +82,7 @@
this.$footer = (this.$el).find('.modal-footer');
this.$buttons = (this.$el).find('.buttons');
this.$backdrop = (this.$el).find('.modal-backdrop');
+ this.$notification = (this.$el).find('.notification-modal');
// append body
this.$body.html(options.body);
@@ -122,6 +123,35 @@
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', true);
},
+ // hide buttons
+ hideButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).hide();
+ },
+ // show buttons
+ showButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).show();
+ },
+
+ // show notification
+ showNotification : function(message, duration, bgColor, txtColor) {
+ // defaults
+ var duration = typeof duration !== 'undefined' ? duration : 1500;
+ var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
+ var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+
+ var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
+ this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
+
+ var self = this;
+ /*animate the bar*/
+ $('#notification-bar').slideDown(function() {
+ setTimeout(function() {
+ $('#notification-bar').slideUp(function() {self.$notification.html('');});
+ }, duration);
+ });
+
+ },
+
// returns scroll top for body element
scrollTop: function()
{
@@ -137,10 +167,11 @@
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-content"">' +
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
+ '<span class="notification-modal"></span>' +
'</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
https://bitbucket.org/galaxy/galaxy-central/commits/2bb7df095a5a/
Changeset: 2bb7df095a5a
Branch: data_library
User: martenson
Date: 2013-12-03 22:43:06
Summary: slight refactoring and bugfix of db_next_hid generator
Affected #: 1 file
diff -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 -r 2bb7df095a5a23e3dce2e02fb963a30ff3b7d377 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1988,8 +1988,18 @@
table = self.table
trans = conn.begin()
try:
- next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
- table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
+
+# log.debug("XXXXXXXXXXXXXXX NEW GENERATOOOOOR")
+# next_hid = table.update() \
+# .returning( table.c.hid_counter ) \
+# .where( table.c.id == self.id ) \
+# .values( hid_counter = hid_counter + 1 )
+# log.debug('XXXXXXXXXXXXXXXXXXXXXXXXXXX NEXT HID: ' + str(next_hid))
+
+
+ current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
+ next_hid = current_hid + 1
+ table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
trans.commit()
return next_hid
except:
https://bitbucket.org/galaxy/galaxy-central/commits/4e53d7b759d1/
Changeset: 4e53d7b759d1
Branch: data_library
User: martenson
Date: 2013-12-03 22:43:25
Summary: added dropdown menu plugin to bootstrap
Affected #: 1 file
diff -r 2bb7df095a5a23e3dce2e02fb963a30ff3b7d377 -r 4e53d7b759d1999758a30611e5d897e3e1d20782 static/scripts/libs/bootstrap.js
--- a/static/scripts/libs/bootstrap.js
+++ b/static/scripts/libs/bootstrap.js
@@ -575,3 +575,158 @@
}
}(window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#dropdowns
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ var $el = $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ $parent.trigger(e = $.Event('show.bs.dropdown'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown')
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index=0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus() {
+ $(backdrop).remove()
+ $(toggle).each(function (e) {
+ var $parent = getParent($(this))
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown'))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('dropdown')
+
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
\ No newline at end of file
https://bitbucket.org/galaxy/galaxy-central/commits/44399512777b/
Changeset: 44399512777b
Branch: data_library
User: martenson
Date: 2013-12-03 22:44:22
Summary: library work, galaxy modal changes, api v2 preparation, API tweaks, API for dataset downloading implementation
Affected #: 9 files
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1768,7 +1768,7 @@
class Library( object, Dictifiable ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
dict_collection_visible_keys = ( 'id', 'name' )
- dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
+ dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id' )
def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 lib/galaxy/webapps/galaxy/api/datasets.py
--- a/lib/galaxy/webapps/galaxy/api/datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a dataset.
+API operations on the contents of a history dataset.
"""
from galaxy import web
from galaxy.visualization.data_providers.genome import FeatureLocationIndexDataProvider
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 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
@@ -42,19 +42,34 @@
GET /api/folders/{encoded_folder_id}/contents
Displays a collection (list) of a folder's contents (files and folders).
Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
+ Full path is provided as a separate object in response providing data for breadcrumb path building.
"""
folder_container = []
current_user_roles = trans.get_current_user_roles()
+
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX GOT id: ' + folder_id )
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX decode id: ' + folder_id[1:] )
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX call decode: ' + str(trans.security.decode_id( folder_id[1:] )) )
- try:
- decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
- except TypeError:
- trans.response.status = 400
- return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+# try:
+# decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
+# except TypeError:
+# trans.response.status = 400
+# return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX decoded id: ' + decoded_folder_id )
+
+ if ( folder_id.startswith( 'F' ) ):
+ try:
+ decoded_folder_id = trans.security.decode_id( folder_id[1:] )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
- parent_library = folder.parent_library
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX FOLDER id: ' + str(folder.id) )
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX FOLDER name: ' + str(folder.name) )
+# parent_library = folder.parent_library
except:
folder = None
log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
@@ -77,13 +92,17 @@
path_to_root = []
# We are almost in root
if folder.parent_id is None:
- log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING: " + str( folder.name ) )
+
+# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING ID: " + str( folder.id ) )
+# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING NAME: " + str( folder.name ) )
path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
# upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_library.id )
# path_to_root.append( ( upper_folder.id, upper_folder.name ) )
else:
# We add the current folder and traverse up one folder.
- log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ADDING THIS FOLDER AND TRAVERSING UP: " + str( folder.name ) )
+# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id " + str( folder.parent_id ) )
+# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING ID: " + str( folder.id ) )
+# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ADDING THIS FOLDER AND TRAVERSING UP: " + str( folder.name ) )
path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
path_to_root.extend( build_path( upper_folder ) )
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -1,27 +1,69 @@
"""
-API operations on the contents of a dataset from library.
+API operations on the dataset from library.
"""
-from galaxy import web
+import glob
+import logging
+import operator
+import os
+import os.path
+import string
+import sys
+import tarfile
+import tempfile
+import urllib
+import urllib2
+import zipfile
+from galaxy.security import Action
+from galaxy import util, web
+from galaxy.util.streamball import StreamBall
from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
import logging
log = logging.getLogger( __name__ )
+# Test for available compression types
+# tmpd = tempfile.mkdtemp()
+# comptypes = []
+# for comptype in ( 'gz', 'bz2' ):
+# tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype )
+# try:
+# archive = tarfile.open( tmpf, 'w:' + comptype )
+# archive.close()
+# comptypes.append( comptype )
+# except tarfile.CompressionError:
+# log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+ziptype = '32'
+# tmpf = os.path.join( tmpd, 'compression_test.zip' )
+# try:
+# archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+# archive.close()
+# comptypes.append( 'zip' )
+# ziptype = '64'
+# except RuntimeError:
+# log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." )
+# except (TypeError, zipfile.LargeZipFile):
+# # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped
+# log.warning( 'Max zip file size is 2GB, ZIP64 not supported' )
+# comptypes.append( 'zip' )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+# os.rmdir( tmpd )
+
+
+
class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
@web.expose_api
- def index( self, trans, **kwd ):
- """
- GET /api/libraries/datasets
- """
- trans.response.status = 501
- return 'not implemented'
-
- @web.expose_api
def show( self, trans, id, **kwd ):
"""
GET /api/libraries/datasets/{encoded_dataset_id}
- Displays information about and/or content of a dataset identified by the lda ID.
+ Displays information about the dataset identified by the lda ID.
"""
# Get dataset.
try:
@@ -35,4 +77,165 @@
rval = "Error in dataset API at listing contents: " + str( e )
log.error( rval + ": %s" % str(e), exc_info=True )
trans.response.status = 500
- return rval
\ No newline at end of file
+
+ rval['id'] = trans.security.encode_id(rval['id']);
+ rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
+ rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
+
+ return rval
+
+ @web.expose
+ def download( self, trans, format, **kwd ):
+ """
+ POST /api/libraries/datasets/download/{format}
+ POST data: ldda_ids = []
+ Downloads dataset(s) in the requested format.
+ """
+ lddas = []
+# is_admin = trans.user_is_admin()
+# current_user_roles = trans.get_current_user_roles()
+
+ datasets_to_download = kwd['ldda_ids%5B%5D']
+
+ if ( datasets_to_download != None ):
+ datasets_to_download = util.listify( datasets_to_download )
+ for dataset_id in datasets_to_download:
+ try:
+ ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ assert not ldda.dataset.purged
+ lddas.append( ldda )
+ except:
+ ldda = None
+ message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
+
+ if format in [ 'zip','tgz','tbz' ]:
+ error = False
+ killme = string.punctuation + string.whitespace
+ trantab = string.maketrans(killme,'_'*len(killme))
+ try:
+ outext = 'zip'
+ if format == 'zip':
+ # Can't use mkstemp - the file must not exist first
+ tmpd = tempfile.mkdtemp()
+ util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
+ tmpf = os.path.join( tmpd, 'library_download.' + format )
+ if ziptype == '64' and trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
+ elif ziptype == '64':
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+ elif trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED )
+ else:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED )
+ archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
+ elif format == 'tgz':
+ if trans.app.config.upstream_gzip:
+ archive = StreamBall( 'w|' )
+ outext = 'tar'
+ else:
+ archive = StreamBall( 'w|gz' )
+ outext = 'tgz'
+ elif format == 'tbz':
+ archive = StreamBall( 'w|bz2' )
+ outext = 'tbz2'
+ except ( OSError, zipfile.BadZipfile ):
+ error = True
+ log.exception( "Unable to create archive for download" )
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ except:
+ error = True
+ log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
+ message = "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
+ status = 'error'
+ if not error:
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
+ continue
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite:
+ # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
+ except IOError:
+ error = True
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
+ try:
+ archive.add( fpath,fname )
+ except IOError:
+ error = True
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ else: # simple case
+ try:
+ archive.add( ldda.dataset.file_name, path )
+ except IOError:
+ error = True
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ if not error:
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ elif format == 'uncompressed':
+ if len(lddas) != 1:
+ return 'Wrong request'
+ else:
+ single_dataset = lddas[0]
+ trans.response.set_content_type( single_dataset.get_mime() )
+ fStat = os.stat( ldda.file_name )
+ trans.response.headers[ 'Content-Length' ] = int( fStat.st_size )
+ valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ fname = ldda.name
+ fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
+ try:
+ return open( single_dataset.file_name )
+ except:
+ return 'This dataset contains no content'
+ else:
+ return 'Wrong format';
\ No newline at end of file
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -52,6 +52,7 @@
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
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -146,6 +146,7 @@
# =======================
webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
+ webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -5,6 +5,9 @@
// global variables
var view = null;
var library_router = null;
+var responses = [];
+var KEYCODE_ENTER = 13;
+var KEYCODE_ESC = 27;
// dependencies
define(["galaxy.modal", "galaxy.master"], function(mod_modal, mod_master) {
@@ -70,52 +73,99 @@
var LibraryRouter = Backbone.Router.extend({
routes: {
"" : "libraries",
- "folders/:id" : "folder_content"
+ "folders/:id" : "folder_content",
+ "folders/:folder_id/download/:format" : "download"
}
});
// MMMMMMMMMMMMMM
-// === Views ====
+// === VIEWS ====
// MMMMMMMMMMMMMM
// galaxy folder
var FolderContentView = Backbone.View.extend({
- //main element
+ // main element definition
el : '#center',
+ // progress percentage
+ progress: 0,
+ // progress rate per one item
+ progressStep: 1,
+ // last selected history in modal for UX
+ lastSelectedHistory: '',
+ // self modal
+ modal : null,
+ // loaded folders
+ folders : null,
+
// initialize
initialize : function(){
this.folders = [];
+ this.queue = jQuery.Deferred();
+ this.queue.resolve();
},
+
+// MMMMMMMMMMMMMMMMMM
+// === TEMPLATES ====
+// MMMMMMMMMMMMMMMMMM
+
// set up
templateFolder : function (){
var tmpl_array = [];
- tmpl_array.push('<a href="#">Libraries</a> | ');
+ // CONTAINER
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to Martin.</h3>');
+
+ // TOOLBAR
+ tmpl_array.push('<div id="library_folder_toolbar" >');
+ tmpl_array.push(' <button id="toolbtn_create_folder" class="btn btn-primary" type="button">new folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" style="display: none; margin-left: 0.5em;" type="button">Import into History</button>');
+
+ tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' Download <span class="caret"></span>');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
+ // tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/uncompressed">uncompressed</a></li>');
+ // tmpl_array.push(' <li class="divider"></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
+ tmpl_array.push(' </ul>');
+ tmpl_array.push(' </div>');
+
+ tmpl_array.push('</div>');
+
+ // BREADCRUMBS
+ tmpl_array.push('<a href="#">Libraries</a><b>|</b> ');
tmpl_array.push('<% _.each(path, function(path_item) { %>'); //breadcrumb
- tmpl_array.push('<% if (path_item[0] != id) { %>');
- tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> |');
- tmpl_array.push('<% } else { %>');
- tmpl_array.push('<%- path_item[1] %>');
- tmpl_array.push('<% } %>');
- tmpl_array.push('<% }); %>');
- tmpl_array.push('<table class="table table-hover table-condensed">');
- tmpl_array.push(' <thead>');
- tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
- tmpl_array.push(' <th>name</th>');
- tmpl_array.push(' <th>type</th>');
- tmpl_array.push(' </thead>');
- tmpl_array.push(' <tbody>');
- tmpl_array.push(' <% _.each(items, function(content_item) { %>');
- tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
- tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<%- path_item[1] %>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+
+ // FODLER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-hover table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>type</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); //folder
- tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
+ tmpl_array.push(' <td style="text-align: center; "></td>');
+ tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); //empty folder
tmpl_array.push(' <span class="muted">(empty folder)</span>');
tmpl_array.push(' <% } %>');
tmpl_array.push(' </td>');
tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
tmpl_array.push(' <td><a class="library-dataset" href="#"><%- content_item.get("name") %></a></td>'); //dataset
tmpl_array.push(' <% } %> ');
tmpl_array.push(' <td><%= _.escape(content_item.get("type")) %></td>');
@@ -123,10 +173,10 @@
tmpl_array.push(' <% }); %>');
tmpl_array.push(' ');
tmpl_array.push(' ');
- tmpl_array.push(' ');
tmpl_array.push(' </tbody>');
tmpl_array.push('</table>');
+ tmpl_array.push('</div>');
return tmpl_array.join('');
},
templateDatasetModal : function(){
@@ -135,7 +185,7 @@
tmpl_array.push('<div id="dataset_info_modal">');
tmpl_array.push(' <table class="table table-striped table-condensed">');
tmpl_array.push(' <tr>');
- tmpl_array.push(' <th scope="row">Name</th>');
+ tmpl_array.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');
tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
tmpl_array.push(' </tr>');
tmpl_array.push(' <tr>');
@@ -157,9 +207,6 @@
tmpl_array.push(' <th scope="row">Uploaded by</th>');
tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
tmpl_array.push(' </tr>');
- // tmpl_array.push(' </table>');
- // tmpl_array.push(' <hr/>');
- // tmpl_array.push(' <table class="table table-striped">');
tmpl_array.push(' <tr scope="row">');
tmpl_array.push(' <th scope="row">Data Lines</th>');
tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
@@ -183,31 +230,45 @@
tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
tmpl_array.push(' </tr>');
- tmpl_array.push(' <tr>');
- tmpl_array.push(' ');
tmpl_array.push(' </table>');
- // tmpl_array.push(' <hr/>');
tmpl_array.push(' <pre class="peek">');
tmpl_array.push(' </pre>');
tmpl_array.push('</div>');
return tmpl_array.join('');
},
+
templateHistorySelectInModal : function(){
var tmpl_array = [];
- tmpl_array.push('<span id="history_modal_footer" style="width:60%;">');
- tmpl_array.push('<select name="history_import" style="width:60%; margin-left: 2em; "> ');
+ tmpl_array.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');
tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
- tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
- tmpl_array.push(' <% }); %>');
- tmpl_array.push('</span>');
- tmpl_array.push('</div>');
- tmpl_array.push('');
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
- return tmpl_array.join('');
- },
- // to string
+ return tmpl_array.join('');
+ },
+
+ templateBulkImportInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ // convert size to nice string
size_to_string : function (size)
{
// identify unit
@@ -220,17 +281,26 @@
// return formatted string
return (Math.round(size) / 10) + unit;
},
- events: {
+
+// MMMMMMMMMMMMMMM
+// === EVENTS ====
+// MMMMMMMMMMMMMMM
+
+ // event binding
+ events: {
'click #select-all-checkboxes' : 'selectAll',
'click .folder_row' : 'selectClicked',
+ 'click #toolbtn_bulk_import' : 'modalBulkImport',
+ 'click #toolbtn_dl' : 'bulkDownload',
'click .library-dataset' : 'showDatasetDetails'
},
- //self modal
- modal : null,
- //loaded folders
- folders : null,
- //render the view
+
+ //render the folder view
render: function (options) {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+
+ view = this;
var that = this;
var folderContainer = new FolderContainer({id: options.id});
@@ -241,15 +311,16 @@
// folderContainer.attributes.folder = container.attributes.folder;
var template = _.template(that.templateFolder(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
that.$el.html(template);
- }
- })
- },
- //show modal with dataset info
+ }
+ })
+ },
+
+ //show modal with current dataset info
showDatasetDetails : function(e){
// prevent default
e.preventDefault();
- //TODO check whether we already have the data
+//TODO check whether we already have the data
//load the ID of the row
var id = $(e.target).parent().parent().attr('id');
@@ -259,53 +330,90 @@
var histories = new GalaxyHistories();
item.id = id;
var self = this;
+
//fetch the dataset info
item.fetch({
success: function (item) {
- //fetch user histories for import
+// TODO can render here already
+ //fetch user histories for import purposes
histories.fetch({
- success: function (histories){self.modalFetchSuccess(item, histories)}
+ success: function (histories){self.renderModalAfterFetch(item, histories)}
});
}
});
},
- modalFetchSuccess : function(item, histories){
- var histories_modal = this.templateHistorySelectInModal();
+ // show the current dataset in a modal
+ 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({
title : 'Dataset Details',
body : template,
buttons : {
- 'Import' : function() {self.importIntoHistory()},
- // 'Notify' : function() {self.modal.showNotification("TEST")},
- 'Close' : function() {self.modal.hide()}
+ 'Import' : function() { self.importCurrentIntoHistory() },
+ 'Download' : function() { self.downloadCurrent() },
+ 'Close' : function() { self.modal.hide(); $('.modal').html(''); self.modal = null; } // TODO refill nicely modal with data
}
});
$(".peek").html(item.get("peek"));
- // this.modal.hideButton('Import');
- // $(this.modal.elMain).find('.modal-footer').prepend("<div>BUBUBUBU" + "</div>");
var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
- $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+ // preset last selected history if we know it
+ if (self.lastSelectedHistory.length > 0) {
+ $(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
+ }
// show the prepared modal
this.modal.show();
},
- importIntoHistory : function(){
- var history_id = 'a0c15f4d91084599';
- var library_dataset_id = '03501d7626bd192f';
+ // download dataset shown currently in modal
+ downloadCurrent : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+ var library_dataset_id = [];
+ library_dataset_id.push($('#id_row').attr('data-id'));
+ var url = '/api/libraries/datasets/download/uncompressed';
+ var data = {'ldda_ids' : library_dataset_id};
+
+ // we assume the view is existent
+ folderContentView.processDownload(url, data);
+ this.modal.enableButton('Import');
+ this.modal.enableButton('Download');
+ },
+
+ // import dataset shown currently in modal into selected history
+ importCurrentIntoHistory : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var history_id = $(this.modal.elMain).find('select[name=dataset_import_single] option:selected').val();
+ this.lastSelectedHistory = history_id; //save selected history for further use
+
+ var library_dataset_id = $('#id_row').attr('data-id');
var historyItem = new HistoryItem();
var self = this;
historyItem.url = historyItem.urlRoot + history_id + '/contents';
- console.log(historyItem);
+
+ // save the dataset into selected history
historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
self.modal.showNotification('Dataset imported', 3000, '#e1f4e0', '#32a427');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }, error : function(){
+ self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, '#f4e0e1', '#a42732');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
}
});
},
@@ -314,9 +422,11 @@
var selected = ev.target.checked;
// Iterate each checkbox
$(':checkbox').each(function () { this.checked = selected; });
+ this.showTools();
},
- selectClicked : function (ev) {
- var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
+
+ selectClicked : function (ev) {
+ var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
if (checkbox[0] != undefined) {
if (checkbox[0].checked){
checkbox[0].checked = '';
@@ -324,11 +434,161 @@
checkbox[0].checked = 'selected';
}
}
- }
+ this.showTools();
+ },
+
+ // show toolbar in case something is selected
+ showTools : function(){
+ var checkedValues = $('#folder_table').find(':checked');
+ if(checkedValues.length > 0){
+ $('#toolbtn_bulk_import').show();
+ $('#toolbtn_dl').show();
+ } else {
+ $('#toolbtn_bulk_import').hide();
+ $('#toolbtn_dl').hide();
+ }
+
+ },
+
+ // show bulk import modal
+ modalBulkImport : function(){
+ var self = this;
+ // fetch histories
+ var histories = new GalaxyHistories();
+ histories.fetch({
+ success: function (histories){
+ // make modal
+ var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
+ self.modal = new mod_modal.GalaxyModal({
+ title : 'Import into History',
+ body : history_modal_tmpl,
+ buttons : {
+ 'Import' : function() {self.importAllIntoHistory()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+ // show the prepared modal
+ self.modal.show();
+ }
+ });
+ },
+
+ // import all selected datasets into history
+ importAllIntoHistory : function (){
+ //disable the button
+ this.modal.disableButton('Import');
+
+ var history_id = $("select[name=dataset_import_bulk] option:selected").val();
+ var history_name = $("select[name=dataset_import_bulk] option:selected").text();
+
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+ var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
+ $(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
+
+ // init the progress bar
+ var progressStep = 100 / dataset_ids.length;
+ this.initProgress(progressStep);
+
+ // prepare the dataset objects to be imported
+ var datasets_to_import = [];
+ for (var i = dataset_ids.length - 1; i >= 0; i--) {
+ library_dataset_id = dataset_ids[i];
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ historyItem.content = library_dataset_id;
+ historyItem.source = 'library';
+ datasets_to_import.push(historyItem);
+ };
+
+ // call the recursive function to call ajax one after each other
+ this.chainCall(datasets_to_import);
+ },
+
+ chainCall: function(history_item_set){
+ var self = this;
+ var popped_item = history_item_set.pop();
+ if (typeof popped_item === "undefined") {
+ self.modal.showNotification('All datasets imported', 3000, '#e1f4e0', '#32a427');
+ // enable button again
+ self.modal.enableButton('Import');
+ return
+ }
+ var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
+ self.updateProgress();
+ responses.push(a1);
+ self.chainCall(history_item_set);
+ });
+ },
+
+ initProgress: function(progressStep){
+ this.progress = 0;
+ this.progressStep = progressStep;
+ },
+ updateProgress: function(){
+ this.progress += this.progressStep;
+ $('.progress-bar').width(Math.round(this.progress) + '%');
+ txt_representation = Math.round(this.progress) + '% Complete';
+ $('.completion_span').text(txt_representation);
+ },
+
+ // progress bar
+ templateProgressBar : function (){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div class="import_text">');
+ tmpl_array.push('Importing selected datasets to history <b><%= _.escape(history_name) %></b>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('<div class="progress">');
+ tmpl_array.push(' <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');
+ tmpl_array.push(' <span class="completion_span">0% Complete</span>');
+ tmpl_array.push(' </div>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+
+ // download selected datasets
+ download : function(folder_id, format){
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+
+ var url = '/api/libraries/datasets/download/' + format;
+ var data = {'ldda_ids' : dataset_ids};
+ this.processDownload(url, data);
+ },
+
+ // create hidden form and submit through POST to initialize download
+ processDownload: function(url, data, method){
+ //url and data options required
+ if( url && data ){
+ //data can be string of parameters or array/object
+ data = typeof data == 'string' ? data : $.param(data);
+ //split params into form inputs
+ var inputs = '';
+ $.each(data.split('&'), function(){
+ var pair = this.split('=');
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ });
+ //send request
+ $('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
+ .appendTo('body').submit().remove();
+ };
+ }
+
});
-
-// galaxy library
+// galaxy library view
var GalaxyLibraryview = Backbone.View.extend({
el: '#center',
@@ -343,9 +603,10 @@
// template
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('<h1>Welcome to the data libraries</h1>');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to Martin.</h3>');
tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary icon-file ">New Library</a>');
tmpl_array.push('<table class="table table-striped">');
tmpl_array.push(' <thead>');
@@ -353,30 +614,27 @@
tmpl_array.push(' <th>description</th>');
tmpl_array.push(' <th>synopsis</th> ');
tmpl_array.push(' <th>model type</th> ');
- // tmpl_array.push(' <th>id</th> ');
tmpl_array.push(' </thead>');
tmpl_array.push(' <tbody>');
tmpl_array.push(' <% _.each(libraries, function(library) { %>');
- tmpl_array.push(' <tr>');
- tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%- library.get("name") %></a></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(' <td><a href="#/folders/<%- library.id %>"><%= _.escape(library.get("id")) %></a></td>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <td><a href="#/folders/<%- library.get("root_folder_id") %>"><%- library.get("name") %></a></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('');
- tmpl_array.push('');
- tmpl_array.push('');
- tmpl_array.push('');
tmpl_array.push(' </tbody>');
tmpl_array.push('</table>');
-
+
+ tmpl_array.push('</div>');
return tmpl_array.join('');
},
// render
render: function () {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
var that = this;
// if (typeof libraries === "undefined") {
libraries = new Libraries();
@@ -394,12 +652,11 @@
// show/hide create library modal
show_library_modal : function (e){
- // prevent default
+ // prevent default, may be unnecessary
e.preventDefault();
// create modal
if (!this.modal){
- // make modal
var self = this;
this.modal = new mod_modal.GalaxyModal(
{
@@ -412,9 +669,11 @@
});
}
- // show modal
+ // show prepared modal
this.modal.show();
},
+
+ // create the new library from modal
create_new_library_event: function(){
var libraryDetails = this.serialize_new_library();
var library = new Library();
@@ -428,11 +687,15 @@
});
return false;
},
+
+ // clear the library modal once saved
clear_library_modal : function(){
$("input[name='Name']").val('');
$("input[name='Description']").val('');
$("input[name='Synopsis']").val('');
},
+
+ // serialize data from the form
serialize_new_library : function(){
return {
name: $("input[name='Name']").val(),
@@ -440,10 +703,12 @@
synopsis: $("input[name='Synopsis']").val()
};
},
- // load html template
+
+ // template for new library modal
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">');
@@ -452,10 +717,8 @@
tmpl_array.push('');
tmpl_array.push('</form>');
tmpl_array.push('</div>');
+
return tmpl_array.join('');
-
-
- // return '<div id="'+ id +'"></div>';
}
});
@@ -473,19 +736,28 @@
library_router.on('route:libraries', function() {
// render libraries list
galaxyLibraryview.render();
- })
+ });
library_router.on('route:folder_content', function(id) {
// render folder's contents
folderContentView.render({id: id});
+ });
- })
+ library_router.on('route:download', function(folder_id, format) {
+ // send download stream
+ if (typeof folderContentView === 'undefined'){
+ alert('you cant touch this!');
+ // } else if (folderContentView.modal !== null){
+ // folderContentView.download(folder_id, format);
+ } else if ($('#center').find(':checked').length === 0) { // coming from outside of the library app
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ // TODO show message of redirection
+ } else {
+ folderContentView.download(folder_id, format);
+ library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
+ }
- // library_router.on('route:show_library_modal', function() {
- // // render folder's contents
- // galaxyLibraryview.show_library_modal();
-
- // })
+ });
Backbone.history.start();
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -20,9 +20,28 @@
// initialize
initialize : function(options) {
+ self = this;
// create
if (options)
this.create(options);
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the click anywhere to hide() function...
+ $('html').on('click', function(event){
+ self.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // destroy
+ destroy : function(){
+ this.hide();
+ $('.modal').html('');
},
// adds and displays a new frame/window
https://bitbucket.org/galaxy/galaxy-central/commits/f466834f693f/
Changeset: f466834f693f
Branch: data_library
User: martenson
Date: 2013-12-10 19:01:19
Summary: improved styles for libraries, added features to modal window, API tweaks
Affected #: 5 files
diff -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 -r f466834f693ff7ae0f60cf121d97f67a5338ea95 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1988,15 +1988,6 @@
table = self.table
trans = conn.begin()
try:
-
-# log.debug("XXXXXXXXXXXXXXX NEW GENERATOOOOOR")
-# next_hid = table.update() \
-# .returning( table.c.hid_counter ) \
-# .where( table.c.id == self.id ) \
-# .values( hid_counter = hid_counter + 1 )
-# log.debug('XXXXXXXXXXXXXXXXXXXXXXXXXXX NEXT HID: ' + str(next_hid))
-
-
current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
next_hid = current_hid + 1
table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
diff -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 -r f466834f693ff7ae0f60cf121d97f67a5338ea95 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
@@ -46,17 +46,6 @@
"""
folder_container = []
current_user_roles = trans.get_current_user_roles()
-
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX GOT id: ' + folder_id )
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX decode id: ' + folder_id[1:] )
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX call decode: ' + str(trans.security.decode_id( folder_id[1:] )) )
-
-# try:
-# decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
-# except TypeError:
-# trans.response.status = 400
-# return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX decoded id: ' + decoded_folder_id )
if ( folder_id.startswith( 'F' ) ):
try:
@@ -67,9 +56,6 @@
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX FOLDER id: ' + str(folder.id) )
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX FOLDER name: ' + str(folder.name) )
-# parent_library = folder.parent_library
except:
folder = None
log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
@@ -92,17 +78,9 @@
path_to_root = []
# We are almost in root
if folder.parent_id is None:
-
-# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING ID: " + str( folder.id ) )
-# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING NAME: " + str( folder.name ) )
path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
-# upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_library.id )
-# path_to_root.append( ( upper_folder.id, upper_folder.name ) )
else:
# We add the current folder and traverse up one folder.
-# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id " + str( folder.parent_id ) )
-# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING ID: " + str( folder.id ) )
-# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ADDING THIS FOLDER AND TRAVERSING UP: " + str( folder.name ) )
path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
path_to_root.extend( build_path( upper_folder ) )
@@ -113,25 +91,40 @@
folder_container.append( dict( full_path = full_path ) )
folder_contents = []
+ time_updated = ''
+ time_created = ''
# Go through every item in the folder and include its meta-data.
for content_item in self.load_folder_contents( trans, folder ):
+# rval = content_item.to_dict()
return_item = {}
encoded_id = trans.security.encode_id( content_item.id )
+ time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
+# log.debug('XXXXXXXXXXXXXXXXXXX api type: ' + str(content_item.api_type))
+# log.debug('XXXXXXXX ALL: ' + str(content_item.__dict__))
# For folder return also hierarchy values
if content_item.api_type == 'folder':
-# encoded_parent_library_id = trans.security.encode_id( content_item.parent_library.id )
encoded_id = 'F' + encoded_id
-# if content_item.parent_id is not None: # Return folder's parent id for browsing back.
-# encoded_parent_id = 'F' + trans.security.encode_id( content_item.parent_id )
- last_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
- return_item.update ( dict ( item_count = content_item.item_count, last_updated = last_updated ) )
-
+# time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count ) )
+
+ if content_item.api_type == 'file':
+# log.debug('XXXXX content item class: ' + str(content_item.__class__))
+ library_dataset_dict = content_item.to_dict()
+ library_dataset_dict['data_type']
+ library_dataset_dict['file_size']
+ library_dataset_dict['date_uploaded']
+ return_item.update ( dict ( data_type = library_dataset_dict['data_type'],
+ file_size = library_dataset_dict['file_size'],
+ date_uploaded = library_dataset_dict['date_uploaded'] ) )
+
# For every item return also the default meta-data
return_item.update( dict( id = encoded_id,
type = content_item.api_type,
- name = content_item.name
-
+ name = content_item.name,
+ time_updated = time_updated,
+ time_created = time_created
) )
folder_contents.append( return_item )
# Put the data in the container
diff -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 -r f466834f693ff7ae0f60cf121d97f67a5338ea95 static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -6,8 +6,16 @@
var view = null;
var library_router = null;
var responses = [];
-var KEYCODE_ENTER = 13;
-var KEYCODE_ESC = 27;
+
+
+// load required libraries
+require([
+ // load js libraries
+ 'utils/galaxy.css',
+ ], function(css){
+ // load css
+ css.load_file("static/style/library.css");
+});
// dependencies
define(["galaxy.modal", "galaxy.master"], function(mod_modal, mod_master) {
@@ -115,20 +123,18 @@
// CONTAINER
tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
- tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to Martin.</h3>');
+ 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>');
// TOOLBAR
tmpl_array.push('<div id="library_folder_toolbar" >');
- tmpl_array.push(' <button id="toolbtn_create_folder" class="btn btn-primary" type="button">new folder</button>');
- tmpl_array.push(' <button id="toolbtn_bulk_import" style="display: none; margin-left: 0.5em;" type="button">Import into History</button>');
+ tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-icon-plus"></span><span class="fa fa-icon-folder-close"></span> folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-icon-external-link"></span> to history</button>');
tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
- tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">');
- tmpl_array.push(' Download <span class="caret"></span>');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' <span class="fa fa-icon-download"></span> download <span class="caret"></span>');
tmpl_array.push(' </button>');
tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
- // tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/uncompressed">uncompressed</a></li>');
- // tmpl_array.push(' <li class="divider"></li>');
tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
@@ -138,41 +144,64 @@
tmpl_array.push('</div>');
// BREADCRUMBS
- tmpl_array.push('<a href="#">Libraries</a><b>|</b> ');
- tmpl_array.push('<% _.each(path, function(path_item) { %>'); //breadcrumb
+ tmpl_array.push('<div class="library_breadcrumb">');
+ tmpl_array.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
tmpl_array.push('<% if (path_item[0] != id) { %>');
- tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
tmpl_array.push('<% } else { %>');
- tmpl_array.push('<%- path_item[1] %>');
+ tmpl_array.push('<span title="You are in this folder"><%- path_item[1] %></span>');
tmpl_array.push('<% } %>');
tmpl_array.push('<% }); %>');
+ tmpl_array.push('</div>');
- // FODLER CONTENT
- tmpl_array.push('<table id="folder_table" class="table table-hover table-condensed">');
+ // FOLDER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-condensed">');
tmpl_array.push(' <thead>');
tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th class="button_heading">view</th>');
tmpl_array.push(' <th>name</th>');
- tmpl_array.push(' <th>type</th>');
+ tmpl_array.push(' <th>data type</th>');
+ tmpl_array.push(' <th>size</th>');
+ tmpl_array.push(' <th>date</th>');
tmpl_array.push(' </thead>');
tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
tmpl_array.push(' <% _.each(items, function(content_item) { %>');
- tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
- tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); //folder
- tmpl_array.push(' <td style="text-align: center; "></td>');
- tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
- tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); //empty folder
+ tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
tmpl_array.push(' <span class="muted">(empty folder)</span>');
tmpl_array.push(' <% } %>');
tmpl_array.push(' </td>');
+ tmpl_array.push(' <td>folder</td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>'); // size
tmpl_array.push(' <% } else { %>');
tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
- tmpl_array.push(' <td><a class="library-dataset" href="#"><%- content_item.get("name") %></a></td>'); //dataset
+ tmpl_array.push(' <td>');
+ tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-eye-open"></span> details');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
+ tmpl_array.push(' <td><%= _.escape(content_item.get("data_type")) %></td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>'); // size
tmpl_array.push(' <% } %> ');
- tmpl_array.push(' <td><%= _.escape(content_item.get("type")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>'); // time updated
tmpl_array.push(' </tr>');
tmpl_array.push(' <% }); %>');
tmpl_array.push(' ');
- tmpl_array.push(' ');
tmpl_array.push(' </tbody>');
tmpl_array.push('</table>');
@@ -292,7 +321,9 @@
'click .folder_row' : 'selectClicked',
'click #toolbtn_bulk_import' : 'modalBulkImport',
'click #toolbtn_dl' : 'bulkDownload',
- 'click .library-dataset' : 'showDatasetDetails'
+ 'click .library-dataset' : 'showDatasetDetails',
+ 'click #toolbtn_create_folder' : 'createFolderModal',
+ 'click .btn_open_folder' : 'navigateToFolder'
},
//render the folder view
@@ -308,22 +339,52 @@
folderContainer.fetch({
success: function (container) {
- // folderContainer.attributes.folder = container.attributes.folder;
- var template = _.template(that.templateFolder(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
+
+ // prepare nice size strings
+ for (var i = 0; i < folderContainer.attributes.folder.models.length; i++) {
+ var model = folderContainer.attributes.folder.models[i]
+ if (model.get('type') === 'file'){
+ model.set('readable_size', that.size_to_string(model.get('file_size')))
+ }
+ };
+
+ // find the upper id
+ var path = folderContainer.full_path;
+ var upper_folder_id;
+ if (path.length === 1){ // library is above us
+ upper_folder_id = 0;
+ } else {
+ upper_folder_id = path[path.length-2][0];
+ }
+
+ var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
+ // var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
that.$el.html(template);
}
})
},
+ // handles the click on 'open' and 'upper' folder icons
+ navigateToFolder : function(event){
+ var folder_id = $(event.target).attr('data-id');
+ if (typeof folder_id === 'undefined') {
+ return false;
+ } else if (folder_id === '0'){
+ library_router.navigate('#', {trigger: true, replace: true});
+ } else {
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ }
+ },
+
//show modal with current dataset info
- showDatasetDetails : function(e){
+ showDatasetDetails : function(event){
// prevent default
- e.preventDefault();
+ event.preventDefault();
//TODO check whether we already have the data
//load the ID of the row
- var id = $(e.target).parent().parent().attr('id');
+ var id = $(event.target).parent().parent().attr('id');
//create new item
var item = new Item();
@@ -356,7 +417,7 @@
buttons : {
'Import' : function() { self.importCurrentIntoHistory() },
'Download' : function() { self.downloadCurrent() },
- 'Close' : function() { self.modal.hide(); $('.modal').html(''); self.modal = null; } // TODO refill nicely modal with data
+ 'Close' : function() { self.modal.hide(); $('.modal').remove(); self.modal = null; } // TODO refill nicely modal with data
}
});
$(".peek").html(item.get("peek"));
@@ -405,12 +466,12 @@
// save the dataset into selected history
historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
- self.modal.showNotification('Dataset imported', 3000, '#e1f4e0', '#32a427');
+ self.modal.showNotification('Dataset imported', 3000, 'success');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
}, error : function(){
- self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, '#f4e0e1', '#a42732');
+ self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
@@ -418,20 +479,32 @@
});
},
- selectAll : function (ev) {
- var selected = ev.target.checked;
+ // select all datasets
+ selectAll : function (event) {
+ var selected = event.target.checked;
// Iterate each checkbox
$(':checkbox').each(function () { this.checked = selected; });
this.showTools();
},
- selectClicked : function (ev) {
- var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
+ // click checkbox on folder click
+ selectClicked : function (event) {
+ var checkbox = $("#" + event.target.parentElement.id).find(':checkbox')
if (checkbox[0] != undefined) {
if (checkbox[0].checked){
checkbox[0].checked = '';
+ // $(event.target.parentElement).css('background-color', '').css('color', '');
+ $(event.target.parentElement).removeClass('dark');
+ $(event.target.parentElement).find('a').removeClass('dark');
+ $(event.target.parentElement).addClass('light');
+ $(event.target.parentElement).find('a').addClass('light');
} else {
checkbox[0].checked = 'selected';
+ $(event.target.parentElement).removeClass('light');
+ $(event.target.parentElement).find('a').removeClass('light');
+ $(event.target.parentElement).addClass('dark');
+ $(event.target.parentElement).find('a').addClass('dark');
+ // $(event.target.parentElement).css('background-color', '#8389a1').css('color', 'white');
}
}
this.showTools();
@@ -464,7 +537,7 @@
body : history_modal_tmpl,
buttons : {
'Import' : function() {self.importAllIntoHistory()},
- 'Close' : function() {self.modal.hide()}
+ 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
}
});
// show the prepared modal
@@ -514,7 +587,7 @@
var self = this;
var popped_item = history_item_set.pop();
if (typeof popped_item === "undefined") {
- self.modal.showNotification('All datasets imported', 3000, '#e1f4e0', '#32a427');
+ self.modal.showNotification('All datasets imported', 3000, 'success');
// enable button again
self.modal.enableButton('Import');
return
@@ -584,6 +657,11 @@
$('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
.appendTo('body').submit().remove();
};
+ },
+
+ // shows modal for creating folder
+ createFolderModal: function(){
+ alert('creating folder');
}
});
@@ -606,11 +684,12 @@
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 Martin.</h3>');
+ 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 icon-file ">New Library</a>');
- tmpl_array.push('<table class="table table-striped">');
+ tmpl_array.push('<table class="table table-condensed">');
tmpl_array.push(' <thead>');
- tmpl_array.push(' <th>name</th>');
+ 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> ');
@@ -618,7 +697,9 @@
tmpl_array.push(' <tbody>');
tmpl_array.push(' <% _.each(libraries, function(library) { %>');
tmpl_array.push(' <tr>');
- tmpl_array.push(' <td><a href="#/folders/<%- library.get("root_folder_id") %>"><%- library.get("name") %></a></td>');
+ 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-icon-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>');
@@ -651,12 +732,11 @@
modal : null,
// show/hide create library modal
- show_library_modal : function (e){
- // prevent default, may be unnecessary
- e.preventDefault();
+ show_library_modal : function (event){
+ event.preventDefault();
+ event.stopPropagation();
// create modal
- if (!this.modal){
var self = this;
this.modal = new mod_modal.GalaxyModal(
{
@@ -667,7 +747,6 @@
'Close' : function() {self.modal.hide()}
}
});
- }
// show prepared modal
this.modal.show();
@@ -676,6 +755,7 @@
// create the new library from modal
create_new_library_event: function(){
var libraryDetails = this.serialize_new_library();
+ var valid = this.validate_new_library(libraryDetails);
var library = new Library();
var self = this;
library.save(libraryDetails, {
@@ -683,7 +763,10 @@
self.modal.hide();
self.clear_library_modal();
self.render();
- }
+ },
+ error: function(){
+ self.modal.showNotification('An error occured', 5000, 'error');
+ }
});
return false;
},
@@ -704,6 +787,11 @@
};
},
+ validate_new_library: function(library){
+
+ },
+
+
// template for new library modal
template_new_library: function()
{
@@ -714,7 +802,6 @@
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('');
tmpl_array.push('</form>');
tmpl_array.push('</div>');
diff -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 -r f466834f693ff7ae0f60cf121d97f67a5338ea95 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -1,6 +1,5 @@
-/*
- galaxy modal
-*/
+// galaxy modal
+
// dependencies
define(["libs/backbone/backbone-relational"], function() {
@@ -24,24 +23,48 @@
// create
if (options)
this.create(options);
- // bind the ESC key to hide() function
- $(document).on('keyup', function(event){
- if (event.keyCode == 27) { self.hide(); }
- })
- // bind the click anywhere to hide() function...
- $('html').on('click', function(event){
- self.hide();
- })
- // ...but don't hide if the click is on modal content
- $('.modal-content').on('click', function(event){
- event.stopPropagation();
- })
+
+ this.bindClick(event, self);
},
+ // bind the click-to-hide function
+ bindClick: function(event, that) {
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the click anywhere to hide() function...
+ $('html').on('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // unbind the click-to-hide function
+ unbindClick: function(event, that){
+ // bind the ESC key to hide() function
+ $(document).off('keyup', function(event){
+ if (event.keyCode == 27) { that.hide(); }
+ })
+ // unbind the click anywhere to hide() function...
+ $('html').off('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').off('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+
// destroy
destroy : function(){
this.hide();
- $('.modal').html('');
+ this.unbindClick();
+ $('.modal').remove();
},
// adds and displays a new frame/window
@@ -152,16 +175,28 @@
},
// show notification
- showNotification : function(message, duration, bgColor, txtColor) {
+ showNotification : function(message, duration, type) {
// defaults
var duration = typeof duration !== 'undefined' ? duration : 1500;
- var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
- var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ // var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
+ // var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ var bgColor;
+ var txtColor;
+
+ if (type === 'error'){
+ bgColor = '#f4e0e1';
+ txtColor = '#a42732';
+ // } else if (type === 'success'){
+ } else { // success is default
+ bgColor = '#e1f4e0';
+ txtColor = '#32a427';
+ }
var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
var self = this;
+
/*animate the bar*/
$('#notification-bar').slideDown(function() {
setTimeout(function() {
diff -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 -r f466834f693ff7ae0f60cf121d97f67a5338ea95 static/style/src/less/library.less
--- a/static/style/src/less/library.less
+++ b/static/style/src/less/library.less
@@ -29,6 +29,50 @@
background-color: @table-bg-accent;
}
+
+tr.light td
+{
+ background-color: white;
+ color: black;
+}
+tr.light:hover td
+{
+ background-color: #f5f5f5;
+ color: #8389a1;
+}
+tr.dark td
+{
+ background-color: #8389a1;
+ color: white;
+}
+tr.dark:hover td
+{
+ background-color: #bbbfd0;
+ color: white;
+}
+a.dark:hover
+{
+ color: yellow;
+ // text-decoration: none;
+}
+a.dark
+{
+ color: white;
+ // text-decoration: none;
+}
+th.button_heading
+{
+ width: 7em;
+}
+div.library_breadcrumb{
+ padding-top: 0.8em;
+ padding-bottom: 0.8em;
+}
+div.library_breadcrumb a:hover{
+ color:green;
+}
+
+
img.expanderIcon {
padding-right: 4px;
}
https://bitbucket.org/galaxy/galaxy-central/commits/e2b8d3481d66/
Changeset: e2b8d3481d66
User: martenson
Date: 2013-12-11 00:02:36
Summary: Merge data libraries branch to default
Affected #: 19 files
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -116,3 +116,6 @@
*.rej
*~
+
+syntax: regexp
+^static/AAA_scratch$
\ No newline at end of file
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1827,7 +1827,7 @@
class Library( object, Dictifiable ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
dict_collection_visible_keys = ( 'id', 'name' )
- dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
+ dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id' )
def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
@@ -1894,7 +1894,7 @@
return name
class LibraryFolder( object, Dictifiable ):
- dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
+ dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time' )
def __init__( self, name=None, description=None, item_count=0, order_id=None ):
self.name = name or "Unnamed folder"
self.description = description
@@ -2060,6 +2060,7 @@
genome_build = ldda.dbkey,
misc_info = ldda.info,
misc_blurb = ldda.blurb,
+ peek = ( lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None )( ldda ),
template_data = template_data )
if ldda.dataset.uuid is None:
rval['uuid'] = None
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1878,8 +1878,9 @@
table = self.table
trans = conn.begin()
try:
- next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
- table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
+ current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
+ next_hid = current_hid + 1
+ table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
trans.commit()
return next_hid
except:
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/webapps/galaxy/api/datasets.py
--- a/lib/galaxy/webapps/galaxy/api/datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a dataset.
+API operations on the contents of a history dataset.
"""
from galaxy import web
from galaxy.visualization.data_providers.genome import FeatureLocationIndexDataProvider
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 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
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a library.
+API operations on the contents of a folder.
"""
import logging, os, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
@@ -11,67 +11,125 @@
log = logging.getLogger( __name__ )
class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ):
+ """
+ Class controls retrieval, creation and updating of folder contents.
+ """
+
+ def load_folder_contents( self, trans, folder ):
+ """
+ Loads all contents of the folder (folders and data sets) but only in the first level.
+ """
+ current_user_roles = trans.get_current_user_roles()
+ is_admin = trans.user_is_admin()
+ content_items = []
+ for subfolder in folder.active_folders:
+ if not is_admin:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
+ if (is_admin or can_access) and not subfolder.deleted:
+ subfolder.api_type = 'folder'
+ content_items.append( subfolder )
+ for dataset in folder.datasets:
+ if not is_admin:
+ can_access = trans.app.security_agent.can_access_dataset( current_user_roles, dataset.library_dataset_dataset_association.dataset )
+ if (is_admin or can_access) and not dataset.deleted:
+ dataset.api_type = 'file'
+ content_items.append( dataset )
+ return content_items
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
Displays a collection (list) of a folder's contents (files and folders).
- The /api/library_contents/{encoded_library_id}/contents
- lists everything in a library recursively, which is not what
- we want here. We could add a parameter to use the recursive
- style, but this is meant to act similar to an "ls" directory listing.
+ Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
+ Full path is provided as a separate object in response providing data for breadcrumb path building.
"""
- rval = []
+ folder_container = []
current_user_roles = trans.get_current_user_roles()
- def traverse( folder ):
- admin = trans.user_is_admin()
- rval = []
- for subfolder in folder.active_folders:
- if not admin:
- can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
- if (admin or can_access) and not subfolder.deleted:
- subfolder.api_type = 'folder'
- rval.append( subfolder )
- for ld in folder.datasets:
- if not admin:
- can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ld.library_dataset_dataset_association.dataset )
- if (admin or can_access) and not ld.deleted:
- ld.api_type = 'file'
- rval.append( ld )
- return rval
-
- try:
- decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
- except TypeError:
- trans.response.status = 400
- return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+ if ( folder_id.startswith( 'F' ) ):
+ try:
+ decoded_folder_id = trans.security.decode_id( folder_id[1:] )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
- parent_library = folder.parent_library
except:
folder = None
- log.error( "FolderContentsController.index: Unable to retrieve folder %s"
- % folder_id )
+ log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
- # TODO: Find the API's path to this folder if necessary.
- # This was needed in recursive descent, but it's not needed
- # for "ls"-style content checking:
- if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ # We didn't find the folder or user does not have an access to it.
+ if not folder:
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, folder.id ) )
+ trans.response.status = 400
+ return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ path_to_root = []
+ def build_path ( folder ):
+ """
+ Search the path upwards recursively and load the whole route of names and ids for breadcrumb purposes.
+ """
+ path_to_root = []
+ # We are almost in root
+ if folder.parent_id is None:
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ else:
+ # We add the current folder and traverse up one folder.
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
+ path_to_root.extend( build_path( upper_folder ) )
+ return path_to_root
+
+ # Return the reversed path so it starts with the library node.
+ full_path = build_path( folder )[::-1]
+ folder_container.append( dict( full_path = full_path ) )
+
+ folder_contents = []
+ time_updated = ''
+ time_created = ''
+ # Go through every item in the folder and include its meta-data.
+ for content_item in self.load_folder_contents( trans, folder ):
+# rval = content_item.to_dict()
+ return_item = {}
+ encoded_id = trans.security.encode_id( content_item.id )
+ time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
+# log.debug('XXXXXXXXXXXXXXXXXXX api type: ' + str(content_item.api_type))
+# log.debug('XXXXXXXX ALL: ' + str(content_item.__dict__))
+
+ # For folder return also hierarchy values
+ if content_item.api_type == 'folder':
+ encoded_id = 'F' + encoded_id
+# time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count ) )
- for content in traverse( folder ):
- encoded_id = trans.security.encode_id( content.id )
- if content.api_type == 'folder':
- encoded_id = 'F' + encoded_id
- rval.append( dict( id = encoded_id,
- type = content.api_type,
- name = content.name,
- url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
- return rval
+ if content_item.api_type == 'file':
+# log.debug('XXXXX content item class: ' + str(content_item.__class__))
+ library_dataset_dict = content_item.to_dict()
+ library_dataset_dict['data_type']
+ library_dataset_dict['file_size']
+ library_dataset_dict['date_uploaded']
+ return_item.update ( dict ( data_type = library_dataset_dict['data_type'],
+ file_size = library_dataset_dict['file_size'],
+ date_uploaded = library_dataset_dict['date_uploaded'] ) )
+
+ # For every item return also the default meta-data
+ return_item.update( dict( id = encoded_id,
+ type = content_item.api_type,
+ name = content_item.name,
+ time_updated = time_updated,
+ time_created = time_created
+ ) )
+ folder_contents.append( return_item )
+ # Put the data in the container
+ folder_container.append( dict( folder_contents = folder_contents ) )
+ return folder_container
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -0,0 +1,241 @@
+"""
+API operations on the dataset from library.
+"""
+import glob
+import logging
+import operator
+import os
+import os.path
+import string
+import sys
+import tarfile
+import tempfile
+import urllib
+import urllib2
+import zipfile
+from galaxy.security import Action
+from galaxy import util, web
+from galaxy.util.streamball import StreamBall
+from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
+
+import logging
+log = logging.getLogger( __name__ )
+
+# Test for available compression types
+# tmpd = tempfile.mkdtemp()
+# comptypes = []
+# for comptype in ( 'gz', 'bz2' ):
+# tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype )
+# try:
+# archive = tarfile.open( tmpf, 'w:' + comptype )
+# archive.close()
+# comptypes.append( comptype )
+# except tarfile.CompressionError:
+# log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+ziptype = '32'
+# tmpf = os.path.join( tmpd, 'compression_test.zip' )
+# try:
+# archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+# archive.close()
+# comptypes.append( 'zip' )
+# ziptype = '64'
+# except RuntimeError:
+# log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." )
+# except (TypeError, zipfile.LargeZipFile):
+# # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped
+# log.warning( 'Max zip file size is 2GB, ZIP64 not supported' )
+# comptypes.append( 'zip' )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+# os.rmdir( tmpd )
+
+
+
+class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
+
+ @web.expose_api
+ def show( self, trans, id, **kwd ):
+ """
+ GET /api/libraries/datasets/{encoded_dataset_id}
+ Displays information about the dataset identified by the lda ID.
+ """
+ # Get dataset.
+ try:
+ dataset = self.get_library_dataset( trans, id = id )
+ except Exception, e:
+ return str( e )
+ try:
+ # Default: return dataset as dict.
+ rval = dataset.to_dict()
+ except Exception, e:
+ rval = "Error in dataset API at listing contents: " + str( e )
+ log.error( rval + ": %s" % str(e), exc_info=True )
+ trans.response.status = 500
+
+ rval['id'] = trans.security.encode_id(rval['id']);
+ rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
+ rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
+
+ return rval
+
+ @web.expose
+ def download( self, trans, format, **kwd ):
+ """
+ POST /api/libraries/datasets/download/{format}
+ POST data: ldda_ids = []
+ Downloads dataset(s) in the requested format.
+ """
+ lddas = []
+# is_admin = trans.user_is_admin()
+# current_user_roles = trans.get_current_user_roles()
+
+ datasets_to_download = kwd['ldda_ids%5B%5D']
+
+ if ( datasets_to_download != None ):
+ datasets_to_download = util.listify( datasets_to_download )
+ for dataset_id in datasets_to_download:
+ try:
+ ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ assert not ldda.dataset.purged
+ lddas.append( ldda )
+ except:
+ ldda = None
+ message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
+
+ if format in [ 'zip','tgz','tbz' ]:
+ error = False
+ killme = string.punctuation + string.whitespace
+ trantab = string.maketrans(killme,'_'*len(killme))
+ try:
+ outext = 'zip'
+ if format == 'zip':
+ # Can't use mkstemp - the file must not exist first
+ tmpd = tempfile.mkdtemp()
+ util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
+ tmpf = os.path.join( tmpd, 'library_download.' + format )
+ if ziptype == '64' and trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
+ elif ziptype == '64':
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+ elif trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED )
+ else:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED )
+ archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
+ elif format == 'tgz':
+ if trans.app.config.upstream_gzip:
+ archive = StreamBall( 'w|' )
+ outext = 'tar'
+ else:
+ archive = StreamBall( 'w|gz' )
+ outext = 'tgz'
+ elif format == 'tbz':
+ archive = StreamBall( 'w|bz2' )
+ outext = 'tbz2'
+ except ( OSError, zipfile.BadZipfile ):
+ error = True
+ log.exception( "Unable to create archive for download" )
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ except:
+ error = True
+ log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
+ message = "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
+ status = 'error'
+ if not error:
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
+ continue
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite:
+ # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
+ except IOError:
+ error = True
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
+ try:
+ archive.add( fpath,fname )
+ except IOError:
+ error = True
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ else: # simple case
+ try:
+ archive.add( ldda.dataset.file_name, path )
+ except IOError:
+ error = True
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ if not error:
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ elif format == 'uncompressed':
+ if len(lddas) != 1:
+ return 'Wrong request'
+ else:
+ single_dataset = lddas[0]
+ trans.response.set_content_type( single_dataset.get_mime() )
+ fStat = os.stat( ldda.file_name )
+ trans.response.headers[ 'Content-Length' ] = int( fStat.st_size )
+ valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ fname = ldda.name
+ fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
+ try:
+ return open( single_dataset.file_name )
+ except:
+ return 'This dataset contains no content'
+ else:
+ return 'Wrong format';
\ No newline at end of file
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -49,9 +49,10 @@
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
rval = []
for library in query:
- item = library.to_dict()
+ item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
- item['id'] = trans.security.encode_id( item['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
@@ -131,6 +132,9 @@
rval['name'] = name
rval['id'] = encoded_id
return rval
+
+ def edit( self, trans, payload, **kwd ):
+ return "Not implemented yet"
@web.expose_api
def delete( self, trans, id, **kwd ):
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,6 +46,7 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
+ # Add the controllers folder
# Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
controller='admin_toolshed',
@@ -53,11 +54,17 @@
repository_id=None,
image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
+ # Add the api folder
+ webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
+ # Add the api folder VERSION 2
+ webapp.add_api_controllers( 'galaxy.webapps.galaxy.api.v2', app )
+
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
# Force /activate to go to the controller
webapp.add_route( '/activate', controller='user', action='activate' )
# These two routes handle our simple needs at the moment
+
webapp.add_route( '/async/:tool_id/:data_id/:data_secret', controller='async', action='index', tool_id=None, data_id=None, data_secret=None )
webapp.add_route( '/:controller/:action', action='index' )
webapp.add_route( '/:action', controller='root', action='index' )
@@ -66,8 +73,6 @@
webapp.add_route( '/datasets/:dataset_id/display/{filename:.+?}', controller='dataset', action='display', dataset_id=None, filename=None)
webapp.add_route( '/datasets/:dataset_id/:action/:filename', controller='dataset', action='index', dataset_id=None, filename=None)
webapp.add_route( '/display_application/:dataset_id/:app_name/:link_name/:user_id/:app_action/:action_param',
- controller='dataset', action='display_application', dataset_id=None, user_id=None,
- app_name = None, link_name = None, app_action = None, action_param = None )
webapp.add_route( '/u/:username/d/:slug/:filename', controller='dataset', action='display_by_username_and_slug', filename=None )
webapp.add_route( '/u/:username/p/:slug', controller='page', action='display_by_username_and_slug' )
webapp.add_route( '/u/:username/h/:slug', controller='history', action='display_by_username_and_slug' )
@@ -171,9 +176,44 @@
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' )
+
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
+ webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
+
+ webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
+ webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
+
+ webapp.mapper.resource( 'content', 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
+
+
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
+ webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current", controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
webapp.mapper.connect( "create_api_key", "/api/users/:user_id/api_key",
controller="users", action="api_key", user_id=None, conditions=dict( method=["POST"] ) )
@@ -181,6 +221,7 @@
# visualizations registry generic template renderer
webapp.add_route( '/visualization/show/:visualization_name',
controller='visualization', action='render', visualization_name=None )
+ webapp.add_route( '/visualization/show/:visualization_name', controller='visualization', action='render', visualization_name=None )
# "POST /api/workflows/import" => ``workflows.import_workflow()``.
# Defines a named route "import_workflow".
@@ -188,6 +229,15 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+
+ # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
+ webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
+ controller='admin_toolshed',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -76,6 +76,17 @@
library_list_grid = LibraryListGrid()
+
+ @web.expose
+ def list( self, trans, **kwd ):
+ params = util.Params( kwd )
+ # define app configuration for generic mako template
+ app = {
+ 'jscript' : "galaxy.library"
+ }
+ # fill template
+ return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/scripts/galaxy.library.js
--- /dev/null
+++ b/static/scripts/galaxy.library.js
@@ -0,0 +1,860 @@
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+// === GALAXY LIBRARY MODULE ====
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+
+// global variables
+var view = null;
+var library_router = null;
+var responses = [];
+
+
+// load required libraries
+// require([
+// // load js libraries
+// 'utils/galaxy.css',
+// ], function(css){
+// // load css
+// css.load_file("static/style/library.css");
+// });
+
+// dependencies
+define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils"], function(mod_modal, mod_masthead, mod_utils) {
+
+// MMMMMMMMMMMMMMM
+// === Models ====
+// MMMMMMMMMMMMMMM
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries'
+ });
+
+ // LIBRARIES
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ urlRoot : '/api/libraries/datasets'
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // CONTAINER for folder contents (folders, items and metadata).
+ var FolderContainer = Backbone.Model.extend({
+ defaults : {
+ folder : new Folder(),
+ full_path : "unknown",
+ urlRoot : "/api/folders/",
+ id : "unknown"
+ },
+ parse : function(obj) {
+ this.full_path = obj[0].full_path;
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+ return obj;
+ }
+ })
+
+ // HISTORY ITEM
+ var HistoryItem = Backbone.Model.extend({
+ urlRoot : '/api/histories/'
+ });
+
+ // HISTORY
+ var GalaxyHistory = Backbone.Model.extend({
+ url : '/api/histories/'
+ });
+
+ // HISTORIES
+ var GalaxyHistories = Backbone.Collection.extend({
+ url : '/api/histories',
+ model : GalaxyHistory
+ });
+
+ //ROUTER
+ var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content",
+ "folders/:folder_id/download/:format" : "download"
+ }
+ });
+
+
+// MMMMMMMMMMMMMM
+// === VIEWS ====
+// MMMMMMMMMMMMMM
+
+// galaxy folder
+var FolderContentView = Backbone.View.extend({
+ // main element definition
+ el : '#center',
+ // progress percentage
+ progress: 0,
+ // progress rate per one item
+ progressStep: 1,
+ // last selected history in modal for UX
+ lastSelectedHistory: '',
+ // self modal
+ modal : null,
+ // loaded folders
+ folders : null,
+
+ // initialize
+ initialize : function(){
+ this.folders = [];
+ this.queue = jQuery.Deferred();
+ this.queue.resolve();
+ },
+
+// MMMMMMMMMMMMMMMMMM
+// === TEMPLATES ====
+// MMMMMMMMMMMMMMMMMM
+
+ // set up
+ templateFolder : function (){
+ var tmpl_array = [];
+
+ // CONTAINER
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
+ 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>');
+
+ // TOOLBAR
+ tmpl_array.push('<div id="library_folder_toolbar" >');
+ tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-icon-plus"></span><span class="fa fa-icon-folder-close"></span> folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-icon-external-link"></span> to history</button>');
+
+ tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' <span class="fa fa-icon-download"></span> download <span class="caret"></span>');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
+ tmpl_array.push(' </ul>');
+ tmpl_array.push(' </div>');
+
+ tmpl_array.push('</div>');
+
+ // BREADCRUMBS
+ tmpl_array.push('<div class="library_breadcrumb">');
+ tmpl_array.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<span title="You are in this folder"><%- path_item[1] %></span>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('</div>');
+
+ // FOLDER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th class="button_heading">view</th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>data type</th>');
+ tmpl_array.push(' <th>size</th>');
+ tmpl_array.push(' <th>date</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td>folder</td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>'); // size
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <td>');
+ tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-eye-open"></span> details');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
+ tmpl_array.push(' <td><%= _.escape(content_item.get("data_type")) %></td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>'); // size
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>'); // time updated
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+ templateDatasetModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div id="dataset_info_modal">');
+ tmpl_array.push(' <table class="table table-striped table-condensed">');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Data type</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("data_type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Genome build</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("genome_build")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Size</th>');
+ tmpl_array.push(' <td><%= _.escape(size) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Date uploaded</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Uploaded by</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr scope="row">');
+ tmpl_array.push(' <th scope="row">Data Lines</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Comment Lines</th>');
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Number of Columns</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Column Types</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' </table>');
+ tmpl_array.push(' <pre class="peek">');
+ tmpl_array.push(' </pre>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ },
+
+ templateHistorySelectInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ templateBulkImportInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ // convert size to nice string
+ size_to_string : function (size)
+ {
+ // identify unit
+ var unit = "";
+ if (size >= 100000000000) { size = size / 100000000000; unit = "TB"; } else
+ if (size >= 100000000) { size = size / 100000000; unit = "GB"; } else
+ if (size >= 100000) { size = size / 100000; unit = "MB"; } else
+ if (size >= 100) { size = size / 100; unit = "KB"; } else
+ { size = size * 10; unit = "b"; }
+ // return formatted string
+ return (Math.round(size) / 10) + unit;
+ },
+
+// MMMMMMMMMMMMMMM
+// === EVENTS ====
+// MMMMMMMMMMMMMMM
+
+ // event binding
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked',
+ 'click #toolbtn_bulk_import' : 'modalBulkImport',
+ 'click #toolbtn_dl' : 'bulkDownload',
+ 'click .library-dataset' : 'showDatasetDetails',
+ 'click #toolbtn_create_folder' : 'createFolderModal',
+ 'click .btn_open_folder' : 'navigateToFolder'
+ },
+
+ //render the folder view
+ render: function (options) {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+
+ view = this;
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+
+ folderContainer.fetch({
+ success: function (container) {
+
+ // prepare nice size strings
+ for (var i = 0; i < folderContainer.attributes.folder.models.length; i++) {
+ var model = folderContainer.attributes.folder.models[i]
+ if (model.get('type') === 'file'){
+ model.set('readable_size', that.size_to_string(model.get('file_size')))
+ }
+ };
+
+ // find the upper id
+ var path = folderContainer.full_path;
+ var upper_folder_id;
+ if (path.length === 1){ // library is above us
+ upper_folder_id = 0;
+ } else {
+ upper_folder_id = path[path.length-2][0];
+ }
+
+ var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
+ // var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // handles the click on 'open' and 'upper' folder icons
+ navigateToFolder : function(event){
+ var folder_id = $(event.target).attr('data-id');
+ if (typeof folder_id === 'undefined') {
+ return false;
+ } else if (folder_id === '0'){
+ library_router.navigate('#', {trigger: true, replace: true});
+ } else {
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ }
+ },
+
+ //show modal with current dataset info
+ showDatasetDetails : function(event){
+ // prevent default
+ event.preventDefault();
+
+//TODO check whether we already have the data
+
+ //load the ID of the row
+ var id = $(event.target).parent().parent().attr('id');
+
+ //create new item
+ var item = new Item();
+ var histories = new GalaxyHistories();
+ item.id = id;
+ var self = this;
+
+ //fetch the dataset info
+ item.fetch({
+ success: function (item) {
+// TODO can render here already
+ //fetch user histories for import purposes
+ histories.fetch({
+ success: function (histories){self.renderModalAfterFetch(item, histories)}
+ });
+ }
+ });
+ },
+
+ // show the current dataset in a modal
+ 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({
+ 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
+ }
+ });
+ $(".peek").html(item.get("peek"));
+ var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+
+ // preset last selected history if we know it
+ if (self.lastSelectedHistory.length > 0) {
+ $(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
+ }
+
+ // show the prepared modal
+ this.modal.show();
+ },
+
+ // download dataset shown currently in modal
+ downloadCurrent : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var library_dataset_id = [];
+ library_dataset_id.push($('#id_row').attr('data-id'));
+ var url = '/api/libraries/datasets/download/uncompressed';
+ var data = {'ldda_ids' : library_dataset_id};
+
+ // we assume the view is existent
+ folderContentView.processDownload(url, data);
+ this.modal.enableButton('Import');
+ this.modal.enableButton('Download');
+ },
+
+ // import dataset shown currently in modal into selected history
+ importCurrentIntoHistory : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var history_id = $(this.modal.elMain).find('select[name=dataset_import_single] option:selected').val();
+ this.lastSelectedHistory = history_id; //save selected history for further use
+
+ var library_dataset_id = $('#id_row').attr('data-id');
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+
+ // save the dataset into selected history
+ historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
+ self.modal.showNotification('Dataset imported', 3000, 'success');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }, error : function(){
+ self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }
+ });
+ },
+
+ // select all datasets
+ selectAll : function (event) {
+ var selected = event.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ this.showTools();
+ },
+
+ // click checkbox on folder click
+ selectClicked : function (event) {
+ var checkbox = $("#" + event.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ // $(event.target.parentElement).css('background-color', '').css('color', '');
+ $(event.target.parentElement).removeClass('dark');
+ $(event.target.parentElement).find('a').removeClass('dark');
+ $(event.target.parentElement).addClass('light');
+ $(event.target.parentElement).find('a').addClass('light');
+ } else {
+ checkbox[0].checked = 'selected';
+ $(event.target.parentElement).removeClass('light');
+ $(event.target.parentElement).find('a').removeClass('light');
+ $(event.target.parentElement).addClass('dark');
+ $(event.target.parentElement).find('a').addClass('dark');
+ // $(event.target.parentElement).css('background-color', '#8389a1').css('color', 'white');
+ }
+ }
+ this.showTools();
+ },
+
+ // show toolbar in case something is selected
+ showTools : function(){
+ var checkedValues = $('#folder_table').find(':checked');
+ if(checkedValues.length > 0){
+ $('#toolbtn_bulk_import').show();
+ $('#toolbtn_dl').show();
+ } else {
+ $('#toolbtn_bulk_import').hide();
+ $('#toolbtn_dl').hide();
+ }
+
+ },
+
+ // show bulk import modal
+ modalBulkImport : function(){
+ var self = this;
+ // fetch histories
+ var histories = new GalaxyHistories();
+ histories.fetch({
+ success: function (histories){
+ // make modal
+ var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
+ self.modal = new mod_modal.GalaxyModal({
+ title : 'Import into History',
+ body : history_modal_tmpl,
+ buttons : {
+ 'Import' : function() {self.importAllIntoHistory()},
+ 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
+ }
+ });
+ // show the prepared modal
+ self.modal.show();
+ }
+ });
+ },
+
+ // import all selected datasets into history
+ importAllIntoHistory : function (){
+ //disable the button
+ this.modal.disableButton('Import');
+
+ var history_id = $("select[name=dataset_import_bulk] option:selected").val();
+ var history_name = $("select[name=dataset_import_bulk] option:selected").text();
+
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+ var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
+ $(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
+
+ // init the progress bar
+ var progressStep = 100 / dataset_ids.length;
+ this.initProgress(progressStep);
+
+ // prepare the dataset objects to be imported
+ var datasets_to_import = [];
+ for (var i = dataset_ids.length - 1; i >= 0; i--) {
+ library_dataset_id = dataset_ids[i];
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ historyItem.content = library_dataset_id;
+ historyItem.source = 'library';
+ datasets_to_import.push(historyItem);
+ };
+
+ // call the recursive function to call ajax one after each other
+ this.chainCall(datasets_to_import);
+ },
+
+ chainCall: function(history_item_set){
+ var self = this;
+ var popped_item = history_item_set.pop();
+ if (typeof popped_item === "undefined") {
+ self.modal.showNotification('All datasets imported', 3000, 'success');
+ // enable button again
+ self.modal.enableButton('Import');
+ return
+ }
+ var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
+ self.updateProgress();
+ responses.push(a1);
+ self.chainCall(history_item_set);
+ });
+ },
+
+ initProgress: function(progressStep){
+ this.progress = 0;
+ this.progressStep = progressStep;
+ },
+ updateProgress: function(){
+ this.progress += this.progressStep;
+ $('.progress-bar').width(Math.round(this.progress) + '%');
+ txt_representation = Math.round(this.progress) + '% Complete';
+ $('.completion_span').text(txt_representation);
+ },
+
+ // progress bar
+ templateProgressBar : function (){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div class="import_text">');
+ tmpl_array.push('Importing selected datasets to history <b><%= _.escape(history_name) %></b>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('<div class="progress">');
+ tmpl_array.push(' <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');
+ tmpl_array.push(' <span class="completion_span">0% Complete</span>');
+ tmpl_array.push(' </div>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+
+ // download selected datasets
+ download : function(folder_id, format){
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+
+ var url = '/api/libraries/datasets/download/' + format;
+ var data = {'ldda_ids' : dataset_ids};
+ this.processDownload(url, data);
+ },
+
+ // create hidden form and submit through POST to initialize download
+ processDownload: function(url, data, method){
+ //url and data options required
+ if( url && data ){
+ //data can be string of parameters or array/object
+ data = typeof data == 'string' ? data : $.param(data);
+ //split params into form inputs
+ var inputs = '';
+ $.each(data.split('&'), function(){
+ var pair = this.split('=');
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ });
+ //send request
+ $('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
+ .appendTo('body').submit().remove();
+ };
+ },
+
+ // shows modal for creating folder
+ createFolderModal: function(){
+ alert('creating folder');
+ }
+
+ });
+
+// galaxy library view
+var GalaxyLibraryview = Backbone.View.extend({
+ el: '#center',
+
+ events: {
+ 'click #create_new_library_btn' : 'show_library_modal'
+ },
+
+ // initialize
+ initialize : function(){
+ },
+
+ // template
+ 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 icon-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-icon-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
+ render: function () {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+ var that = this;
+ // if (typeof libraries === "undefined") {
+ libraries = new Libraries();
+ // }
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template(that.template_library_list(), { libraries : libraries.models });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // own modal
+ modal : null,
+
+ // show/hide create library modal
+ show_library_modal : function (event){
+ event.preventDefault();
+ event.stopPropagation();
+
+ // create modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal(
+ {
+ title : 'Create New Library',
+ body : this.template_new_library(),
+ buttons : {
+ 'Create' : function() {self.create_new_library_event()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+
+ // show prepared modal
+ this.modal.show();
+ },
+
+ // create the new library from modal
+ create_new_library_event: function(){
+ var libraryDetails = this.serialize_new_library();
+ var valid = this.validate_new_library(libraryDetails);
+ var library = new Library();
+ var self = this;
+ library.save(libraryDetails, {
+ success: function (library) {
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ },
+ error: function(){
+ self.modal.showNotification('An error occured', 5000, 'error');
+ }
+ });
+ return false;
+ },
+
+ // clear the library modal once saved
+ clear_library_modal : function(){
+ $("input[name='Name']").val('');
+ $("input[name='Description']").val('');
+ $("input[name='Synopsis']").val('');
+ },
+
+ // serialize data from the form
+ serialize_new_library : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val(),
+ synopsis: $("input[name='Synopsis']").val()
+ };
+ },
+
+ validate_new_library: function(library){
+
+ },
+
+
+ // template for new library modal
+ 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('');
+ }
+});
+
+// galaxy library wrapper View
+var GalaxyLibrary = Backbone.View.extend({
+ folderContentView : null,
+ galaxyLibraryview : null,
+ initialize : function(){
+
+ folderContentView = new FolderContentView();
+ galaxyLibraryview = new GalaxyLibraryview();
+
+ library_router = new LibraryRouter();
+
+ library_router.on('route:libraries', function() {
+ // render libraries list
+ galaxyLibraryview.render();
+ });
+
+ library_router.on('route:folder_content', function(id) {
+ // render folder's contents
+ folderContentView.render({id: id});
+ });
+
+ library_router.on('route:download', function(folder_id, format) {
+ // send download stream
+ if (typeof folderContentView === 'undefined'){
+ alert('you cant touch this!');
+ // } else if (folderContentView.modal !== null){
+ // folderContentView.download(folder_id, format);
+ } else if ($('#center').find(':checked').length === 0) { // coming from outside of the library app
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ // TODO show message of redirection
+ } else {
+ folderContentView.download(folder_id, format);
+ library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
+ }
+
+ });
+
+Backbone.history.start();
+
+return this
+}
+});
+
+// return
+return {
+ GalaxyApp: GalaxyLibrary
+};
+
+});
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/scripts/galaxy.library.router.js
--- /dev/null
+++ b/static/scripts/galaxy.library.router.js
@@ -0,0 +1,21 @@
+define( [], function() {
+
+/**
+ * -- Routers --
+ */
+
+/**
+ * Router for library browser.
+ */
+var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content"
+ }
+});
+
+return {
+ LibraryRouter: LibraryRouter,
+};
+
+})
\ No newline at end of file
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -58,6 +58,12 @@
tab_shared.add({
title : "Data Libraries",
content : "library/index",
+ content : "library/index"
+ });
+
+ tab_shared.add({
+ title : "New Libraries",
+ content : "library/list",
divider : true
});
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -1,6 +1,3 @@
-/*
- galaxy modal
-*/
// dependencies
define([], function() {
@@ -25,8 +22,51 @@
// initialize
initialize : function(options) {
+ self = this;
if (options)
this.create(options);
+
+ this.bindClick(event, self);
+ },
+
+ // bind the click-to-hide function
+ bindClick: function(event, that) {
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the click anywhere to hide() function...
+ $('html').on('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // unbind the click-to-hide function
+ unbindClick: function(event, that){
+ // bind the ESC key to hide() function
+ $(document).off('keyup', function(event){
+ if (event.keyCode == 27) { that.hide(); }
+ })
+ // unbind the click anywhere to hide() function...
+ $('html').off('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').off('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+
+ // destroy
+ destroy : function(){
+ this.hide();
+ this.unbindClick();
+ $('.modal').remove();
},
// adds and displays a new frame/window
@@ -87,6 +127,7 @@
this.$footer = (this.$el).find('.modal-footer');
this.$buttons = (this.$el).find('.buttons');
this.$backdrop = (this.$el).find('.modal-backdrop');
+ this.$notification = (this.$el).find('.notification-modal');
// append body
this.$body.html(this.options.body);
@@ -120,6 +161,47 @@
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', true);
},
+ // hide buttons
+ hideButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).hide();
+ },
+ // show buttons
+ showButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).show();
+ },
+
+ // show notification
+ showNotification : function(message, duration, type) {
+ // defaults
+ var duration = typeof duration !== 'undefined' ? duration : 1500;
+ // var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
+ // var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ var bgColor;
+ var txtColor;
+
+ if (type === 'error'){
+ bgColor = '#f4e0e1';
+ txtColor = '#a42732';
+ // } else if (type === 'success'){
+ } else { // success is default
+ bgColor = '#e1f4e0';
+ txtColor = '#32a427';
+ }
+
+ var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
+ this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
+
+ var self = this;
+
+ /*animate the bar*/
+ $('#notification-bar').slideDown(function() {
+ setTimeout(function() {
+ $('#notification-bar').slideUp(function() {self.$notification.html('');});
+ }, duration);
+ });
+
+ },
+
// returns scroll top for body element
scrollTop: function()
{
@@ -135,10 +217,10 @@
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">' + title + '</h4>' +
+ '<span class="notification-modal"></span>' +
'</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/scripts/libs/bootstrap.js
--- a/static/scripts/libs/bootstrap.js
+++ b/static/scripts/libs/bootstrap.js
@@ -575,3 +575,158 @@
}
}(window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#dropdowns
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ var $el = $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ $parent.trigger(e = $.Event('show.bs.dropdown'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown')
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index=0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus() {
+ $(backdrop).remove()
+ $(toggle).each(function (e) {
+ var $parent = getParent($(this))
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown'))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('dropdown')
+
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
\ No newline at end of file
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster' ];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'new_library', 'trackster' ];
var _ = grunt.util._;
var fmt = _.sprintf;
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/style/src/less/library.less
--- a/static/style/src/less/library.less
+++ b/static/style/src/less/library.less
@@ -29,6 +29,50 @@
background-color: @table-bg-accent;
}
+
+tr.light td
+{
+ background-color: white;
+ color: black;
+}
+tr.light:hover td
+{
+ background-color: #f5f5f5;
+ color: #8389a1;
+}
+tr.dark td
+{
+ background-color: #8389a1;
+ color: white;
+}
+tr.dark:hover td
+{
+ background-color: #bbbfd0;
+ color: white;
+}
+a.dark:hover
+{
+ color: yellow;
+ // text-decoration: none;
+}
+a.dark
+{
+ color: white;
+ // text-decoration: none;
+}
+th.button_heading
+{
+ width: 7em;
+}
+div.library_breadcrumb{
+ padding-top: 0.8em;
+ padding-bottom: 0.8em;
+}
+div.library_breadcrumb a:hover{
+ color:green;
+}
+
+
img.expanderIcon {
padding-right: 4px;
}
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 templates/base/base_panels.mako
--- a/templates/base/base_panels.mako
+++ b/templates/base/base_panels.mako
@@ -357,4 +357,4 @@
## Scripts can be loaded later since they progressively add features to
## the panels, but do not change layout
${self.late_javascripts()}
-</html>
+</html>
\ No newline at end of file
https://bitbucket.org/galaxy/galaxy-central/commits/fb3161b21a4b/
Changeset: fb3161b21a4b
User: martenson
Date: 2013-12-11 00:12:14
Summary: aiding the merge of data_library branch
Affected #: 5 files
diff -r e2b8d3481d668203bfad7efe306925c72c644839 -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -114,8 +114,4 @@
*.orig
.DS_Store
*.rej
-*~
-
-
-syntax: regexp
-^static/AAA_scratch$
\ No newline at end of file
+*~
\ No newline at end of file
diff -r e2b8d3481d668203bfad7efe306925c72c644839 -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 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
@@ -100,8 +100,6 @@
encoded_id = trans.security.encode_id( content_item.id )
time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
-# log.debug('XXXXXXXXXXXXXXXXXXX api type: ' + str(content_item.api_type))
-# log.debug('XXXXXXXX ALL: ' + str(content_item.__dict__))
# For folder return also hierarchy values
if content_item.api_type == 'folder':
@@ -110,7 +108,6 @@
return_item.update ( dict ( item_count = content_item.item_count ) )
if content_item.api_type == 'file':
-# log.debug('XXXXX content item class: ' + str(content_item.__class__))
library_dataset_dict = content_item.to_dict()
library_dataset_dict['data_type']
library_dataset_dict['file_size']
diff -r e2b8d3481d668203bfad7efe306925c72c644839 -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 static/scripts/galaxy.library.router.js
--- a/static/scripts/galaxy.library.router.js
+++ /dev/null
@@ -1,21 +0,0 @@
-define( [], function() {
-
-/**
- * -- Routers --
- */
-
-/**
- * Router for library browser.
- */
-var LibraryRouter = Backbone.Router.extend({
- routes: {
- "" : "libraries",
- "folders/:id" : "folder_content"
- }
-});
-
-return {
- LibraryRouter: LibraryRouter,
-};
-
-})
\ No newline at end of file
diff -r e2b8d3481d668203bfad7efe306925c72c644839 -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -57,7 +57,6 @@
tab_shared.add({
title : "Data Libraries",
- content : "library/index",
content : "library/index"
});
diff -r e2b8d3481d668203bfad7efe306925c72c644839 -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'new_library', 'trackster' ];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster' ];
var _ = grunt.util._;
var fmt = _.sprintf;
https://bitbucket.org/galaxy/galaxy-central/commits/96f52c9fd562/
Changeset: 96f52c9fd562
User: martenson
Date: 2013-12-11 00:21:06
Summary: Merged galaxy/galaxy-central into default
Affected #: 3 files
diff -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 -r 96f52c9fd562fe160fc8ac71aa6cddd902d23e0f 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>
diff -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 -r 96f52c9fd562fe160fc8ac71aa6cddd902d23e0f lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -474,6 +474,9 @@
"""Returns history data in the form of a dictionary.
"""
history_dict = history.to_dict( view='element', value_mapper={ 'id':trans.security.encode_id })
+ history_dict[ 'user_id' ] = None
+ if history.user_id:
+ history_dict[ 'user_id' ] = trans.security.encode_id( history.user_id )
history_dict[ 'nice_size' ] = history.get_disk_size( nice_size=True )
history_dict[ 'annotation' ] = history.get_item_annotation_str( trans.sa_session, trans.user, history )
diff -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 -r 96f52c9fd562fe160fc8ac71aa6cddd902d23e0f lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
@@ -93,7 +93,7 @@
env_file_builder.append_line( action="source", value=required_tool_dependency_env_file_path )
return_code = env_file_builder.return_code
if return_code:
- error_message = 'Error defining env.sh file for package %s, return_code: %s' % ( str( package_name, str( return_code ) ) )
+ error_message = 'Error defining env.sh file for package %s, return_code: %s' % ( str( package_name ), str( return_code ) )
tool_dependency = tool_dependency_util.handle_tool_dependency_installation_error( app,
tool_dependency,
error_message,
https://bitbucket.org/galaxy/galaxy-central/commits/42e74befc796/
Changeset: 42e74befc796
User: martenson
Date: 2013-12-11 00:29:50
Summary: aid after merge data_library branch
Affected #: 1 file
diff -r 96f52c9fd562fe160fc8ac71aa6cddd902d23e0f -r 42e74befc7962afb93baba6197273565755e086c lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -73,6 +73,8 @@
webapp.add_route( '/datasets/:dataset_id/display/{filename:.+?}', controller='dataset', action='display', dataset_id=None, filename=None)
webapp.add_route( '/datasets/:dataset_id/:action/:filename', controller='dataset', action='index', dataset_id=None, filename=None)
webapp.add_route( '/display_application/:dataset_id/:app_name/:link_name/:user_id/:app_action/:action_param',
+ controller='dataset', action='display_application', dataset_id=None, user_id=None,
+ app_name = None, link_name = None, app_action = None, action_param = None )
webapp.add_route( '/u/:username/d/:slug/:filename', controller='dataset', action='display_by_username_and_slug', filename=None )
webapp.add_route( '/u/:username/p/:slug', controller='page', action='display_by_username_and_slug' )
webapp.add_route( '/u/:username/h/:slug', controller='history', action='display_by_username_and_slug' )
https://bitbucket.org/galaxy/galaxy-central/commits/8fcfddb3e1b5/
Changeset: 8fcfddb3e1b5
User: martenson
Date: 2013-12-11 16:41:39
Summary: aiding problematic merging of data_library branch, removing duplicates, removing API v2 relicts
Affected #: 3 files
diff -r 42e74befc7962afb93baba6197273565755e086c -r 8fcfddb3e1b57ad627ebab3619920552a6e967ff .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -114,4 +114,5 @@
*.orig
.DS_Store
*.rej
-*~
\ No newline at end of file
+*~
+
diff -r 42e74befc7962afb93baba6197273565755e086c -r 8fcfddb3e1b57ad627ebab3619920552a6e967ff lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,7 +46,6 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Add the controllers folder
# Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
controller='admin_toolshed',
@@ -54,17 +53,11 @@
repository_id=None,
image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
- # Add the api folder
- webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # Add the api folder VERSION 2
- webapp.add_api_controllers( 'galaxy.webapps.galaxy.api.v2', app )
-
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
# Force /activate to go to the controller
webapp.add_route( '/activate', controller='user', action='activate' )
# These two routes handle our simple needs at the moment
-
webapp.add_route( '/async/:tool_id/:data_id/:data_secret', controller='async', action='index', tool_id=None, data_id=None, data_secret=None )
webapp.add_route( '/:controller/:action', action='index' )
webapp.add_route( '/:action', controller='root', action='index' )
@@ -73,8 +66,8 @@
webapp.add_route( '/datasets/:dataset_id/display/{filename:.+?}', controller='dataset', action='display', dataset_id=None, filename=None)
webapp.add_route( '/datasets/:dataset_id/:action/:filename', controller='dataset', action='index', dataset_id=None, filename=None)
webapp.add_route( '/display_application/:dataset_id/:app_name/:link_name/:user_id/:app_action/:action_param',
- controller='dataset', action='display_application', dataset_id=None, user_id=None,
- app_name = None, link_name = None, app_action = None, action_param = None )
+ controller='dataset', action='display_application', dataset_id=None, user_id=None,
+ app_name = None, link_name = None, app_action = None, action_param = None )
webapp.add_route( '/u/:username/d/:slug/:filename', controller='dataset', action='display_by_username_and_slug', filename=None )
webapp.add_route( '/u/:username/p/:slug', controller='page', action='display_by_username_and_slug' )
webapp.add_route( '/u/:username/h/:slug', controller='history', action='display_by_username_and_slug' )
@@ -82,22 +75,12 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
webapp.add_route( '/search', controller='search', action='index' )
- # Add the web API
+ # ================
+ # ===== API =====
+ # ================
+
webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
@@ -109,10 +92,6 @@
controller="datasets",
action="display",
conditions=dict(method=["GET"]))
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'user',
'users',
controller='group_users',
@@ -134,11 +113,6 @@
_add_item_tags_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
-
_add_item_annotation_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -148,7 +122,6 @@
_add_item_annotation_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
_add_item_provenance_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -178,13 +151,12 @@
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' )
-
# =======================
# ===== LIBRARY API =====
# =======================
+
+ # The /folders section is experimental at this point:
+ log.debug( "app.config.api_folders: %s" % app.config.api_folders )
webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
@@ -232,6 +204,9 @@
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+ # ====================
+ # ===== TOOLSHED =====
+ # ====================
# Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
@@ -239,7 +214,7 @@
action='display_image_in_repository',
repository_id=None,
image_file=None )
-
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
@@ -253,6 +228,7 @@
path_prefix='/api',
new={ 'install_repository_revision' : 'POST' },
parent_resources=dict( member_name='tool_shed_repository', collection_name='tool_shed_repositories' ) )
+
# Connect logger from app
if app.trace_logger:
webapp.trace_logger = app.trace_logger
@@ -273,11 +249,6 @@
galaxy.model.mapping.metadata.engine.connection_provider._pool.dispose()
except:
pass
- # Close any pooled database connections before forking
- try:
- galaxy.model.tool_shed_install.mapping.metadata.engine.connection_provider._pool.dispose()
- except:
- pass
# Return
return webapp
https://bitbucket.org/galaxy/galaxy-central/commits/4506583cb1bc/
Changeset: 4506583cb1bc
User: martenson
Date: 2013-12-11 16:51:21
Summary: aiding merg problems with data_library branch
Affected #: 1 file
diff -r 8fcfddb3e1b57ad627ebab3619920552a6e967ff -r 4506583cb1bc66db01d98c1619b6829fcda31e00 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,12 +46,6 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
- webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
- controller='admin_toolshed',
- action='display_image_in_repository',
- repository_id=None,
- image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
@@ -187,7 +181,6 @@
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
- webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current", controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
webapp.mapper.connect( "create_api_key", "/api/users/:user_id/api_key",
controller="users", action="api_key", user_id=None, conditions=dict( method=["POST"] ) )
@@ -195,7 +188,6 @@
# visualizations registry generic template renderer
webapp.add_route( '/visualization/show/:visualization_name',
controller='visualization', action='render', visualization_name=None )
- webapp.add_route( '/visualization/show/:visualization_name', controller='visualization', action='render', visualization_name=None )
# "POST /api/workflows/import" => ``workflows.import_workflow()``.
# Defines a named route "import_workflow".
https://bitbucket.org/galaxy/galaxy-central/commits/2fb8dfdb5b19/
Changeset: 2fb8dfdb5b19
User: martenson
Date: 2013-12-11 16:53:42
Summary: aiding merg problems with data_library branch
Affected #: 1 file
diff -r 4506583cb1bc66db01d98c1619b6829fcda31e00 -r 2fb8dfdb5b19378a5bc61ee713aa4da3f9767a91 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -241,6 +241,11 @@
galaxy.model.mapping.metadata.engine.connection_provider._pool.dispose()
except:
pass
+ # Close any pooled database connections before forking
+ try:
+ galaxy.model.tool_shed_install.mapping.metadata.engine.connection_provider._pool.dispose()
+ except:
+ pass
# Return
return webapp
https://bitbucket.org/galaxy/galaxy-central/commits/78c39d32ac09/
Changeset: 78c39d32ac09
User: martenson
Date: 2013-12-11 17:01:45
Summary: aiding merg problems with data_library branch
Affected #: 1 file
diff -r 2fb8dfdb5b19378a5bc61ee713aa4da3f9767a91 -r 78c39d32ac09a796966cc192636f4f8b1ba1bc07 templates/base/base_panels.mako
--- a/templates/base/base_panels.mako
+++ b/templates/base/base_panels.mako
@@ -357,4 +357,4 @@
## Scripts can be loaded later since they progressively add features to
## the panels, but do not change layout
${self.late_javascripts()}
-</html>
\ No newline at end of file
+</html>
https://bitbucket.org/galaxy/galaxy-central/commits/8528640574a5/
Changeset: 8528640574a5
User: martenson
Date: 2013-12-11 17:08:59
Summary: aiding merg problems with data_library branch
Affected #: 1 file
diff -r 78c39d32ac09a796966cc192636f4f8b1ba1bc07 -r 8528640574a5b981ffb0940477abb0bd65ea2885 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -217,11 +217,12 @@
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">' + title + '</h4>' +
'<span class="notification-modal"></span>' +
- '</div>' +
+ '</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
'<div class="buttons" style="float: right;"></div>' +
https://bitbucket.org/galaxy/galaxy-central/commits/181b293ea659/
Changeset: 181b293ea659
User: martenson
Date: 2013-12-11 17:19:42
Summary: Merge with second head of ‘default’ branch
Affected #: 17 files
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1827,7 +1827,7 @@
class Library( object, Dictifiable ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
dict_collection_visible_keys = ( 'id', 'name' )
- dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
+ dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id' )
def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
@@ -1894,7 +1894,7 @@
return name
class LibraryFolder( object, Dictifiable ):
- dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
+ dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time' )
def __init__( self, name=None, description=None, item_count=0, order_id=None ):
self.name = name or "Unnamed folder"
self.description = description
@@ -2060,6 +2060,7 @@
genome_build = ldda.dbkey,
misc_info = ldda.info,
misc_blurb = ldda.blurb,
+ peek = ( lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None )( ldda ),
template_data = template_data )
if ldda.dataset.uuid is None:
rval['uuid'] = None
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1878,8 +1878,9 @@
table = self.table
trans = conn.begin()
try:
- next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
- table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
+ current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
+ next_hid = current_hid + 1
+ table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
trans.commit()
return next_hid
except:
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/webapps/galaxy/api/datasets.py
--- a/lib/galaxy/webapps/galaxy/api/datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a dataset.
+API operations on the contents of a history dataset.
"""
from galaxy import web
from galaxy.visualization.data_providers.genome import FeatureLocationIndexDataProvider
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 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
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a library.
+API operations on the contents of a folder.
"""
import logging, os, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
@@ -11,67 +11,122 @@
log = logging.getLogger( __name__ )
class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ):
+ """
+ Class controls retrieval, creation and updating of folder contents.
+ """
+
+ def load_folder_contents( self, trans, folder ):
+ """
+ Loads all contents of the folder (folders and data sets) but only in the first level.
+ """
+ current_user_roles = trans.get_current_user_roles()
+ is_admin = trans.user_is_admin()
+ content_items = []
+ for subfolder in folder.active_folders:
+ if not is_admin:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
+ if (is_admin or can_access) and not subfolder.deleted:
+ subfolder.api_type = 'folder'
+ content_items.append( subfolder )
+ for dataset in folder.datasets:
+ if not is_admin:
+ can_access = trans.app.security_agent.can_access_dataset( current_user_roles, dataset.library_dataset_dataset_association.dataset )
+ if (is_admin or can_access) and not dataset.deleted:
+ dataset.api_type = 'file'
+ content_items.append( dataset )
+ return content_items
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
Displays a collection (list) of a folder's contents (files and folders).
- The /api/library_contents/{encoded_library_id}/contents
- lists everything in a library recursively, which is not what
- we want here. We could add a parameter to use the recursive
- style, but this is meant to act similar to an "ls" directory listing.
+ Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
+ Full path is provided as a separate object in response providing data for breadcrumb path building.
"""
- rval = []
+ folder_container = []
current_user_roles = trans.get_current_user_roles()
- def traverse( folder ):
- admin = trans.user_is_admin()
- rval = []
- for subfolder in folder.active_folders:
- if not admin:
- can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
- if (admin or can_access) and not subfolder.deleted:
- subfolder.api_type = 'folder'
- rval.append( subfolder )
- for ld in folder.datasets:
- if not admin:
- can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ld.library_dataset_dataset_association.dataset )
- if (admin or can_access) and not ld.deleted:
- ld.api_type = 'file'
- rval.append( ld )
- return rval
-
- try:
- decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
- except TypeError:
- trans.response.status = 400
- return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+ if ( folder_id.startswith( 'F' ) ):
+ try:
+ decoded_folder_id = trans.security.decode_id( folder_id[1:] )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
- parent_library = folder.parent_library
except:
folder = None
- log.error( "FolderContentsController.index: Unable to retrieve folder %s"
- % folder_id )
+ log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
- # TODO: Find the API's path to this folder if necessary.
- # This was needed in recursive descent, but it's not needed
- # for "ls"-style content checking:
- if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ # We didn't find the folder or user does not have an access to it.
+ if not folder:
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, folder.id ) )
+ trans.response.status = 400
+ return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ path_to_root = []
+ def build_path ( folder ):
+ """
+ Search the path upwards recursively and load the whole route of names and ids for breadcrumb purposes.
+ """
+ path_to_root = []
+ # We are almost in root
+ if folder.parent_id is None:
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ else:
+ # We add the current folder and traverse up one folder.
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
+ path_to_root.extend( build_path( upper_folder ) )
+ return path_to_root
+
+ # Return the reversed path so it starts with the library node.
+ full_path = build_path( folder )[::-1]
+ folder_container.append( dict( full_path = full_path ) )
+
+ folder_contents = []
+ time_updated = ''
+ time_created = ''
+ # Go through every item in the folder and include its meta-data.
+ for content_item in self.load_folder_contents( trans, folder ):
+# rval = content_item.to_dict()
+ return_item = {}
+ encoded_id = trans.security.encode_id( content_item.id )
+ time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
+
+ # For folder return also hierarchy values
+ if content_item.api_type == 'folder':
+ encoded_id = 'F' + encoded_id
+# time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count ) )
- for content in traverse( folder ):
- encoded_id = trans.security.encode_id( content.id )
- if content.api_type == 'folder':
- encoded_id = 'F' + encoded_id
- rval.append( dict( id = encoded_id,
- type = content.api_type,
- name = content.name,
- url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
- return rval
+ if content_item.api_type == 'file':
+ library_dataset_dict = content_item.to_dict()
+ library_dataset_dict['data_type']
+ library_dataset_dict['file_size']
+ library_dataset_dict['date_uploaded']
+ return_item.update ( dict ( data_type = library_dataset_dict['data_type'],
+ file_size = library_dataset_dict['file_size'],
+ date_uploaded = library_dataset_dict['date_uploaded'] ) )
+
+ # For every item return also the default meta-data
+ return_item.update( dict( id = encoded_id,
+ type = content_item.api_type,
+ name = content_item.name,
+ time_updated = time_updated,
+ time_created = time_created
+ ) )
+ folder_contents.append( return_item )
+ # Put the data in the container
+ folder_container.append( dict( folder_contents = folder_contents ) )
+ return folder_container
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -0,0 +1,241 @@
+"""
+API operations on the dataset from library.
+"""
+import glob
+import logging
+import operator
+import os
+import os.path
+import string
+import sys
+import tarfile
+import tempfile
+import urllib
+import urllib2
+import zipfile
+from galaxy.security import Action
+from galaxy import util, web
+from galaxy.util.streamball import StreamBall
+from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
+
+import logging
+log = logging.getLogger( __name__ )
+
+# Test for available compression types
+# tmpd = tempfile.mkdtemp()
+# comptypes = []
+# for comptype in ( 'gz', 'bz2' ):
+# tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype )
+# try:
+# archive = tarfile.open( tmpf, 'w:' + comptype )
+# archive.close()
+# comptypes.append( comptype )
+# except tarfile.CompressionError:
+# log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+ziptype = '32'
+# tmpf = os.path.join( tmpd, 'compression_test.zip' )
+# try:
+# archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+# archive.close()
+# comptypes.append( 'zip' )
+# ziptype = '64'
+# except RuntimeError:
+# log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." )
+# except (TypeError, zipfile.LargeZipFile):
+# # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped
+# log.warning( 'Max zip file size is 2GB, ZIP64 not supported' )
+# comptypes.append( 'zip' )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+# os.rmdir( tmpd )
+
+
+
+class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
+
+ @web.expose_api
+ def show( self, trans, id, **kwd ):
+ """
+ GET /api/libraries/datasets/{encoded_dataset_id}
+ Displays information about the dataset identified by the lda ID.
+ """
+ # Get dataset.
+ try:
+ dataset = self.get_library_dataset( trans, id = id )
+ except Exception, e:
+ return str( e )
+ try:
+ # Default: return dataset as dict.
+ rval = dataset.to_dict()
+ except Exception, e:
+ rval = "Error in dataset API at listing contents: " + str( e )
+ log.error( rval + ": %s" % str(e), exc_info=True )
+ trans.response.status = 500
+
+ rval['id'] = trans.security.encode_id(rval['id']);
+ rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
+ rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
+
+ return rval
+
+ @web.expose
+ def download( self, trans, format, **kwd ):
+ """
+ POST /api/libraries/datasets/download/{format}
+ POST data: ldda_ids = []
+ Downloads dataset(s) in the requested format.
+ """
+ lddas = []
+# is_admin = trans.user_is_admin()
+# current_user_roles = trans.get_current_user_roles()
+
+ datasets_to_download = kwd['ldda_ids%5B%5D']
+
+ if ( datasets_to_download != None ):
+ datasets_to_download = util.listify( datasets_to_download )
+ for dataset_id in datasets_to_download:
+ try:
+ ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ assert not ldda.dataset.purged
+ lddas.append( ldda )
+ except:
+ ldda = None
+ message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
+
+ if format in [ 'zip','tgz','tbz' ]:
+ error = False
+ killme = string.punctuation + string.whitespace
+ trantab = string.maketrans(killme,'_'*len(killme))
+ try:
+ outext = 'zip'
+ if format == 'zip':
+ # Can't use mkstemp - the file must not exist first
+ tmpd = tempfile.mkdtemp()
+ util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
+ tmpf = os.path.join( tmpd, 'library_download.' + format )
+ if ziptype == '64' and trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
+ elif ziptype == '64':
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+ elif trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED )
+ else:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED )
+ archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
+ elif format == 'tgz':
+ if trans.app.config.upstream_gzip:
+ archive = StreamBall( 'w|' )
+ outext = 'tar'
+ else:
+ archive = StreamBall( 'w|gz' )
+ outext = 'tgz'
+ elif format == 'tbz':
+ archive = StreamBall( 'w|bz2' )
+ outext = 'tbz2'
+ except ( OSError, zipfile.BadZipfile ):
+ error = True
+ log.exception( "Unable to create archive for download" )
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ except:
+ error = True
+ log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
+ message = "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
+ status = 'error'
+ if not error:
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
+ continue
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite:
+ # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
+ except IOError:
+ error = True
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
+ try:
+ archive.add( fpath,fname )
+ except IOError:
+ error = True
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ else: # simple case
+ try:
+ archive.add( ldda.dataset.file_name, path )
+ except IOError:
+ error = True
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ if not error:
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ elif format == 'uncompressed':
+ if len(lddas) != 1:
+ return 'Wrong request'
+ else:
+ single_dataset = lddas[0]
+ trans.response.set_content_type( single_dataset.get_mime() )
+ fStat = os.stat( ldda.file_name )
+ trans.response.headers[ 'Content-Length' ] = int( fStat.st_size )
+ valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ fname = ldda.name
+ fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
+ try:
+ return open( single_dataset.file_name )
+ except:
+ return 'This dataset contains no content'
+ else:
+ return 'Wrong format';
\ No newline at end of file
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -49,9 +49,10 @@
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
rval = []
for library in query:
- item = library.to_dict()
+ item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
- item['id'] = trans.security.encode_id( item['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
@@ -131,6 +132,9 @@
rval['name'] = name
rval['id'] = encoded_id
return rval
+
+ def edit( self, trans, payload, **kwd ):
+ return "Not implemented yet"
@web.expose_api
def delete( self, trans, id, **kwd ):
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,12 +46,6 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
- webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
- controller='admin_toolshed',
- action='display_image_in_repository',
- repository_id=None,
- image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
@@ -75,22 +69,12 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
webapp.add_route( '/search', controller='search', action='index' )
- # Add the web API
+ # ================
+ # ===== API =====
+ # ================
+
webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
@@ -102,10 +86,6 @@
controller="datasets",
action="display",
conditions=dict(method=["GET"]))
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'user',
'users',
controller='group_users',
@@ -127,11 +107,6 @@
_add_item_tags_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
-
_add_item_annotation_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -141,7 +116,6 @@
_add_item_annotation_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
_add_item_provenance_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -171,6 +145,39 @@
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ # The /folders section is experimental at this point:
+ log.debug( "app.config.api_folders: %s" % app.config.api_folders )
+
+ webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
+ webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
+
+ webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
+ webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
+
+ webapp.mapper.resource( 'content', 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
+
+
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
@@ -188,6 +195,18 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+ # ====================
+ # ===== TOOLSHED =====
+ # ====================
+
+ # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
+ webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
+ controller='admin_toolshed',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
@@ -201,6 +220,7 @@
path_prefix='/api',
new={ 'install_repository_revision' : 'POST' },
parent_resources=dict( member_name='tool_shed_repository', collection_name='tool_shed_repositories' ) )
+
# Connect logger from app
if app.trace_logger:
webapp.trace_logger = app.trace_logger
@@ -221,7 +241,7 @@
galaxy.model.mapping.metadata.engine.connection_provider._pool.dispose()
except:
pass
- # Close any pooled database connections before forking
+ # Close any pooled database connections before forking
try:
galaxy.model.tool_shed_install.mapping.metadata.engine.connection_provider._pool.dispose()
except:
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -76,6 +76,17 @@
library_list_grid = LibraryListGrid()
+
+ @web.expose
+ def list( self, trans, **kwd ):
+ params = util.Params( kwd )
+ # define app configuration for generic mako template
+ app = {
+ 'jscript' : "galaxy.library"
+ }
+ # fill template
+ return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 static/scripts/galaxy.library.js
--- /dev/null
+++ b/static/scripts/galaxy.library.js
@@ -0,0 +1,860 @@
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+// === GALAXY LIBRARY MODULE ====
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+
+// global variables
+var view = null;
+var library_router = null;
+var responses = [];
+
+
+// load required libraries
+// require([
+// // load js libraries
+// 'utils/galaxy.css',
+// ], function(css){
+// // load css
+// css.load_file("static/style/library.css");
+// });
+
+// dependencies
+define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils"], function(mod_modal, mod_masthead, mod_utils) {
+
+// MMMMMMMMMMMMMMM
+// === Models ====
+// MMMMMMMMMMMMMMM
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries'
+ });
+
+ // LIBRARIES
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ urlRoot : '/api/libraries/datasets'
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // CONTAINER for folder contents (folders, items and metadata).
+ var FolderContainer = Backbone.Model.extend({
+ defaults : {
+ folder : new Folder(),
+ full_path : "unknown",
+ urlRoot : "/api/folders/",
+ id : "unknown"
+ },
+ parse : function(obj) {
+ this.full_path = obj[0].full_path;
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+ return obj;
+ }
+ })
+
+ // HISTORY ITEM
+ var HistoryItem = Backbone.Model.extend({
+ urlRoot : '/api/histories/'
+ });
+
+ // HISTORY
+ var GalaxyHistory = Backbone.Model.extend({
+ url : '/api/histories/'
+ });
+
+ // HISTORIES
+ var GalaxyHistories = Backbone.Collection.extend({
+ url : '/api/histories',
+ model : GalaxyHistory
+ });
+
+ //ROUTER
+ var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content",
+ "folders/:folder_id/download/:format" : "download"
+ }
+ });
+
+
+// MMMMMMMMMMMMMM
+// === VIEWS ====
+// MMMMMMMMMMMMMM
+
+// galaxy folder
+var FolderContentView = Backbone.View.extend({
+ // main element definition
+ el : '#center',
+ // progress percentage
+ progress: 0,
+ // progress rate per one item
+ progressStep: 1,
+ // last selected history in modal for UX
+ lastSelectedHistory: '',
+ // self modal
+ modal : null,
+ // loaded folders
+ folders : null,
+
+ // initialize
+ initialize : function(){
+ this.folders = [];
+ this.queue = jQuery.Deferred();
+ this.queue.resolve();
+ },
+
+// MMMMMMMMMMMMMMMMMM
+// === TEMPLATES ====
+// MMMMMMMMMMMMMMMMMM
+
+ // set up
+ templateFolder : function (){
+ var tmpl_array = [];
+
+ // CONTAINER
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
+ 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>');
+
+ // TOOLBAR
+ tmpl_array.push('<div id="library_folder_toolbar" >');
+ tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-icon-plus"></span><span class="fa fa-icon-folder-close"></span> folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-icon-external-link"></span> to history</button>');
+
+ tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' <span class="fa fa-icon-download"></span> download <span class="caret"></span>');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
+ tmpl_array.push(' </ul>');
+ tmpl_array.push(' </div>');
+
+ tmpl_array.push('</div>');
+
+ // BREADCRUMBS
+ tmpl_array.push('<div class="library_breadcrumb">');
+ tmpl_array.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<span title="You are in this folder"><%- path_item[1] %></span>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('</div>');
+
+ // FOLDER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th class="button_heading">view</th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>data type</th>');
+ tmpl_array.push(' <th>size</th>');
+ tmpl_array.push(' <th>date</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td>folder</td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>'); // size
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <td>');
+ tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-eye-open"></span> details');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
+ tmpl_array.push(' <td><%= _.escape(content_item.get("data_type")) %></td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>'); // size
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>'); // time updated
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+ templateDatasetModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div id="dataset_info_modal">');
+ tmpl_array.push(' <table class="table table-striped table-condensed">');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Data type</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("data_type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Genome build</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("genome_build")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Size</th>');
+ tmpl_array.push(' <td><%= _.escape(size) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Date uploaded</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Uploaded by</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr scope="row">');
+ tmpl_array.push(' <th scope="row">Data Lines</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Comment Lines</th>');
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Number of Columns</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Column Types</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' </table>');
+ tmpl_array.push(' <pre class="peek">');
+ tmpl_array.push(' </pre>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ },
+
+ templateHistorySelectInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ templateBulkImportInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ // convert size to nice string
+ size_to_string : function (size)
+ {
+ // identify unit
+ var unit = "";
+ if (size >= 100000000000) { size = size / 100000000000; unit = "TB"; } else
+ if (size >= 100000000) { size = size / 100000000; unit = "GB"; } else
+ if (size >= 100000) { size = size / 100000; unit = "MB"; } else
+ if (size >= 100) { size = size / 100; unit = "KB"; } else
+ { size = size * 10; unit = "b"; }
+ // return formatted string
+ return (Math.round(size) / 10) + unit;
+ },
+
+// MMMMMMMMMMMMMMM
+// === EVENTS ====
+// MMMMMMMMMMMMMMM
+
+ // event binding
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked',
+ 'click #toolbtn_bulk_import' : 'modalBulkImport',
+ 'click #toolbtn_dl' : 'bulkDownload',
+ 'click .library-dataset' : 'showDatasetDetails',
+ 'click #toolbtn_create_folder' : 'createFolderModal',
+ 'click .btn_open_folder' : 'navigateToFolder'
+ },
+
+ //render the folder view
+ render: function (options) {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+
+ view = this;
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+
+ folderContainer.fetch({
+ success: function (container) {
+
+ // prepare nice size strings
+ for (var i = 0; i < folderContainer.attributes.folder.models.length; i++) {
+ var model = folderContainer.attributes.folder.models[i]
+ if (model.get('type') === 'file'){
+ model.set('readable_size', that.size_to_string(model.get('file_size')))
+ }
+ };
+
+ // find the upper id
+ var path = folderContainer.full_path;
+ var upper_folder_id;
+ if (path.length === 1){ // library is above us
+ upper_folder_id = 0;
+ } else {
+ upper_folder_id = path[path.length-2][0];
+ }
+
+ var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
+ // var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // handles the click on 'open' and 'upper' folder icons
+ navigateToFolder : function(event){
+ var folder_id = $(event.target).attr('data-id');
+ if (typeof folder_id === 'undefined') {
+ return false;
+ } else if (folder_id === '0'){
+ library_router.navigate('#', {trigger: true, replace: true});
+ } else {
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ }
+ },
+
+ //show modal with current dataset info
+ showDatasetDetails : function(event){
+ // prevent default
+ event.preventDefault();
+
+//TODO check whether we already have the data
+
+ //load the ID of the row
+ var id = $(event.target).parent().parent().attr('id');
+
+ //create new item
+ var item = new Item();
+ var histories = new GalaxyHistories();
+ item.id = id;
+ var self = this;
+
+ //fetch the dataset info
+ item.fetch({
+ success: function (item) {
+// TODO can render here already
+ //fetch user histories for import purposes
+ histories.fetch({
+ success: function (histories){self.renderModalAfterFetch(item, histories)}
+ });
+ }
+ });
+ },
+
+ // show the current dataset in a modal
+ 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({
+ 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
+ }
+ });
+ $(".peek").html(item.get("peek"));
+ var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+
+ // preset last selected history if we know it
+ if (self.lastSelectedHistory.length > 0) {
+ $(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
+ }
+
+ // show the prepared modal
+ this.modal.show();
+ },
+
+ // download dataset shown currently in modal
+ downloadCurrent : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var library_dataset_id = [];
+ library_dataset_id.push($('#id_row').attr('data-id'));
+ var url = '/api/libraries/datasets/download/uncompressed';
+ var data = {'ldda_ids' : library_dataset_id};
+
+ // we assume the view is existent
+ folderContentView.processDownload(url, data);
+ this.modal.enableButton('Import');
+ this.modal.enableButton('Download');
+ },
+
+ // import dataset shown currently in modal into selected history
+ importCurrentIntoHistory : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var history_id = $(this.modal.elMain).find('select[name=dataset_import_single] option:selected').val();
+ this.lastSelectedHistory = history_id; //save selected history for further use
+
+ var library_dataset_id = $('#id_row').attr('data-id');
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+
+ // save the dataset into selected history
+ historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
+ self.modal.showNotification('Dataset imported', 3000, 'success');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }, error : function(){
+ self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }
+ });
+ },
+
+ // select all datasets
+ selectAll : function (event) {
+ var selected = event.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ this.showTools();
+ },
+
+ // click checkbox on folder click
+ selectClicked : function (event) {
+ var checkbox = $("#" + event.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ // $(event.target.parentElement).css('background-color', '').css('color', '');
+ $(event.target.parentElement).removeClass('dark');
+ $(event.target.parentElement).find('a').removeClass('dark');
+ $(event.target.parentElement).addClass('light');
+ $(event.target.parentElement).find('a').addClass('light');
+ } else {
+ checkbox[0].checked = 'selected';
+ $(event.target.parentElement).removeClass('light');
+ $(event.target.parentElement).find('a').removeClass('light');
+ $(event.target.parentElement).addClass('dark');
+ $(event.target.parentElement).find('a').addClass('dark');
+ // $(event.target.parentElement).css('background-color', '#8389a1').css('color', 'white');
+ }
+ }
+ this.showTools();
+ },
+
+ // show toolbar in case something is selected
+ showTools : function(){
+ var checkedValues = $('#folder_table').find(':checked');
+ if(checkedValues.length > 0){
+ $('#toolbtn_bulk_import').show();
+ $('#toolbtn_dl').show();
+ } else {
+ $('#toolbtn_bulk_import').hide();
+ $('#toolbtn_dl').hide();
+ }
+
+ },
+
+ // show bulk import modal
+ modalBulkImport : function(){
+ var self = this;
+ // fetch histories
+ var histories = new GalaxyHistories();
+ histories.fetch({
+ success: function (histories){
+ // make modal
+ var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
+ self.modal = new mod_modal.GalaxyModal({
+ title : 'Import into History',
+ body : history_modal_tmpl,
+ buttons : {
+ 'Import' : function() {self.importAllIntoHistory()},
+ 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
+ }
+ });
+ // show the prepared modal
+ self.modal.show();
+ }
+ });
+ },
+
+ // import all selected datasets into history
+ importAllIntoHistory : function (){
+ //disable the button
+ this.modal.disableButton('Import');
+
+ var history_id = $("select[name=dataset_import_bulk] option:selected").val();
+ var history_name = $("select[name=dataset_import_bulk] option:selected").text();
+
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+ var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
+ $(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
+
+ // init the progress bar
+ var progressStep = 100 / dataset_ids.length;
+ this.initProgress(progressStep);
+
+ // prepare the dataset objects to be imported
+ var datasets_to_import = [];
+ for (var i = dataset_ids.length - 1; i >= 0; i--) {
+ library_dataset_id = dataset_ids[i];
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ historyItem.content = library_dataset_id;
+ historyItem.source = 'library';
+ datasets_to_import.push(historyItem);
+ };
+
+ // call the recursive function to call ajax one after each other
+ this.chainCall(datasets_to_import);
+ },
+
+ chainCall: function(history_item_set){
+ var self = this;
+ var popped_item = history_item_set.pop();
+ if (typeof popped_item === "undefined") {
+ self.modal.showNotification('All datasets imported', 3000, 'success');
+ // enable button again
+ self.modal.enableButton('Import');
+ return
+ }
+ var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
+ self.updateProgress();
+ responses.push(a1);
+ self.chainCall(history_item_set);
+ });
+ },
+
+ initProgress: function(progressStep){
+ this.progress = 0;
+ this.progressStep = progressStep;
+ },
+ updateProgress: function(){
+ this.progress += this.progressStep;
+ $('.progress-bar').width(Math.round(this.progress) + '%');
+ txt_representation = Math.round(this.progress) + '% Complete';
+ $('.completion_span').text(txt_representation);
+ },
+
+ // progress bar
+ templateProgressBar : function (){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div class="import_text">');
+ tmpl_array.push('Importing selected datasets to history <b><%= _.escape(history_name) %></b>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('<div class="progress">');
+ tmpl_array.push(' <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');
+ tmpl_array.push(' <span class="completion_span">0% Complete</span>');
+ tmpl_array.push(' </div>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+
+ // download selected datasets
+ download : function(folder_id, format){
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+
+ var url = '/api/libraries/datasets/download/' + format;
+ var data = {'ldda_ids' : dataset_ids};
+ this.processDownload(url, data);
+ },
+
+ // create hidden form and submit through POST to initialize download
+ processDownload: function(url, data, method){
+ //url and data options required
+ if( url && data ){
+ //data can be string of parameters or array/object
+ data = typeof data == 'string' ? data : $.param(data);
+ //split params into form inputs
+ var inputs = '';
+ $.each(data.split('&'), function(){
+ var pair = this.split('=');
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ });
+ //send request
+ $('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
+ .appendTo('body').submit().remove();
+ };
+ },
+
+ // shows modal for creating folder
+ createFolderModal: function(){
+ alert('creating folder');
+ }
+
+ });
+
+// galaxy library view
+var GalaxyLibraryview = Backbone.View.extend({
+ el: '#center',
+
+ events: {
+ 'click #create_new_library_btn' : 'show_library_modal'
+ },
+
+ // initialize
+ initialize : function(){
+ },
+
+ // template
+ 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 icon-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-icon-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
+ render: function () {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+ var that = this;
+ // if (typeof libraries === "undefined") {
+ libraries = new Libraries();
+ // }
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template(that.template_library_list(), { libraries : libraries.models });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // own modal
+ modal : null,
+
+ // show/hide create library modal
+ show_library_modal : function (event){
+ event.preventDefault();
+ event.stopPropagation();
+
+ // create modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal(
+ {
+ title : 'Create New Library',
+ body : this.template_new_library(),
+ buttons : {
+ 'Create' : function() {self.create_new_library_event()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+
+ // show prepared modal
+ this.modal.show();
+ },
+
+ // create the new library from modal
+ create_new_library_event: function(){
+ var libraryDetails = this.serialize_new_library();
+ var valid = this.validate_new_library(libraryDetails);
+ var library = new Library();
+ var self = this;
+ library.save(libraryDetails, {
+ success: function (library) {
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ },
+ error: function(){
+ self.modal.showNotification('An error occured', 5000, 'error');
+ }
+ });
+ return false;
+ },
+
+ // clear the library modal once saved
+ clear_library_modal : function(){
+ $("input[name='Name']").val('');
+ $("input[name='Description']").val('');
+ $("input[name='Synopsis']").val('');
+ },
+
+ // serialize data from the form
+ serialize_new_library : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val(),
+ synopsis: $("input[name='Synopsis']").val()
+ };
+ },
+
+ validate_new_library: function(library){
+
+ },
+
+
+ // template for new library modal
+ 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('');
+ }
+});
+
+// galaxy library wrapper View
+var GalaxyLibrary = Backbone.View.extend({
+ folderContentView : null,
+ galaxyLibraryview : null,
+ initialize : function(){
+
+ folderContentView = new FolderContentView();
+ galaxyLibraryview = new GalaxyLibraryview();
+
+ library_router = new LibraryRouter();
+
+ library_router.on('route:libraries', function() {
+ // render libraries list
+ galaxyLibraryview.render();
+ });
+
+ library_router.on('route:folder_content', function(id) {
+ // render folder's contents
+ folderContentView.render({id: id});
+ });
+
+ library_router.on('route:download', function(folder_id, format) {
+ // send download stream
+ if (typeof folderContentView === 'undefined'){
+ alert('you cant touch this!');
+ // } else if (folderContentView.modal !== null){
+ // folderContentView.download(folder_id, format);
+ } else if ($('#center').find(':checked').length === 0) { // coming from outside of the library app
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ // TODO show message of redirection
+ } else {
+ folderContentView.download(folder_id, format);
+ library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
+ }
+
+ });
+
+Backbone.history.start();
+
+return this
+}
+});
+
+// return
+return {
+ GalaxyApp: GalaxyLibrary
+};
+
+});
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -57,7 +57,12 @@
tab_shared.add({
title : "Data Libraries",
- content : "library/index",
+ content : "library/index"
+ });
+
+ tab_shared.add({
+ title : "New Libraries",
+ content : "library/list",
divider : true
});
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -1,6 +1,3 @@
-/*
- galaxy modal
-*/
// dependencies
define([], function() {
@@ -25,8 +22,51 @@
// initialize
initialize : function(options) {
+ self = this;
if (options)
this.create(options);
+
+ this.bindClick(event, self);
+ },
+
+ // bind the click-to-hide function
+ bindClick: function(event, that) {
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the click anywhere to hide() function...
+ $('html').on('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // unbind the click-to-hide function
+ unbindClick: function(event, that){
+ // bind the ESC key to hide() function
+ $(document).off('keyup', function(event){
+ if (event.keyCode == 27) { that.hide(); }
+ })
+ // unbind the click anywhere to hide() function...
+ $('html').off('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').off('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+
+ // destroy
+ destroy : function(){
+ this.hide();
+ this.unbindClick();
+ $('.modal').remove();
},
// adds and displays a new frame/window
@@ -87,6 +127,7 @@
this.$footer = (this.$el).find('.modal-footer');
this.$buttons = (this.$el).find('.buttons');
this.$backdrop = (this.$el).find('.modal-backdrop');
+ this.$notification = (this.$el).find('.notification-modal');
// append body
this.$body.html(this.options.body);
@@ -120,6 +161,47 @@
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', true);
},
+ // hide buttons
+ hideButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).hide();
+ },
+ // show buttons
+ showButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).show();
+ },
+
+ // show notification
+ showNotification : function(message, duration, type) {
+ // defaults
+ var duration = typeof duration !== 'undefined' ? duration : 1500;
+ // var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
+ // var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ var bgColor;
+ var txtColor;
+
+ if (type === 'error'){
+ bgColor = '#f4e0e1';
+ txtColor = '#a42732';
+ // } else if (type === 'success'){
+ } else { // success is default
+ bgColor = '#e1f4e0';
+ txtColor = '#32a427';
+ }
+
+ var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
+ this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
+
+ var self = this;
+
+ /*animate the bar*/
+ $('#notification-bar').slideDown(function() {
+ setTimeout(function() {
+ $('#notification-bar').slideUp(function() {self.$notification.html('');});
+ }, duration);
+ });
+
+ },
+
// returns scroll top for body element
scrollTop: function()
{
@@ -139,7 +221,8 @@
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
- '</div>' +
+ '<span class="notification-modal"></span>' +
+ '</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
'<div class="buttons" style="float: right;"></div>' +
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 static/scripts/libs/bootstrap.js
--- a/static/scripts/libs/bootstrap.js
+++ b/static/scripts/libs/bootstrap.js
@@ -575,3 +575,158 @@
}
}(window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#dropdowns
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ var $el = $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ $parent.trigger(e = $.Event('show.bs.dropdown'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown')
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index=0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus() {
+ $(backdrop).remove()
+ $(toggle).each(function (e) {
+ var $parent = getParent($(this))
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown'))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('dropdown')
+
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
\ No newline at end of file
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 static/style/src/less/library.less
--- a/static/style/src/less/library.less
+++ b/static/style/src/less/library.less
@@ -29,6 +29,50 @@
background-color: @table-bg-accent;
}
+
+tr.light td
+{
+ background-color: white;
+ color: black;
+}
+tr.light:hover td
+{
+ background-color: #f5f5f5;
+ color: #8389a1;
+}
+tr.dark td
+{
+ background-color: #8389a1;
+ color: white;
+}
+tr.dark:hover td
+{
+ background-color: #bbbfd0;
+ color: white;
+}
+a.dark:hover
+{
+ color: yellow;
+ // text-decoration: none;
+}
+a.dark
+{
+ color: white;
+ // text-decoration: none;
+}
+th.button_heading
+{
+ width: 7em;
+}
+div.library_breadcrumb{
+ padding-top: 0.8em;
+ padding-bottom: 0.8em;
+}
+div.library_breadcrumb a:hover{
+ color:green;
+}
+
+
img.expanderIcon {
padding-right: 4px;
}
https://bitbucket.org/galaxy/galaxy-central/commits/17c16125da1a/
Changeset: 17c16125da1a
User: martenson
Date: 2013-12-11 17:22:38
Summary: Merged galaxy/galaxy-central into default
Affected #: 4 files
diff -r 181b293ea65936b7edb338bcbeac3bdea8e53063 -r 17c16125da1a00a53a020ff0fd84e5676dbabaf1 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1050,8 +1050,11 @@
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
Column( "key", TrimmedString( 32 ), index=True, unique=True ) )
+
# With the tables defined we can define the mappers and setup the
# relationships between the model objects.
+def simple_mapping( model, **kwds ):
+ mapper( model, model.table, properties=kwds )
mapper( model.Sample, model.Sample.table,
@@ -1200,64 +1203,62 @@
mapper( model.ValidationError, model.ValidationError.table )
-mapper( model.HistoryDatasetAssociation, model.HistoryDatasetAssociation.table,
- properties=dict(
- dataset=relation(
- model.Dataset,
- primaryjoin=( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ), lazy=False ),
- # .history defined in History mapper
- copied_from_history_dataset_association=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == model.HistoryDatasetAssociation.table.c.id ),
- remote_side=[model.HistoryDatasetAssociation.table.c.id],
- uselist=False ),
- copied_to_history_dataset_associations=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == model.HistoryDatasetAssociation.table.c.id ) ),
- copied_from_library_dataset_dataset_association=relation(
- model.LibraryDatasetDatasetAssociation,
- primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == model.LibraryDatasetDatasetAssociation.table.c.id ),
- uselist=False ),
- copied_to_library_dataset_dataset_associations=relation(
- model.LibraryDatasetDatasetAssociation,
- primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == model.LibraryDatasetDatasetAssociation.table.c.id ) ),
- implicitly_converted_datasets=relation(
- model.ImplicitlyConvertedDatasetAssociation,
- primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_parent_id == model.HistoryDatasetAssociation.table.c.id ) ),
- implicitly_converted_parent_datasets=relation(
- model.ImplicitlyConvertedDatasetAssociation,
- primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_id == model.HistoryDatasetAssociation.table.c.id ) ),
- children=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ),
- backref=backref( "parent", primaryjoin=( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ), remote_side=[model.HistoryDatasetAssociation.table.c.id], uselist=False ) ),
- visible_children=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( ( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ) & ( model.HistoryDatasetAssociation.table.c.visible == True ) ) ),
- tags=relation( model.HistoryDatasetAssociationTagAssociation, order_by=model.HistoryDatasetAssociationTagAssociation.table.c.id, backref='history_tag_associations' ),
- annotations=relation( model.HistoryDatasetAssociationAnnotationAssociation, order_by=model.HistoryDatasetAssociationAnnotationAssociation.table.c.id, backref="hdas" ),
- ratings=relation( model.HistoryDatasetAssociationRatingAssociation, order_by=model.HistoryDatasetAssociationRatingAssociation.table.c.id, backref="hdas" ) )
- )
+simple_mapping( model.HistoryDatasetAssociation,
+ dataset=relation(
+ model.Dataset,
+ primaryjoin=( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ), lazy=False ),
+ # .history defined in History mapper
+ copied_from_history_dataset_association=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == model.HistoryDatasetAssociation.table.c.id ),
+ remote_side=[model.HistoryDatasetAssociation.table.c.id],
+ uselist=False ),
+ copied_to_history_dataset_associations=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == model.HistoryDatasetAssociation.table.c.id ) ),
+ copied_from_library_dataset_dataset_association=relation(
+ model.LibraryDatasetDatasetAssociation,
+ primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == model.LibraryDatasetDatasetAssociation.table.c.id ),
+ uselist=False ),
+ copied_to_library_dataset_dataset_associations=relation(
+ model.LibraryDatasetDatasetAssociation,
+ primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == model.LibraryDatasetDatasetAssociation.table.c.id ) ),
+ implicitly_converted_datasets=relation(
+ model.ImplicitlyConvertedDatasetAssociation,
+ primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_parent_id == model.HistoryDatasetAssociation.table.c.id ) ),
+ implicitly_converted_parent_datasets=relation(
+ model.ImplicitlyConvertedDatasetAssociation,
+ primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_id == model.HistoryDatasetAssociation.table.c.id ) ),
+ children=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ),
+ backref=backref( "parent", primaryjoin=( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ), remote_side=[model.HistoryDatasetAssociation.table.c.id], uselist=False ) ),
+ visible_children=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( ( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ) & ( model.HistoryDatasetAssociation.table.c.visible == True ) ) ),
+ tags=relation( model.HistoryDatasetAssociationTagAssociation, order_by=model.HistoryDatasetAssociationTagAssociation.table.c.id, backref='history_tag_associations' ),
+ annotations=relation( model.HistoryDatasetAssociationAnnotationAssociation, order_by=model.HistoryDatasetAssociationAnnotationAssociation.table.c.id, backref="hdas" ),
+ ratings=relation( model.HistoryDatasetAssociationRatingAssociation, order_by=model.HistoryDatasetAssociationRatingAssociation.table.c.id, backref="hdas" )
+)
-mapper( model.Dataset, model.Dataset.table,
- properties=dict(
- history_associations=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) ),
- active_history_associations=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( ( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) & ( model.HistoryDatasetAssociation.table.c.deleted == False ) & ( model.HistoryDatasetAssociation.table.c.purged == False ) ) ),
- purged_history_associations=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( ( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) & ( model.HistoryDatasetAssociation.table.c.purged == True ) ) ),
- library_associations=relation(
- model.LibraryDatasetDatasetAssociation,
- primaryjoin=( model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id ) ),
- active_library_associations=relation(
- model.LibraryDatasetDatasetAssociation,
- primaryjoin=( ( model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id ) & ( model.LibraryDatasetDatasetAssociation.table.c.deleted == False ) ) ),
- tags=relation(model.DatasetTagAssociation, order_by=model.DatasetTagAssociation.table.c.id, backref='datasets')
- ) )
+simple_mapping( model.Dataset,
+ history_associations=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) ),
+ active_history_associations=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( ( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) & ( model.HistoryDatasetAssociation.table.c.deleted == False ) & ( model.HistoryDatasetAssociation.table.c.purged == False ) ) ),
+ purged_history_associations=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( ( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) & ( model.HistoryDatasetAssociation.table.c.purged == True ) ) ),
+ library_associations=relation(
+ model.LibraryDatasetDatasetAssociation,
+ primaryjoin=( model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id ) ),
+ active_library_associations=relation(
+ model.LibraryDatasetDatasetAssociation,
+ primaryjoin=( ( model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id ) & ( model.LibraryDatasetDatasetAssociation.table.c.deleted == False ) ) ),
+ tags=relation(model.DatasetTagAssociation, order_by=model.DatasetTagAssociation.table.c.id, backref='datasets')
+)
mapper( model.HistoryDatasetAssociationDisplayAtAuthorization, model.HistoryDatasetAssociationDisplayAtAuthorization.table,
properties=dict( history_dataset_association = relation( model.HistoryDatasetAssociation ),
@@ -1751,90 +1752,63 @@
) )
# Tag tables.
+simple_mapping( model.Tag,
+ children=relation(model.Tag, backref=backref( 'parent', remote_side=[model.Tag.table.c.id] ) )
+)
-mapper( model.Tag, model.Tag.table,
- properties=dict( children=relation(model.Tag, backref=backref( 'parent', remote_side=[model.Tag.table.c.id] ) )
- ) )
-mapper( model.HistoryTagAssociation, model.HistoryTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_histories"), user=relation( model.User ) )
- )
+def tag_mapping( tag_association_class, backref_name ):
+ simple_mapping( tag_association_class, tag=relation( model.Tag, backref=backref_name), user=relation( model.User ) )
-mapper( model.DatasetTagAssociation, model.DatasetTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_datasets"), user=relation( model.User ) )
- )
+tag_mapping( model.HistoryTagAssociation, "tagged_histories" )
-mapper( model.HistoryDatasetAssociationTagAssociation, model.HistoryDatasetAssociationTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_history_dataset_associations"), user=relation( model.User ) )
- )
+tag_mapping( model.DatasetTagAssociation, "tagged_datasets" )
-mapper( model.PageTagAssociation, model.PageTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_pages"), user=relation( model.User ) )
- )
+tag_mapping( model.HistoryDatasetAssociationTagAssociation, "tagged_history_dataset_associations" )
-mapper( model.StoredWorkflowTagAssociation, model.StoredWorkflowTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_workflows"), user=relation( model.User ) )
- )
+tag_mapping( model.PageTagAssociation, "tagged_pages" )
-mapper( model.WorkflowStepTagAssociation, model.WorkflowStepTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_workflow_steps"), user=relation( model.User ) )
- )
+tag_mapping( model.StoredWorkflowTagAssociation, "tagged_workflows" )
-mapper( model.VisualizationTagAssociation, model.VisualizationTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_visualizations"), user=relation( model.User ) )
- )
+tag_mapping( model.WorkflowStepTagAssociation, "tagged_workflow_steps" )
-mapper( model.ToolTagAssociation, model.ToolTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_tools"), user=relation( model.User ) )
- )
+tag_mapping( model.VisualizationTagAssociation, "tagged_visualizations" )
+
+tag_mapping( model.ToolTagAssociation, "tagged_tools" )
+
# Annotation tables.
+def annotation_mapping( annotation_class, **kwds ):
+ kwds = dict( [ (key, relation( value ) ) for key, value in kwds.iteritems() ] )
+ simple_mapping( annotation_class, **dict(user=relation( model.User ), **kwds ) )
-mapper( model.HistoryAnnotationAssociation, model.HistoryAnnotationAssociation.table,
- properties=dict( history=relation( model.History ), user=relation( model.User ) )
- )
+annotation_mapping( model.HistoryAnnotationAssociation, history=model.History )
-mapper( model.HistoryDatasetAssociationAnnotationAssociation, model.HistoryDatasetAssociationAnnotationAssociation.table,
- properties=dict( hda=relation( model.HistoryDatasetAssociation ), user=relation( model.User ) )
- )
+annotation_mapping( model.HistoryDatasetAssociationAnnotationAssociation, hda=model.HistoryDatasetAssociation )
-mapper( model.StoredWorkflowAnnotationAssociation, model.StoredWorkflowAnnotationAssociation.table,
- properties=dict( stored_workflow=relation( model.StoredWorkflow ), user=relation( model.User ) )
- )
+annotation_mapping( model.StoredWorkflowAnnotationAssociation, stored_workflow=model.StoredWorkflow )
-mapper( model.WorkflowStepAnnotationAssociation, model.WorkflowStepAnnotationAssociation.table,
- properties=dict( workflow_step=relation( model.WorkflowStep ), user=relation( model.User ) )
- )
+annotation_mapping( model.WorkflowStepAnnotationAssociation, workflow_step=model.WorkflowStep )
-mapper( model.PageAnnotationAssociation, model.PageAnnotationAssociation.table,
- properties=dict( page=relation( model.Page ), user=relation( model.User ) )
- )
+annotation_mapping( model.PageAnnotationAssociation, page=model.Page )
-mapper( model.VisualizationAnnotationAssociation, model.VisualizationAnnotationAssociation.table,
- properties=dict( visualization=relation( model.Visualization ), user=relation( model.User ) )
- )
+annotation_mapping( model.VisualizationAnnotationAssociation, visualization=model.Visualization )
+
# Rating tables.
+def rating_mapping( rating_class, **kwds ):
+ kwds = dict( [ (key, relation( value ) ) for key, value in kwds.iteritems() ] )
+ simple_mapping( rating_class, **dict(user=relation( model.User ), **kwds ) )
-mapper( model.HistoryRatingAssociation, model.HistoryRatingAssociation.table,
- properties=dict( history=relation( model.History ), user=relation( model.User ) )
- )
+rating_mapping( model.HistoryRatingAssociation, history=model.History )
-mapper( model.HistoryDatasetAssociationRatingAssociation, model.HistoryDatasetAssociationRatingAssociation.table,
- properties=dict( hda=relation( model.HistoryDatasetAssociation ), user=relation( model.User ) )
- )
+rating_mapping( model.HistoryDatasetAssociationRatingAssociation, hda=model.HistoryDatasetAssociation )
-mapper( model.StoredWorkflowRatingAssociation, model.StoredWorkflowRatingAssociation.table,
- properties=dict( stored_workflow=relation( model.StoredWorkflow ), user=relation( model.User ) )
- )
+rating_mapping( model.StoredWorkflowRatingAssociation, stored_workflow=model.StoredWorkflow )
-mapper( model.PageRatingAssociation, model.PageRatingAssociation.table,
- properties=dict( page=relation( model.Page ), user=relation( model.User ) )
- )
+rating_mapping( model.PageRatingAssociation, page=model.Page )
-mapper( model.VisualizationRatingAssociation, model.VisualizationRatingAssociation.table,
- properties=dict( visualization=relation( model.Visualization ), user=relation( model.User ) )
- )
+rating_mapping( model.VisualizationRatingAssociation, visualizaiton=model.Visualization )
#Data Manager tables
mapper( model.DataManagerHistoryAssociation, model.DataManagerHistoryAssociation.table,
diff -r 181b293ea65936b7edb338bcbeac3bdea8e53063 -r 17c16125da1a00a53a020ff0fd84e5676dbabaf1 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -3003,10 +3003,10 @@
# Add link details.
if link_details:
# Add details for creating a hyperlink to the tool.
- if not self.tool_type.startswith( 'data_source' ):
- link = url_for( '/tool_runner', tool_id=self.id )
+ if not isinstance( self, DataSourceTool ):
+ link = url_for( controller='tool_runner', tool_id=self.id )
else:
- link = url_for( self.action, **self.get_static_param_values( trans ) )
+ link = url_for( controller='tool_runner', action='data_source_redirect', tool_id=self.id )
# Basic information
tool_dict.update( { 'link': link,
diff -r 181b293ea65936b7edb338bcbeac3bdea8e53063 -r 17c16125da1a00a53a020ff0fd84e5676dbabaf1 lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
@@ -6,6 +6,7 @@
import galaxy.util
from galaxy import web
from galaxy.tools import DefaultToolState
+from galaxy.tools import DataSourceTool
from galaxy.tools.actions import upload_common
from galaxy.tools.parameters import params_to_incoming
from galaxy.tools.parameters import visit_input_values
@@ -233,6 +234,35 @@
**vars )
@web.expose
+ def data_source_redirect( self, trans, tool_id=None ):
+ """
+ Redirects a user accessing a Data Source tool to its target action link.
+ This method will subvert mix-mode content blocking in several browsers when
+ accessing non-https data_source tools from an https galaxy server.
+
+ Tested as working on Safari 7.0 and FireFox 26
+ Subverting did not work on Chrome 31
+ """
+ if tool_id is None:
+ return trans.response.send_redirect( url_for( controller="root", action="welcome" ) )
+ tool_version_select_field, tools, tool = self.__get_tool_components( tool_id,
+ tool_version=None,
+ get_loaded_tools_by_lineage=False,
+ set_selected=False )
+ # No tool matching the tool id, display an error (shouldn't happen)
+ if not tool:
+ log.error( "data_source_redirect called with tool id '%s' but no such tool exists", tool_id )
+ trans.log_event( "Tool id '%s' does not exist" % tool_id )
+ trans.response.status = 404
+ return "Tool '%s' does not exist, kwd=%s " % ( tool_id, kwd )
+
+ if isinstance( tool, DataSourceTool ):
+ link = url_for( tool.action, **tool.get_static_param_values( trans ) )
+ else:
+ link = url_for( controller='tool_runner', tool_id=tool.id )
+ return trans.response.send_redirect( link )
+
+ @web.expose
def redirect( self, trans, redirect_url=None, **kwd ):
if not redirect_url:
return trans.show_error_message( "Required URL for redirection missing" )
diff -r 181b293ea65936b7edb338bcbeac3bdea8e53063 -r 17c16125da1a00a53a020ff0fd84e5676dbabaf1 test/unit/test_galaxy_mapping.py
--- a/test/unit/test_galaxy_mapping.py
+++ b/test/unit/test_galaxy_mapping.py
@@ -3,10 +3,139 @@
class MappingTests( unittest.TestCase ):
+
+ def test_annotations( self ):
+ model = self.model
+
+ u = model.User( email="annotator(a)example.com", password="password" )
+ self.persist( u )
+
+ def persist_and_check_annotation( annotation_class, **kwds ):
+ annotated_association = annotation_class()
+ annotated_association.annotation = "Test Annotation"
+ annotated_association.user = u
+ for key, value in kwds.iteritems():
+ setattr(annotated_association, key, value)
+ self.persist( annotated_association )
+ self.expunge()
+ stored_annotation = self.query( annotation_class ).all()[0]
+ assert stored_annotation.annotation == "Test Annotation"
+ assert stored_annotation.user.email == "annotator(a)example.com"
+
+ sw = model.StoredWorkflow()
+ sw.user = u
+ self.persist( sw )
+ persist_and_check_annotation( model.StoredWorkflowAnnotationAssociation, stored_workflow=sw )
+
+ workflow = model.Workflow()
+ workflow.stored_workflow = sw
+ self.persist( workflow )
+
+ ws = model.WorkflowStep()
+ ws.workflow = workflow
+ self.persist( ws )
+ persist_and_check_annotation( model.WorkflowStepAnnotationAssociation, workflow_step=ws )
+
+ h = model.History( name="History for Annotation", user=u)
+ self.persist( h )
+ persist_and_check_annotation( model.HistoryAnnotationAssociation, history=h )
+
+ d1 = model.HistoryDatasetAssociation( extension="txt", history=h, create_dataset=True, sa_session=model.session )
+ self.persist( d1 )
+ persist_and_check_annotation( model.HistoryDatasetAssociationAnnotationAssociation, hda=d1 )
+
+ page = model.Page()
+ page.user = u
+ self.persist( page )
+ persist_and_check_annotation( model.PageAnnotationAssociation, page=page )
+
+ visualization = model.Visualization()
+ visualization.user = u
+ self.persist( visualization )
+ persist_and_check_annotation( model.VisualizationAnnotationAssociation, visualization=visualization )
+
+ def test_ratings( self ):
+ model = self.model
+
+ u = model.User( email="rater(a)example.com", password="password" )
+ self.persist( u )
+
+ def persist_and_check_rating( rating_class, **kwds ):
+ rating_association = rating_class()
+ rating_association.rating = 5
+ rating_association.user = u
+ for key, value in kwds.iteritems():
+ setattr(rating_association, key, value)
+ self.persist( rating_association )
+ self.expunge()
+ stored_annotation = self.query( rating_class ).all()[0]
+ assert stored_annotation.rating == 5
+ assert stored_annotation.user.email == "rater(a)example.com"
+
+ sw = model.StoredWorkflow()
+ sw.user = u
+ self.persist( sw )
+ persist_and_check_rating( model.StoredWorkflowRatingAssociation, stored_workflow=sw )
+
+ h = model.History( name="History for Rating", user=u)
+ self.persist( h )
+ persist_and_check_rating( model.HistoryRatingAssociation, history=h )
+
+ d1 = model.HistoryDatasetAssociation( extension="txt", history=h, create_dataset=True, sa_session=model.session )
+ self.persist( d1 )
+ persist_and_check_rating( model.HistoryDatasetAssociationRatingAssociation, hda=d1 )
+
+ page = model.Page()
+ page.user = u
+ self.persist( page )
+ persist_and_check_rating( model.PageRatingAssociation, page=page )
+
+ visualization = model.Visualization()
+ visualization.user = u
+ self.persist( visualization )
+ persist_and_check_rating( model.VisualizationRatingAssociation, visualization=visualization )
+
+ def test_tags( self ):
+ model = self.model
+
+ my_tag = model.Tag(name="Test Tag")
+ u = model.User( email="tagger(a)example.com", password="password" )
+ self.persist( my_tag, u )
+
+ def tag_and_test( taggable_object, tag_association_class, backref_name ):
+ assert len( getattr(self.query( model.Tag ).filter( model.Tag.name == "Test Tag" ).all()[0], backref_name) ) == 0
+
+ tag_association = tag_association_class()
+ tag_association.tag = my_tag
+ taggable_object.tags = [ tag_association ]
+ self.persist( tag_association, taggable_object )
+
+ assert len( getattr(self.query( model.Tag ).filter( model.Tag.name == "Test Tag" ).all()[0], backref_name) ) == 1
+
+ sw = model.StoredWorkflow()
+ sw.user = u
+ #self.persist( sw )
+ tag_and_test( sw, model.StoredWorkflowTagAssociation, "tagged_workflows" )
+
+ h = model.History( name="History for Tagging", user=u)
+ tag_and_test( h, model.HistoryTagAssociation, "tagged_histories" )
+
+ d1 = model.HistoryDatasetAssociation( extension="txt", history=h, create_dataset=True, sa_session=model.session )
+ tag_and_test( d1, model.HistoryDatasetAssociationTagAssociation, "tagged_history_dataset_associations" )
+
+ page = model.Page()
+ page.user = u
+ tag_and_test( page, model.PageTagAssociation, "tagged_pages" )
+
+ visualization = model.Visualization()
+ visualization.user = u
+ tag_and_test( visualization, model.VisualizationTagAssociation, "tagged_visualizations" )
+
def test_basic( self ):
- # Start the database and connect the mapping
- model = mapping.init( "/tmp", "sqlite:///:memory:", create_tables=True )
- assert model.engine is not None
+ model = self.model
+
+ original_user_count = len( model.session.query( model.User ).all() )
+
# Make some changes and commit them
u = model.User( email="james(a)foo.bar.baz", password="password" )
# gs = model.GalaxySession()
@@ -14,40 +143,64 @@
#h1.queries.append( model.Query( "h1->q1" ) )
#h1.queries.append( model.Query( "h1->q2" ) )
h2 = model.History( name=( "H" * 1024 ) )
- model.session.add_all( ( u, h1, h2 ) )
+ self.persist( u, h1, h2 )
#q1 = model.Query( "h2->q1" )
metadata = dict( chromCol=1, startCol=2, endCol=3 )
d1 = model.HistoryDatasetAssociation( extension="interval", metadata=metadata, history=h2, create_dataset=True, sa_session=model.session )
#h2.queries.append( q1 )
#h2.queries.append( model.Query( "h2->q2" ) )
- model.session.add( ( d1 ) )
- model.session.flush()
- model.session.expunge_all()
+ self.persist( d1 )
+
# Check
users = model.session.query( model.User ).all()
- assert len( users ) == 1
- assert users[0].email == "james(a)foo.bar.baz"
- assert users[0].password == "password"
- assert len( users[0].histories ) == 1
- assert users[0].histories[0].name == "History 1"
+ assert len( users ) == original_user_count + 1
+ user = [user for user in users if user.email == "james(a)foo.bar.baz"][0]
+ assert user.email == "james(a)foo.bar.baz"
+ assert user.password == "password"
+ assert len( user.histories ) == 1
+ assert user.histories[0].name == "History 1"
hists = model.session.query( model.History ).all()
- assert hists[0].name == "History 1"
- assert hists[1].name == ( "H" * 255 )
- assert hists[0].user == users[0]
- assert hists[1].user is None
- assert hists[1].datasets[0].metadata.chromCol == 1
+ hist0 = [history for history in hists if history.name == "History 1"][0]
+ hist1 = [history for history in hists if history.name == "H" * 255][0]
+ assert hist0.name == "History 1"
+ assert hist1.name == ( "H" * 255 )
+ assert hist0.user == user
+ assert hist1.user is None
+ assert hist1.datasets[0].metadata.chromCol == 1
# The filename test has moved to objecstore
- #id = hists[1].datasets[0].id
- #assert hists[1].datasets[0].file_name == os.path.join( "/tmp", *directory_hash_id( id ) ) + ( "/dataset_%d.dat" % id )
+ #id = hist1.datasets[0].id
+ #assert hist1.datasets[0].file_name == os.path.join( "/tmp", *directory_hash_id( id ) ) + ( "/dataset_%d.dat" % id )
# Do an update and check
- hists[1].name = "History 2b"
- model.session.flush()
- model.session.expunge_all()
+ hist1.name = "History 2b"
+ self.expunge()
hists = model.session.query( model.History ).all()
- assert hists[0].name == "History 1"
- assert hists[1].name == "History 2b"
+ hist0 = [history for history in hists if history.name == "History 1"][0]
+ hist1 = [history for history in hists if history.name == "History 2b"][0]
+ assert hist0.name == "History 1"
+ assert hist1.name == "History 2b"
# gvk TODO need to ad test for GalaxySessions, but not yet sure what they should look like.
+ @classmethod
+ def setUpClass(cls):
+ # Start the database and connect the mapping
+ cls.model = mapping.init( "/tmp", "sqlite:///:memory:", create_tables=True )
+ assert cls.model.engine is not None
+
+ @classmethod
+ def query( cls, type ):
+ return cls.model.session.query( type )
+
+ @classmethod
+ def persist(cls, *args):
+ for arg in args:
+ cls.model.session.add( arg )
+ cls.expunge()
+
+ @classmethod
+ def expunge(cls):
+ cls.model.session.flush()
+ cls.model.session.expunge_all()
+
def get_suite():
suite = unittest.TestSuite()
https://bitbucket.org/galaxy/galaxy-central/commits/ff102054eec1/
Changeset: ff102054eec1
User: martenson
Date: 2013-12-11 19:03:02
Summary: merge into default
Affected #: 17 files
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1827,7 +1827,7 @@
class Library( object, Dictifiable ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
dict_collection_visible_keys = ( 'id', 'name' )
- dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
+ dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id' )
def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
@@ -1894,7 +1894,7 @@
return name
class LibraryFolder( object, Dictifiable ):
- dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
+ dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time' )
def __init__( self, name=None, description=None, item_count=0, order_id=None ):
self.name = name or "Unnamed folder"
self.description = description
@@ -2060,6 +2060,7 @@
genome_build = ldda.dbkey,
misc_info = ldda.info,
misc_blurb = ldda.blurb,
+ peek = ( lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None )( ldda ),
template_data = template_data )
if ldda.dataset.uuid is None:
rval['uuid'] = None
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1852,8 +1852,9 @@
table = self.table
trans = conn.begin()
try:
- next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
- table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
+ current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
+ next_hid = current_hid + 1
+ table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
trans.commit()
return next_hid
except:
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/webapps/galaxy/api/datasets.py
--- a/lib/galaxy/webapps/galaxy/api/datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a dataset.
+API operations on the contents of a history dataset.
"""
from galaxy import web
from galaxy.visualization.data_providers.genome import FeatureLocationIndexDataProvider
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 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
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a library.
+API operations on the contents of a folder.
"""
import logging, os, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
@@ -11,67 +11,122 @@
log = logging.getLogger( __name__ )
class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ):
+ """
+ Class controls retrieval, creation and updating of folder contents.
+ """
+
+ def load_folder_contents( self, trans, folder ):
+ """
+ Loads all contents of the folder (folders and data sets) but only in the first level.
+ """
+ current_user_roles = trans.get_current_user_roles()
+ is_admin = trans.user_is_admin()
+ content_items = []
+ for subfolder in folder.active_folders:
+ if not is_admin:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
+ if (is_admin or can_access) and not subfolder.deleted:
+ subfolder.api_type = 'folder'
+ content_items.append( subfolder )
+ for dataset in folder.datasets:
+ if not is_admin:
+ can_access = trans.app.security_agent.can_access_dataset( current_user_roles, dataset.library_dataset_dataset_association.dataset )
+ if (is_admin or can_access) and not dataset.deleted:
+ dataset.api_type = 'file'
+ content_items.append( dataset )
+ return content_items
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
Displays a collection (list) of a folder's contents (files and folders).
- The /api/library_contents/{encoded_library_id}/contents
- lists everything in a library recursively, which is not what
- we want here. We could add a parameter to use the recursive
- style, but this is meant to act similar to an "ls" directory listing.
+ Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
+ Full path is provided as a separate object in response providing data for breadcrumb path building.
"""
- rval = []
+ folder_container = []
current_user_roles = trans.get_current_user_roles()
- def traverse( folder ):
- admin = trans.user_is_admin()
- rval = []
- for subfolder in folder.active_folders:
- if not admin:
- can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
- if (admin or can_access) and not subfolder.deleted:
- subfolder.api_type = 'folder'
- rval.append( subfolder )
- for ld in folder.datasets:
- if not admin:
- can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ld.library_dataset_dataset_association.dataset )
- if (admin or can_access) and not ld.deleted:
- ld.api_type = 'file'
- rval.append( ld )
- return rval
-
- try:
- decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
- except TypeError:
- trans.response.status = 400
- return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+ if ( folder_id.startswith( 'F' ) ):
+ try:
+ decoded_folder_id = trans.security.decode_id( folder_id[1:] )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
- parent_library = folder.parent_library
except:
folder = None
- log.error( "FolderContentsController.index: Unable to retrieve folder %s"
- % folder_id )
+ log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
- # TODO: Find the API's path to this folder if necessary.
- # This was needed in recursive descent, but it's not needed
- # for "ls"-style content checking:
- if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ # We didn't find the folder or user does not have an access to it.
+ if not folder:
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, folder.id ) )
+ trans.response.status = 400
+ return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ path_to_root = []
+ def build_path ( folder ):
+ """
+ Search the path upwards recursively and load the whole route of names and ids for breadcrumb purposes.
+ """
+ path_to_root = []
+ # We are almost in root
+ if folder.parent_id is None:
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ else:
+ # We add the current folder and traverse up one folder.
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
+ path_to_root.extend( build_path( upper_folder ) )
+ return path_to_root
+
+ # Return the reversed path so it starts with the library node.
+ full_path = build_path( folder )[::-1]
+ folder_container.append( dict( full_path = full_path ) )
+
+ folder_contents = []
+ time_updated = ''
+ time_created = ''
+ # Go through every item in the folder and include its meta-data.
+ for content_item in self.load_folder_contents( trans, folder ):
+# rval = content_item.to_dict()
+ return_item = {}
+ encoded_id = trans.security.encode_id( content_item.id )
+ time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
+
+ # For folder return also hierarchy values
+ if content_item.api_type == 'folder':
+ encoded_id = 'F' + encoded_id
+# time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count ) )
- for content in traverse( folder ):
- encoded_id = trans.security.encode_id( content.id )
- if content.api_type == 'folder':
- encoded_id = 'F' + encoded_id
- rval.append( dict( id = encoded_id,
- type = content.api_type,
- name = content.name,
- url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
- return rval
+ if content_item.api_type == 'file':
+ library_dataset_dict = content_item.to_dict()
+ library_dataset_dict['data_type']
+ library_dataset_dict['file_size']
+ library_dataset_dict['date_uploaded']
+ return_item.update ( dict ( data_type = library_dataset_dict['data_type'],
+ file_size = library_dataset_dict['file_size'],
+ date_uploaded = library_dataset_dict['date_uploaded'] ) )
+
+ # For every item return also the default meta-data
+ return_item.update( dict( id = encoded_id,
+ type = content_item.api_type,
+ name = content_item.name,
+ time_updated = time_updated,
+ time_created = time_created
+ ) )
+ folder_contents.append( return_item )
+ # Put the data in the container
+ folder_container.append( dict( folder_contents = folder_contents ) )
+ return folder_container
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -0,0 +1,241 @@
+"""
+API operations on the dataset from library.
+"""
+import glob
+import logging
+import operator
+import os
+import os.path
+import string
+import sys
+import tarfile
+import tempfile
+import urllib
+import urllib2
+import zipfile
+from galaxy.security import Action
+from galaxy import util, web
+from galaxy.util.streamball import StreamBall
+from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
+
+import logging
+log = logging.getLogger( __name__ )
+
+# Test for available compression types
+# tmpd = tempfile.mkdtemp()
+# comptypes = []
+# for comptype in ( 'gz', 'bz2' ):
+# tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype )
+# try:
+# archive = tarfile.open( tmpf, 'w:' + comptype )
+# archive.close()
+# comptypes.append( comptype )
+# except tarfile.CompressionError:
+# log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+ziptype = '32'
+# tmpf = os.path.join( tmpd, 'compression_test.zip' )
+# try:
+# archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+# archive.close()
+# comptypes.append( 'zip' )
+# ziptype = '64'
+# except RuntimeError:
+# log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." )
+# except (TypeError, zipfile.LargeZipFile):
+# # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped
+# log.warning( 'Max zip file size is 2GB, ZIP64 not supported' )
+# comptypes.append( 'zip' )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+# os.rmdir( tmpd )
+
+
+
+class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
+
+ @web.expose_api
+ def show( self, trans, id, **kwd ):
+ """
+ GET /api/libraries/datasets/{encoded_dataset_id}
+ Displays information about the dataset identified by the lda ID.
+ """
+ # Get dataset.
+ try:
+ dataset = self.get_library_dataset( trans, id = id )
+ except Exception, e:
+ return str( e )
+ try:
+ # Default: return dataset as dict.
+ rval = dataset.to_dict()
+ except Exception, e:
+ rval = "Error in dataset API at listing contents: " + str( e )
+ log.error( rval + ": %s" % str(e), exc_info=True )
+ trans.response.status = 500
+
+ rval['id'] = trans.security.encode_id(rval['id']);
+ rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
+ rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
+
+ return rval
+
+ @web.expose
+ def download( self, trans, format, **kwd ):
+ """
+ POST /api/libraries/datasets/download/{format}
+ POST data: ldda_ids = []
+ Downloads dataset(s) in the requested format.
+ """
+ lddas = []
+# is_admin = trans.user_is_admin()
+# current_user_roles = trans.get_current_user_roles()
+
+ datasets_to_download = kwd['ldda_ids%5B%5D']
+
+ if ( datasets_to_download != None ):
+ datasets_to_download = util.listify( datasets_to_download )
+ for dataset_id in datasets_to_download:
+ try:
+ ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ assert not ldda.dataset.purged
+ lddas.append( ldda )
+ except:
+ ldda = None
+ message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
+
+ if format in [ 'zip','tgz','tbz' ]:
+ error = False
+ killme = string.punctuation + string.whitespace
+ trantab = string.maketrans(killme,'_'*len(killme))
+ try:
+ outext = 'zip'
+ if format == 'zip':
+ # Can't use mkstemp - the file must not exist first
+ tmpd = tempfile.mkdtemp()
+ util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
+ tmpf = os.path.join( tmpd, 'library_download.' + format )
+ if ziptype == '64' and trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
+ elif ziptype == '64':
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+ elif trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED )
+ else:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED )
+ archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
+ elif format == 'tgz':
+ if trans.app.config.upstream_gzip:
+ archive = StreamBall( 'w|' )
+ outext = 'tar'
+ else:
+ archive = StreamBall( 'w|gz' )
+ outext = 'tgz'
+ elif format == 'tbz':
+ archive = StreamBall( 'w|bz2' )
+ outext = 'tbz2'
+ except ( OSError, zipfile.BadZipfile ):
+ error = True
+ log.exception( "Unable to create archive for download" )
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ except:
+ error = True
+ log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
+ message = "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
+ status = 'error'
+ if not error:
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
+ continue
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite:
+ # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
+ except IOError:
+ error = True
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
+ try:
+ archive.add( fpath,fname )
+ except IOError:
+ error = True
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ else: # simple case
+ try:
+ archive.add( ldda.dataset.file_name, path )
+ except IOError:
+ error = True
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ if not error:
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ elif format == 'uncompressed':
+ if len(lddas) != 1:
+ return 'Wrong request'
+ else:
+ single_dataset = lddas[0]
+ trans.response.set_content_type( single_dataset.get_mime() )
+ fStat = os.stat( ldda.file_name )
+ trans.response.headers[ 'Content-Length' ] = int( fStat.st_size )
+ valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ fname = ldda.name
+ fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
+ try:
+ return open( single_dataset.file_name )
+ except:
+ return 'This dataset contains no content'
+ else:
+ return 'Wrong format';
\ No newline at end of file
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -49,9 +49,10 @@
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
rval = []
for library in query:
- item = library.to_dict()
+ item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
- item['id'] = trans.security.encode_id( item['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
@@ -131,6 +132,9 @@
rval['name'] = name
rval['id'] = encoded_id
return rval
+
+ def edit( self, trans, payload, **kwd ):
+ return "Not implemented yet"
@web.expose_api
def delete( self, trans, id, **kwd ):
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,12 +46,6 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
- webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
- controller='admin_toolshed',
- action='display_image_in_repository',
- repository_id=None,
- image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
@@ -75,22 +69,12 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
webapp.add_route( '/search', controller='search', action='index' )
- # Add the web API
+ # ================
+ # ===== API =====
+ # ================
+
webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
@@ -102,10 +86,6 @@
controller="datasets",
action="display",
conditions=dict(method=["GET"]))
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'user',
'users',
controller='group_users',
@@ -127,11 +107,6 @@
_add_item_tags_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
-
_add_item_annotation_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -141,7 +116,6 @@
_add_item_annotation_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
_add_item_provenance_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -171,6 +145,39 @@
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ # The /folders section is experimental at this point:
+ log.debug( "app.config.api_folders: %s" % app.config.api_folders )
+
+ webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
+ webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
+
+ webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
+ webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
+
+ webapp.mapper.resource( 'content', 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
+
+
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
@@ -188,6 +195,18 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+ # ====================
+ # ===== TOOLSHED =====
+ # ====================
+
+ # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
+ webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
+ controller='admin_toolshed',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
@@ -201,6 +220,7 @@
path_prefix='/api',
new={ 'install_repository_revision' : 'POST' },
parent_resources=dict( member_name='tool_shed_repository', collection_name='tool_shed_repositories' ) )
+
# Connect logger from app
if app.trace_logger:
webapp.trace_logger = app.trace_logger
@@ -221,7 +241,7 @@
galaxy.model.mapping.metadata.engine.connection_provider._pool.dispose()
except:
pass
- # Close any pooled database connections before forking
+ # Close any pooled database connections before forking
try:
galaxy.model.tool_shed_install.mapping.metadata.engine.connection_provider._pool.dispose()
except:
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -76,6 +76,17 @@
library_list_grid = LibraryListGrid()
+
+ @web.expose
+ def list( self, trans, **kwd ):
+ params = util.Params( kwd )
+ # define app configuration for generic mako template
+ app = {
+ 'jscript' : "galaxy.library"
+ }
+ # fill template
+ return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 static/scripts/galaxy.library.js
--- /dev/null
+++ b/static/scripts/galaxy.library.js
@@ -0,0 +1,860 @@
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+// === GALAXY LIBRARY MODULE ====
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+
+// global variables
+var view = null;
+var library_router = null;
+var responses = [];
+
+
+// load required libraries
+// require([
+// // load js libraries
+// 'utils/galaxy.css',
+// ], function(css){
+// // load css
+// css.load_file("static/style/library.css");
+// });
+
+// dependencies
+define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils"], function(mod_modal, mod_masthead, mod_utils) {
+
+// MMMMMMMMMMMMMMM
+// === Models ====
+// MMMMMMMMMMMMMMM
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries'
+ });
+
+ // LIBRARIES
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ urlRoot : '/api/libraries/datasets'
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // CONTAINER for folder contents (folders, items and metadata).
+ var FolderContainer = Backbone.Model.extend({
+ defaults : {
+ folder : new Folder(),
+ full_path : "unknown",
+ urlRoot : "/api/folders/",
+ id : "unknown"
+ },
+ parse : function(obj) {
+ this.full_path = obj[0].full_path;
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+ return obj;
+ }
+ })
+
+ // HISTORY ITEM
+ var HistoryItem = Backbone.Model.extend({
+ urlRoot : '/api/histories/'
+ });
+
+ // HISTORY
+ var GalaxyHistory = Backbone.Model.extend({
+ url : '/api/histories/'
+ });
+
+ // HISTORIES
+ var GalaxyHistories = Backbone.Collection.extend({
+ url : '/api/histories',
+ model : GalaxyHistory
+ });
+
+ //ROUTER
+ var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content",
+ "folders/:folder_id/download/:format" : "download"
+ }
+ });
+
+
+// MMMMMMMMMMMMMM
+// === VIEWS ====
+// MMMMMMMMMMMMMM
+
+// galaxy folder
+var FolderContentView = Backbone.View.extend({
+ // main element definition
+ el : '#center',
+ // progress percentage
+ progress: 0,
+ // progress rate per one item
+ progressStep: 1,
+ // last selected history in modal for UX
+ lastSelectedHistory: '',
+ // self modal
+ modal : null,
+ // loaded folders
+ folders : null,
+
+ // initialize
+ initialize : function(){
+ this.folders = [];
+ this.queue = jQuery.Deferred();
+ this.queue.resolve();
+ },
+
+// MMMMMMMMMMMMMMMMMM
+// === TEMPLATES ====
+// MMMMMMMMMMMMMMMMMM
+
+ // set up
+ templateFolder : function (){
+ var tmpl_array = [];
+
+ // CONTAINER
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
+ 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>');
+
+ // TOOLBAR
+ tmpl_array.push('<div id="library_folder_toolbar" >');
+ tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-icon-plus"></span><span class="fa fa-icon-folder-close"></span> folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-icon-external-link"></span> to history</button>');
+
+ tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' <span class="fa fa-icon-download"></span> download <span class="caret"></span>');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
+ tmpl_array.push(' </ul>');
+ tmpl_array.push(' </div>');
+
+ tmpl_array.push('</div>');
+
+ // BREADCRUMBS
+ tmpl_array.push('<div class="library_breadcrumb">');
+ tmpl_array.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<span title="You are in this folder"><%- path_item[1] %></span>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('</div>');
+
+ // FOLDER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th class="button_heading">view</th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>data type</th>');
+ tmpl_array.push(' <th>size</th>');
+ tmpl_array.push(' <th>date</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td>folder</td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>'); // size
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <td>');
+ tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-eye-open"></span> details');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
+ tmpl_array.push(' <td><%= _.escape(content_item.get("data_type")) %></td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>'); // size
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>'); // time updated
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+ templateDatasetModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div id="dataset_info_modal">');
+ tmpl_array.push(' <table class="table table-striped table-condensed">');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Data type</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("data_type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Genome build</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("genome_build")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Size</th>');
+ tmpl_array.push(' <td><%= _.escape(size) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Date uploaded</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Uploaded by</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr scope="row">');
+ tmpl_array.push(' <th scope="row">Data Lines</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Comment Lines</th>');
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Number of Columns</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Column Types</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' </table>');
+ tmpl_array.push(' <pre class="peek">');
+ tmpl_array.push(' </pre>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ },
+
+ templateHistorySelectInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ templateBulkImportInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ // convert size to nice string
+ size_to_string : function (size)
+ {
+ // identify unit
+ var unit = "";
+ if (size >= 100000000000) { size = size / 100000000000; unit = "TB"; } else
+ if (size >= 100000000) { size = size / 100000000; unit = "GB"; } else
+ if (size >= 100000) { size = size / 100000; unit = "MB"; } else
+ if (size >= 100) { size = size / 100; unit = "KB"; } else
+ { size = size * 10; unit = "b"; }
+ // return formatted string
+ return (Math.round(size) / 10) + unit;
+ },
+
+// MMMMMMMMMMMMMMM
+// === EVENTS ====
+// MMMMMMMMMMMMMMM
+
+ // event binding
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked',
+ 'click #toolbtn_bulk_import' : 'modalBulkImport',
+ 'click #toolbtn_dl' : 'bulkDownload',
+ 'click .library-dataset' : 'showDatasetDetails',
+ 'click #toolbtn_create_folder' : 'createFolderModal',
+ 'click .btn_open_folder' : 'navigateToFolder'
+ },
+
+ //render the folder view
+ render: function (options) {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+
+ view = this;
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+
+ folderContainer.fetch({
+ success: function (container) {
+
+ // prepare nice size strings
+ for (var i = 0; i < folderContainer.attributes.folder.models.length; i++) {
+ var model = folderContainer.attributes.folder.models[i]
+ if (model.get('type') === 'file'){
+ model.set('readable_size', that.size_to_string(model.get('file_size')))
+ }
+ };
+
+ // find the upper id
+ var path = folderContainer.full_path;
+ var upper_folder_id;
+ if (path.length === 1){ // library is above us
+ upper_folder_id = 0;
+ } else {
+ upper_folder_id = path[path.length-2][0];
+ }
+
+ var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
+ // var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // handles the click on 'open' and 'upper' folder icons
+ navigateToFolder : function(event){
+ var folder_id = $(event.target).attr('data-id');
+ if (typeof folder_id === 'undefined') {
+ return false;
+ } else if (folder_id === '0'){
+ library_router.navigate('#', {trigger: true, replace: true});
+ } else {
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ }
+ },
+
+ //show modal with current dataset info
+ showDatasetDetails : function(event){
+ // prevent default
+ event.preventDefault();
+
+//TODO check whether we already have the data
+
+ //load the ID of the row
+ var id = $(event.target).parent().parent().attr('id');
+
+ //create new item
+ var item = new Item();
+ var histories = new GalaxyHistories();
+ item.id = id;
+ var self = this;
+
+ //fetch the dataset info
+ item.fetch({
+ success: function (item) {
+// TODO can render here already
+ //fetch user histories for import purposes
+ histories.fetch({
+ success: function (histories){self.renderModalAfterFetch(item, histories)}
+ });
+ }
+ });
+ },
+
+ // show the current dataset in a modal
+ 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({
+ 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
+ }
+ });
+ $(".peek").html(item.get("peek"));
+ var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+
+ // preset last selected history if we know it
+ if (self.lastSelectedHistory.length > 0) {
+ $(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
+ }
+
+ // show the prepared modal
+ this.modal.show();
+ },
+
+ // download dataset shown currently in modal
+ downloadCurrent : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var library_dataset_id = [];
+ library_dataset_id.push($('#id_row').attr('data-id'));
+ var url = '/api/libraries/datasets/download/uncompressed';
+ var data = {'ldda_ids' : library_dataset_id};
+
+ // we assume the view is existent
+ folderContentView.processDownload(url, data);
+ this.modal.enableButton('Import');
+ this.modal.enableButton('Download');
+ },
+
+ // import dataset shown currently in modal into selected history
+ importCurrentIntoHistory : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var history_id = $(this.modal.elMain).find('select[name=dataset_import_single] option:selected').val();
+ this.lastSelectedHistory = history_id; //save selected history for further use
+
+ var library_dataset_id = $('#id_row').attr('data-id');
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+
+ // save the dataset into selected history
+ historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
+ self.modal.showNotification('Dataset imported', 3000, 'success');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }, error : function(){
+ self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }
+ });
+ },
+
+ // select all datasets
+ selectAll : function (event) {
+ var selected = event.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ this.showTools();
+ },
+
+ // click checkbox on folder click
+ selectClicked : function (event) {
+ var checkbox = $("#" + event.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ // $(event.target.parentElement).css('background-color', '').css('color', '');
+ $(event.target.parentElement).removeClass('dark');
+ $(event.target.parentElement).find('a').removeClass('dark');
+ $(event.target.parentElement).addClass('light');
+ $(event.target.parentElement).find('a').addClass('light');
+ } else {
+ checkbox[0].checked = 'selected';
+ $(event.target.parentElement).removeClass('light');
+ $(event.target.parentElement).find('a').removeClass('light');
+ $(event.target.parentElement).addClass('dark');
+ $(event.target.parentElement).find('a').addClass('dark');
+ // $(event.target.parentElement).css('background-color', '#8389a1').css('color', 'white');
+ }
+ }
+ this.showTools();
+ },
+
+ // show toolbar in case something is selected
+ showTools : function(){
+ var checkedValues = $('#folder_table').find(':checked');
+ if(checkedValues.length > 0){
+ $('#toolbtn_bulk_import').show();
+ $('#toolbtn_dl').show();
+ } else {
+ $('#toolbtn_bulk_import').hide();
+ $('#toolbtn_dl').hide();
+ }
+
+ },
+
+ // show bulk import modal
+ modalBulkImport : function(){
+ var self = this;
+ // fetch histories
+ var histories = new GalaxyHistories();
+ histories.fetch({
+ success: function (histories){
+ // make modal
+ var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
+ self.modal = new mod_modal.GalaxyModal({
+ title : 'Import into History',
+ body : history_modal_tmpl,
+ buttons : {
+ 'Import' : function() {self.importAllIntoHistory()},
+ 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
+ }
+ });
+ // show the prepared modal
+ self.modal.show();
+ }
+ });
+ },
+
+ // import all selected datasets into history
+ importAllIntoHistory : function (){
+ //disable the button
+ this.modal.disableButton('Import');
+
+ var history_id = $("select[name=dataset_import_bulk] option:selected").val();
+ var history_name = $("select[name=dataset_import_bulk] option:selected").text();
+
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+ var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
+ $(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
+
+ // init the progress bar
+ var progressStep = 100 / dataset_ids.length;
+ this.initProgress(progressStep);
+
+ // prepare the dataset objects to be imported
+ var datasets_to_import = [];
+ for (var i = dataset_ids.length - 1; i >= 0; i--) {
+ library_dataset_id = dataset_ids[i];
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ historyItem.content = library_dataset_id;
+ historyItem.source = 'library';
+ datasets_to_import.push(historyItem);
+ };
+
+ // call the recursive function to call ajax one after each other
+ this.chainCall(datasets_to_import);
+ },
+
+ chainCall: function(history_item_set){
+ var self = this;
+ var popped_item = history_item_set.pop();
+ if (typeof popped_item === "undefined") {
+ self.modal.showNotification('All datasets imported', 3000, 'success');
+ // enable button again
+ self.modal.enableButton('Import');
+ return
+ }
+ var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
+ self.updateProgress();
+ responses.push(a1);
+ self.chainCall(history_item_set);
+ });
+ },
+
+ initProgress: function(progressStep){
+ this.progress = 0;
+ this.progressStep = progressStep;
+ },
+ updateProgress: function(){
+ this.progress += this.progressStep;
+ $('.progress-bar').width(Math.round(this.progress) + '%');
+ txt_representation = Math.round(this.progress) + '% Complete';
+ $('.completion_span').text(txt_representation);
+ },
+
+ // progress bar
+ templateProgressBar : function (){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div class="import_text">');
+ tmpl_array.push('Importing selected datasets to history <b><%= _.escape(history_name) %></b>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('<div class="progress">');
+ tmpl_array.push(' <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');
+ tmpl_array.push(' <span class="completion_span">0% Complete</span>');
+ tmpl_array.push(' </div>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+
+ // download selected datasets
+ download : function(folder_id, format){
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+
+ var url = '/api/libraries/datasets/download/' + format;
+ var data = {'ldda_ids' : dataset_ids};
+ this.processDownload(url, data);
+ },
+
+ // create hidden form and submit through POST to initialize download
+ processDownload: function(url, data, method){
+ //url and data options required
+ if( url && data ){
+ //data can be string of parameters or array/object
+ data = typeof data == 'string' ? data : $.param(data);
+ //split params into form inputs
+ var inputs = '';
+ $.each(data.split('&'), function(){
+ var pair = this.split('=');
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ });
+ //send request
+ $('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
+ .appendTo('body').submit().remove();
+ };
+ },
+
+ // shows modal for creating folder
+ createFolderModal: function(){
+ alert('creating folder');
+ }
+
+ });
+
+// galaxy library view
+var GalaxyLibraryview = Backbone.View.extend({
+ el: '#center',
+
+ events: {
+ 'click #create_new_library_btn' : 'show_library_modal'
+ },
+
+ // initialize
+ initialize : function(){
+ },
+
+ // template
+ 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 icon-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-icon-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
+ render: function () {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+ var that = this;
+ // if (typeof libraries === "undefined") {
+ libraries = new Libraries();
+ // }
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template(that.template_library_list(), { libraries : libraries.models });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // own modal
+ modal : null,
+
+ // show/hide create library modal
+ show_library_modal : function (event){
+ event.preventDefault();
+ event.stopPropagation();
+
+ // create modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal(
+ {
+ title : 'Create New Library',
+ body : this.template_new_library(),
+ buttons : {
+ 'Create' : function() {self.create_new_library_event()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+
+ // show prepared modal
+ this.modal.show();
+ },
+
+ // create the new library from modal
+ create_new_library_event: function(){
+ var libraryDetails = this.serialize_new_library();
+ var valid = this.validate_new_library(libraryDetails);
+ var library = new Library();
+ var self = this;
+ library.save(libraryDetails, {
+ success: function (library) {
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ },
+ error: function(){
+ self.modal.showNotification('An error occured', 5000, 'error');
+ }
+ });
+ return false;
+ },
+
+ // clear the library modal once saved
+ clear_library_modal : function(){
+ $("input[name='Name']").val('');
+ $("input[name='Description']").val('');
+ $("input[name='Synopsis']").val('');
+ },
+
+ // serialize data from the form
+ serialize_new_library : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val(),
+ synopsis: $("input[name='Synopsis']").val()
+ };
+ },
+
+ validate_new_library: function(library){
+
+ },
+
+
+ // template for new library modal
+ 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('');
+ }
+});
+
+// galaxy library wrapper View
+var GalaxyLibrary = Backbone.View.extend({
+ folderContentView : null,
+ galaxyLibraryview : null,
+ initialize : function(){
+
+ folderContentView = new FolderContentView();
+ galaxyLibraryview = new GalaxyLibraryview();
+
+ library_router = new LibraryRouter();
+
+ library_router.on('route:libraries', function() {
+ // render libraries list
+ galaxyLibraryview.render();
+ });
+
+ library_router.on('route:folder_content', function(id) {
+ // render folder's contents
+ folderContentView.render({id: id});
+ });
+
+ library_router.on('route:download', function(folder_id, format) {
+ // send download stream
+ if (typeof folderContentView === 'undefined'){
+ alert('you cant touch this!');
+ // } else if (folderContentView.modal !== null){
+ // folderContentView.download(folder_id, format);
+ } else if ($('#center').find(':checked').length === 0) { // coming from outside of the library app
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ // TODO show message of redirection
+ } else {
+ folderContentView.download(folder_id, format);
+ library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
+ }
+
+ });
+
+Backbone.history.start();
+
+return this
+}
+});
+
+// return
+return {
+ GalaxyApp: GalaxyLibrary
+};
+
+});
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -57,7 +57,12 @@
tab_shared.add({
title : "Data Libraries",
- content : "library/index",
+ content : "library/index"
+ });
+
+ tab_shared.add({
+ title : "New Libraries",
+ content : "library/list",
divider : true
});
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -1,6 +1,3 @@
-/*
- galaxy modal
-*/
// dependencies
define([], function() {
@@ -25,8 +22,51 @@
// initialize
initialize : function(options) {
+ self = this;
if (options)
this.create(options);
+
+ this.bindClick(event, self);
+ },
+
+ // bind the click-to-hide function
+ bindClick: function(event, that) {
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the click anywhere to hide() function...
+ $('html').on('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // unbind the click-to-hide function
+ unbindClick: function(event, that){
+ // bind the ESC key to hide() function
+ $(document).off('keyup', function(event){
+ if (event.keyCode == 27) { that.hide(); }
+ })
+ // unbind the click anywhere to hide() function...
+ $('html').off('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').off('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+
+ // destroy
+ destroy : function(){
+ this.hide();
+ this.unbindClick();
+ $('.modal').remove();
},
// adds and displays a new frame/window
@@ -87,6 +127,7 @@
this.$footer = (this.$el).find('.modal-footer');
this.$buttons = (this.$el).find('.buttons');
this.$backdrop = (this.$el).find('.modal-backdrop');
+ this.$notification = (this.$el).find('.notification-modal');
// append body
this.$body.html(this.options.body);
@@ -120,6 +161,47 @@
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', true);
},
+ // hide buttons
+ hideButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).hide();
+ },
+ // show buttons
+ showButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).show();
+ },
+
+ // show notification
+ showNotification : function(message, duration, type) {
+ // defaults
+ var duration = typeof duration !== 'undefined' ? duration : 1500;
+ // var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
+ // var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ var bgColor;
+ var txtColor;
+
+ if (type === 'error'){
+ bgColor = '#f4e0e1';
+ txtColor = '#a42732';
+ // } else if (type === 'success'){
+ } else { // success is default
+ bgColor = '#e1f4e0';
+ txtColor = '#32a427';
+ }
+
+ var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
+ this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
+
+ var self = this;
+
+ /*animate the bar*/
+ $('#notification-bar').slideDown(function() {
+ setTimeout(function() {
+ $('#notification-bar').slideUp(function() {self.$notification.html('');});
+ }, duration);
+ });
+
+ },
+
// returns scroll top for body element
scrollTop: function()
{
@@ -139,7 +221,8 @@
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
- '</div>' +
+ '<span class="notification-modal"></span>' +
+ '</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
'<div class="buttons" style="float: right;"></div>' +
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 static/scripts/libs/bootstrap.js
--- a/static/scripts/libs/bootstrap.js
+++ b/static/scripts/libs/bootstrap.js
@@ -575,3 +575,158 @@
}
}(window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#dropdowns
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ var $el = $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ $parent.trigger(e = $.Event('show.bs.dropdown'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown')
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index=0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus() {
+ $(backdrop).remove()
+ $(toggle).each(function (e) {
+ var $parent = getParent($(this))
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown'))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('dropdown')
+
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
\ No newline at end of file
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 static/style/src/less/library.less
--- a/static/style/src/less/library.less
+++ b/static/style/src/less/library.less
@@ -29,6 +29,50 @@
background-color: @table-bg-accent;
}
+
+tr.light td
+{
+ background-color: white;
+ color: black;
+}
+tr.light:hover td
+{
+ background-color: #f5f5f5;
+ color: #8389a1;
+}
+tr.dark td
+{
+ background-color: #8389a1;
+ color: white;
+}
+tr.dark:hover td
+{
+ background-color: #bbbfd0;
+ color: white;
+}
+a.dark:hover
+{
+ color: yellow;
+ // text-decoration: none;
+}
+a.dark
+{
+ color: white;
+ // text-decoration: none;
+}
+th.button_heading
+{
+ width: 7em;
+}
+div.library_breadcrumb{
+ padding-top: 0.8em;
+ padding-bottom: 0.8em;
+}
+div.library_breadcrumb a:hover{
+ color:green;
+}
+
+
img.expanderIcon {
padding-right: 4px;
}
https://bitbucket.org/galaxy/galaxy-central/commits/caba1617084b/
Changeset: caba1617084b
User: martenson
Date: 2013-12-11 20:24:58
Summary: cleaning zip logic, jmchilton pull rq #208
Affected #: 1 file
diff -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 -r caba1617084b4a0d38f1ebce970dda675755cd1d lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -21,42 +21,6 @@
import logging
log = logging.getLogger( __name__ )
-# Test for available compression types
-# tmpd = tempfile.mkdtemp()
-# comptypes = []
-# for comptype in ( 'gz', 'bz2' ):
-# tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype )
-# try:
-# archive = tarfile.open( tmpf, 'w:' + comptype )
-# archive.close()
-# comptypes.append( comptype )
-# except tarfile.CompressionError:
-# log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype )
-# try:
-# os.unlink( tmpf )
-# except OSError:
-# pass
-ziptype = '32'
-# tmpf = os.path.join( tmpd, 'compression_test.zip' )
-# try:
-# archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
-# archive.close()
-# comptypes.append( 'zip' )
-# ziptype = '64'
-# except RuntimeError:
-# log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." )
-# except (TypeError, zipfile.LargeZipFile):
-# # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped
-# log.warning( 'Max zip file size is 2GB, ZIP64 not supported' )
-# comptypes.append( 'zip' )
-# try:
-# os.unlink( tmpf )
-# except OSError:
-# pass
-# os.rmdir( tmpd )
-
-
-
class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
@web.expose_api
@@ -77,13 +41,13 @@
rval = "Error in dataset API at listing contents: " + str( e )
log.error( rval + ": %s" % str(e), exc_info=True )
trans.response.status = 500
-
+
rval['id'] = trans.security.encode_id(rval['id']);
rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
-
+
return rval
-
+
@web.expose
def download( self, trans, format, **kwd ):
"""
@@ -94,9 +58,9 @@
lddas = []
# is_admin = trans.user_is_admin()
# current_user_roles = trans.get_current_user_roles()
-
+
datasets_to_download = kwd['ldda_ids%5B%5D']
-
+
if ( datasets_to_download != None ):
datasets_to_download = util.listify( datasets_to_download )
for dataset_id in datasets_to_download:
@@ -107,7 +71,7 @@
except:
ldda = None
message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
-
+
if format in [ 'zip','tgz','tbz' ]:
error = False
killme = string.punctuation + string.whitespace
@@ -119,14 +83,10 @@
tmpd = tempfile.mkdtemp()
util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
tmpf = os.path.join( tmpd, 'library_download.' + format )
- if ziptype == '64' and trans.app.config.upstream_gzip:
+ if trans.app.config.upstream_gzip:
archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
- elif ziptype == '64':
+ else:
archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
- elif trans.app.config.upstream_gzip:
- archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED )
- else:
- archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED )
archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
elif format == 'tgz':
if trans.app.config.upstream_gzip:
@@ -238,4 +198,4 @@
except:
return 'This dataset contains no content'
else:
- return 'Wrong format';
\ No newline at end of file
+ return 'Wrong format';
https://bitbucket.org/galaxy/galaxy-central/commits/c653272fdbb3/
Changeset: c653272fdbb3
User: martenson
Date: 2013-12-11 21:31:20
Summary: trimming whitespace, import library.less into base.less
Affected #: 2 files
diff -r caba1617084b4a0d38f1ebce970dda675755cd1d -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -7,16 +7,6 @@
var library_router = null;
var responses = [];
-
-// load required libraries
-// require([
-// // load js libraries
-// 'utils/galaxy.css',
-// ], function(css){
-// // load css
-// css.load_file("static/style/library.css");
-// });
-
// dependencies
define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils"], function(mod_modal, mod_masthead, mod_utils) {
@@ -42,7 +32,7 @@
// FOLDER
var Folder = Backbone.Collection.extend({
- model: Item
+ model: Item
})
// CONTAINER for folder contents (folders, items and metadata).
@@ -78,7 +68,7 @@
});
//ROUTER
- var LibraryRouter = Backbone.Router.extend({
+ var LibraryRouter = Backbone.Router.extend({
routes: {
"" : "libraries",
"folders/:id" : "folder_content",
@@ -98,26 +88,26 @@
// progress percentage
progress: 0,
// progress rate per one item
- progressStep: 1,
+ progressStep: 1,
// last selected history in modal for UX
lastSelectedHistory: '',
// self modal
modal : null,
// loaded folders
folders : null,
-
+
// initialize
initialize : function(){
this.folders = [];
this.queue = jQuery.Deferred();
this.queue.resolve();
- },
+ },
// MMMMMMMMMMMMMMMMMM
// === TEMPLATES ====
// MMMMMMMMMMMMMMMMMM
- // set up
+ // set up
templateFolder : function (){
var tmpl_array = [];
@@ -127,12 +117,12 @@
// TOOLBAR
tmpl_array.push('<div id="library_folder_toolbar" >');
- tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-icon-plus"></span><span class="fa fa-icon-folder-close"></span> folder</button>');
- tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-icon-external-link"></span> to history</button>');
-
+ tmpl_array.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>');
+ tmpl_array.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>');
+
tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
- tmpl_array.push(' <span class="fa fa-icon-download"></span> download <span class="caret"></span>');
+ tmpl_array.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');
tmpl_array.push(' </button>');
tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
@@ -166,20 +156,20 @@
tmpl_array.push(' <th>date</th>');
tmpl_array.push(' </thead>');
tmpl_array.push(' <tbody>');
- tmpl_array.push(' <td></td>');
- tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
- tmpl_array.push(' <span class="fa fa-icon-arrow-up"></span> .. go up</td>');
- tmpl_array.push(' <td></td>');
- tmpl_array.push(' <td></td>');
- tmpl_array.push(' <td></td>');
- tmpl_array.push(' <td></td>');
- tmpl_array.push(' </tr>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
tmpl_array.push(' <% _.each(items, function(content_item) { %>');
tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
tmpl_array.push(' <td></td>');
tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');
- tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');
tmpl_array.push(' <td><%- content_item.get("name") %>');
tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
tmpl_array.push(' <span class="muted">(empty folder)</span>');
@@ -191,7 +181,7 @@
tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
tmpl_array.push(' <td>');
tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
- tmpl_array.push(' <span class="fa fa-icon-eye-open"></span> details');
+ tmpl_array.push(' <span class="fa fa-eye"></span> details');
tmpl_array.push(' </button>');
tmpl_array.push(' </td>');
tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
@@ -241,11 +231,11 @@
tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
tmpl_array.push(' </tr>');
tmpl_array.push(' <th scope="row">Comment Lines</th>');
- tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
- tmpl_array.push(' <% } else { %>');
- tmpl_array.push(' <td scope="row">unknown</td>');
- tmpl_array.push(' <% } %>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
tmpl_array.push(' </tr>');
tmpl_array.push(' <tr>');
tmpl_array.push(' <th scope="row">Number of Columns</th>');
@@ -280,7 +270,7 @@
tmpl_array.push('</span>');
return tmpl_array.join('');
- },
+ },
templateBulkImportInModal : function(){
var tmpl_array = [];
@@ -295,7 +285,7 @@
tmpl_array.push('</span>');
return tmpl_array.join('');
- },
+ },
// convert size to nice string
size_to_string : function (size)
@@ -380,12 +370,12 @@
showDatasetDetails : function(event){
// prevent default
event.preventDefault();
-
+
//TODO check whether we already have the data
//load the ID of the row
var id = $(event.target).parent().parent().attr('id');
-
+
//create new item
var item = new Item();
var histories = new GalaxyHistories();
@@ -474,12 +464,12 @@
self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
//enable the buttons
self.modal.enableButton('Import');
- self.modal.enableButton('Download');
+ self.modal.enableButton('Download');
}
});
},
- // select all datasets
+ // select all datasets
selectAll : function (event) {
var selected = event.target.checked;
// Iterate each checkbox
@@ -562,7 +552,7 @@
});
var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
$(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
-
+
// init the progress bar
var progressStep = 100 / dataset_ids.length;
this.initProgress(progressStep);
@@ -610,7 +600,7 @@
$('.completion_span').text(txt_representation);
},
- // progress bar
+ // progress bar
templateProgressBar : function (){
var tmpl_array = [];
@@ -644,14 +634,14 @@
// create hidden form and submit through POST to initialize download
processDownload: function(url, data, method){
//url and data options required
- if( url && data ){
+ if( url && data ){
//data can be string of parameters or array/object
data = typeof data == 'string' ? data : $.param(data);
//split params into form inputs
var inputs = '';
- $.each(data.split('&'), function(){
+ $.each(data.split('&'), function(){
var pair = this.split('=');
- inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
});
//send request
$('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
@@ -676,7 +666,7 @@
// initialize
initialize : function(){
- },
+ },
// template
template_library_list : function (){
@@ -685,7 +675,7 @@
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 icon-file ">New Library</a>');
+ 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>');
@@ -698,7 +688,7 @@
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-icon-folder-open"></span> browse</td>');
+ 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>');
@@ -707,7 +697,7 @@
tmpl_array.push(' <% }); %>');
tmpl_array.push(' </tbody>');
tmpl_array.push('</table>');
-
+
tmpl_array.push('</div>');
return tmpl_array.join('');
},
@@ -735,7 +725,7 @@
show_library_modal : function (event){
event.preventDefault();
event.stopPropagation();
-
+
// create modal
var self = this;
this.modal = new mod_modal.GalaxyModal(
@@ -747,7 +737,7 @@
'Close' : function() {self.modal.hide()}
}
});
-
+
// show prepared modal
this.modal.show();
},
@@ -828,7 +818,7 @@
library_router.on('route:folder_content', function(id) {
// render folder's contents
folderContentView.render({id: id});
- });
+ });
library_router.on('route:download', function(folder_id, format) {
// send download stream
@@ -844,9 +834,9 @@
library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
}
- });
+ });
-Backbone.history.start();
+Backbone.history.start();
return this
}
diff -r caba1617084b4a0d38f1ebce970dda675755cd1d -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a static/style/src/less/base.less
--- a/static/style/src/less/base.less
+++ b/static/style/src/less/base.less
@@ -23,6 +23,7 @@
// galaxy sub-components
@import "frame.less";
@import "upload.less";
+@import "library.less";
// Mixins
https://bitbucket.org/galaxy/galaxy-central/commits/267dcfd58a60/
Changeset: 267dcfd58a60
User: martenson
Date: 2013-12-12 20:21:09
Summary: added library notifications, toaster, can be used outside of libraries as require.js module
Affected #: 7 files
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -8,7 +8,7 @@
var responses = [];
// dependencies
-define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils"], function(mod_modal, mod_masthead, mod_utils) {
+define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils", "libs/toastr"], function(mod_modal, mod_masthead, mod_utils, mod_toastr) {
// MMMMMMMMMMMMMMM
// === Models ====
@@ -706,18 +706,35 @@
render: function () {
//hack to show scrollbars
$("#center").css('overflow','auto');
+
var that = this;
- // if (typeof libraries === "undefined") {
- libraries = new Libraries();
- // }
+ libraries = new Libraries();
+
libraries.fetch({
success: function (libraries) {
var template = _.template(that.template_library_list(), { libraries : libraries.models });
that.$el.html(template);
- }
+ },
+ error: function(model, response){
+ if (response.statusCode().status === 403){
+ //show notification
+ // var toast = mod_toastr;
+ mod_toastr.error('Please log in first. Redirecting to login page in 3s.');
+ setTimeout(that.redirectToLogin, 3000);
+ } else {
+ library_router.navigate('#', {trigger: true, replace: true});
+ }
+ }
})
},
+ redirectToSplash: function(){
+ window.location = '../';
+ },
+ redirectToLogin: function(){
+ window.location = '/user/login';
+ },
+
// own modal
modal : null,
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -172,10 +172,7 @@
// show notification
showNotification : function(message, duration, type) {
- // defaults
- var duration = typeof duration !== 'undefined' ? duration : 1500;
- // var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
- // var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ var duration = typeof duration !== 'undefined' ? duration : 3000;
var bgColor;
var txtColor;
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/scripts/libs/toastr.js
--- /dev/null
+++ b/static/scripts/libs/toastr.js
@@ -0,0 +1,307 @@
+/*
+ * Toastr
+ * Version 2.0.1
+ * Copyright 2012 John Papa and Hans Fjällemark.
+ * All Rights Reserved.
+ * Use, reproduction, distribution, and modification of this code is subject to the terms and
+ * conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
+ *
+ * Author: John Papa and Hans Fjällemark
+ * Project: https://github.com/CodeSeven/toastr
+ */
+; (function (define) {
+ define([], function () {
+ var $ = jQuery;
+ return (function () {
+ var version = '2.0.1';
+ var $container;
+ var listener;
+ var toastId = 0;
+ var toastType = {
+ error: 'error',
+ info: 'info',
+ success: 'success',
+ warning: 'warning'
+ };
+
+ var toastr = {
+ clear: clear,
+ error: error,
+ getContainer: getContainer,
+ info: info,
+ options: {},
+ subscribe: subscribe,
+ success: success,
+ version: version,
+ warning: warning
+ };
+
+ return toastr;
+
+ //#region Accessible Methods
+ function error(message, title, optionsOverride) {
+ return notify({
+ type: toastType.error,
+ iconClass: getOptions().iconClasses.error,
+ message: message,
+ optionsOverride: optionsOverride,
+ title: title
+ });
+ }
+
+ function info(message, title, optionsOverride) {
+ return notify({
+ type: toastType.info,
+ iconClass: getOptions().iconClasses.info,
+ message: message,
+ optionsOverride: optionsOverride,
+ title: title
+ });
+ }
+
+ function subscribe(callback) {
+ listener = callback;
+ }
+
+ function success(message, title, optionsOverride) {
+ return notify({
+ type: toastType.success,
+ iconClass: getOptions().iconClasses.success,
+ message: message,
+ optionsOverride: optionsOverride,
+ title: title
+ });
+ }
+
+ function warning(message, title, optionsOverride) {
+ return notify({
+ type: toastType.warning,
+ iconClass: getOptions().iconClasses.warning,
+ message: message,
+ optionsOverride: optionsOverride,
+ title: title
+ });
+ }
+
+ function clear($toastElement) {
+ var options = getOptions();
+ if (!$container) { getContainer(options); }
+ if ($toastElement && $(':focus', $toastElement).length === 0) {
+ $toastElement[options.hideMethod]({
+ duration: options.hideDuration,
+ easing: options.hideEasing,
+ complete: function () { removeToast($toastElement); }
+ });
+ return;
+ }
+ if ($container.children().length) {
+ $container[options.hideMethod]({
+ duration: options.hideDuration,
+ easing: options.hideEasing,
+ complete: function () { $container.remove(); }
+ });
+ }
+ }
+ //#endregion
+
+ //#region Internal Methods
+
+ function getDefaults() {
+ return {
+ tapToDismiss: true,
+ toastClass: 'toast',
+ containerId: 'toast-container',
+ debug: false,
+
+ showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery
+ showDuration: 300,
+ showEasing: 'swing', //swing and linear are built into jQuery
+ onShown: undefined,
+ hideMethod: 'fadeOut',
+ hideDuration: 1000,
+ hideEasing: 'swing',
+ onHidden: undefined,
+
+ extendedTimeOut: 1000,
+ iconClasses: {
+ error: 'toast-error',
+ info: 'toast-info',
+ success: 'toast-success',
+ warning: 'toast-warning'
+ },
+ iconClass: 'toast-info',
+ positionClass: 'toast-top-right',
+ timeOut: 5000, // Set timeOut and extendedTimeout to 0 to make it sticky
+ titleClass: 'toast-title',
+ messageClass: 'toast-message',
+ target: 'body',
+ closeHtml: '<button>×</button>',
+ newestOnTop: true
+ };
+ }
+
+ function publish(args) {
+ if (!listener) {
+ return;
+ }
+ listener(args);
+ }
+
+ function notify(map) {
+ var
+ options = getOptions(),
+ iconClass = map.iconClass || options.iconClass;
+
+ if (typeof (map.optionsOverride) !== 'undefined') {
+ options = $.extend(options, map.optionsOverride);
+ iconClass = map.optionsOverride.iconClass || iconClass;
+ }
+
+ toastId++;
+
+ $container = getContainer(options);
+ var
+ intervalId = null,
+ $toastElement = $('<div/>'),
+ $titleElement = $('<div/>'),
+ $messageElement = $('<div/>'),
+ $closeElement = $(options.closeHtml),
+ response = {
+ toastId: toastId,
+ state: 'visible',
+ startTime: new Date(),
+ options: options,
+ map: map
+ };
+
+ if (map.iconClass) {
+ $toastElement.addClass(options.toastClass).addClass(iconClass);
+ }
+
+ if (map.title) {
+ $titleElement.append(map.title).addClass(options.titleClass);
+ $toastElement.append($titleElement);
+ }
+
+ if (map.message) {
+ $messageElement.append(map.message).addClass(options.messageClass);
+ $toastElement.append($messageElement);
+ }
+
+ if (options.closeButton) {
+ $closeElement.addClass('toast-close-button');
+ $toastElement.prepend($closeElement);
+ }
+
+ $toastElement.hide();
+ if (options.newestOnTop) {
+ $container.prepend($toastElement);
+ } else {
+ $container.append($toastElement);
+ }
+
+
+ $toastElement[options.showMethod](
+ { duration: options.showDuration, easing: options.showEasing, complete: options.onShown }
+ );
+ if (options.timeOut > 0) {
+ intervalId = setTimeout(hideToast, options.timeOut);
+ }
+
+ $toastElement.hover(stickAround, delayedhideToast);
+ if (!options.onclick && options.tapToDismiss) {
+ $toastElement.click(hideToast);
+ }
+ if (options.closeButton && $closeElement) {
+ $closeElement.click(function (event) {
+ event.stopPropagation();
+ hideToast(true);
+ });
+ }
+
+ if (options.onclick) {
+ $toastElement.click(function () {
+ options.onclick();
+ hideToast();
+ });
+ }
+
+ publish(response);
+
+ if (options.debug && console) {
+ console.log(response);
+ }
+
+ return $toastElement;
+
+ function hideToast(override) {
+ if ($(':focus', $toastElement).length && !override) {
+ return;
+ }
+ return $toastElement[options.hideMethod]({
+ duration: options.hideDuration,
+ easing: options.hideEasing,
+ complete: function () {
+ removeToast($toastElement);
+ if (options.onHidden) {
+ options.onHidden();
+ }
+ response.state = 'hidden';
+ response.endTime = new Date(),
+ publish(response);
+ }
+ });
+ }
+
+ function delayedhideToast() {
+ if (options.timeOut > 0 || options.extendedTimeOut > 0) {
+ intervalId = setTimeout(hideToast, options.extendedTimeOut);
+ }
+ }
+
+ function stickAround() {
+ clearTimeout(intervalId);
+ $toastElement.stop(true, true)[options.showMethod](
+ { duration: options.showDuration, easing: options.showEasing }
+ );
+ }
+ }
+ function getContainer(options) {
+ if (!options) { options = getOptions(); }
+ $container = $('#' + options.containerId);
+ if ($container.length) {
+ return $container;
+ }
+ $container = $('<div/>')
+ .attr('id', options.containerId)
+ .addClass(options.positionClass);
+ $container.appendTo($(options.target));
+ return $container;
+ }
+
+ function getOptions() {
+ return $.extend({}, getDefaults(), toastr.options);
+ }
+
+ function removeToast($toastElement) {
+ if (!$container) { $container = getContainer(); }
+ if ($toastElement.is(':visible')) {
+ return;
+ }
+ $toastElement.remove();
+ $toastElement = null;
+ if ($container.children().length === 0) {
+ $container.remove();
+ }
+ }
+ //#endregion
+
+ })();
+ });
+}(typeof define === 'function' && define.amd ? define : function (deps, factory) {
+ if (typeof module !== 'undefined' && module.exports) { //Node
+ module.exports = factory(require(deps[0]));
+ } else {
+ window['toastr'] = factory(window['jQuery']);
+ }
+}));
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/scripts/utils/galaxy.uploadbox.js
--- a/static/scripts/utils/galaxy.uploadbox.js
+++ b/static/scripts/utils/galaxy.uploadbox.js
@@ -352,4 +352,4 @@
'compatible' : compatible
};
}
-})(jQuery);
\ No newline at end of file
+})(jQuery);
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster' ];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster', 'toastr' ];
var _ = grunt.util._;
var fmt = _.sprintf;
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/style/src/less/base.less
--- a/static/style/src/less/base.less
+++ b/static/style/src/less/base.less
@@ -24,6 +24,7 @@
@import "frame.less";
@import "upload.less";
@import "library.less";
+@import "toastr.less";
// Mixins
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/style/src/less/toastr.less
--- /dev/null
+++ b/static/style/src/less/toastr.less
@@ -0,0 +1,232 @@
+// Mix-ins
+.borderRadius(@radius) {
+ -moz-border-radius: @radius;
+ -webkit-border-radius: @radius;
+ border-radius: @radius;
+}
+
+.boxShadow(@boxShadow) {
+ -moz-box-shadow: @boxShadow;
+ -webkit-box-shadow: @boxShadow;
+ box-shadow: @boxShadow;
+}
+
+.opacity(@opacity) {
+ @opacityPercent: @opacity * 100;
+ opacity: @opacity;
+ -ms-filter: ~"progid:DXImageTransform.Microsoft.Alpha(Opacity=@{opacityPercent})";
+ filter: ~"alpha(opacity=@{opacityPercent})";
+}
+
+.wordWrap(@wordWrap: break-word) {
+ -ms-word-wrap: @wordWrap;
+ word-wrap: @wordWrap;
+}
+
+// Variables
+@black: #000000;
+@grey: #999999;
+@light-grey: #CCCCCC;
+@white: #FFFFFF;
+@near-black: #030303;
+@green: #51A351;
+@red: #BD362F;
+@blue: #2F96B4;
+@orange: #F89406;
+
+// Styles
+.toast-title {
+ font-weight: bold;
+}
+
+.toast-message {
+ .wordWrap();
+
+ a,
+ label {
+ color: @white;
+ }
+
+ a:hover {
+ color: @light-grey;
+ text-decoration: none;
+ }
+}
+
+.toast-close-button {
+ position: relative;
+ right: -0.3em;
+ top: -0.3em;
+ float: right;
+ font-size: 20px;
+ font-weight: bold;
+ color: @white;
+ -webkit-text-shadow: 0 1px 0 rgba(255,255,255,1);
+ text-shadow: 0 1px 0 rgba(255,255,255,1);
+ .opacity(0.8);
+
+ &:hover,
+ &:focus {
+ color: @black;
+ text-decoration: none;
+ cursor: pointer;
+ .opacity(0.4);
+ }
+}
+
+/*Additional properties for button version
+ iOS requires the button element instead of an anchor tag.
+ If you want the anchor version, it requires `href="#"`.*/
+button.toast-close-button {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+}
+
+//#endregion
+
+.toast-top-full-width {
+ top: 0;
+ right: 0;
+ width: 100%;
+}
+
+.toast-bottom-full-width {
+ bottom: 0;
+ right: 0;
+ width: 100%;
+}
+
+.toast-top-left {
+ top: 12px;
+ left: 12px;
+}
+
+.toast-top-right {
+ top: 12px;
+ right: 12px;
+}
+
+.toast-bottom-right {
+ right: 12px;
+ bottom: 12px;
+}
+
+.toast-bottom-left {
+ bottom: 12px;
+ left: 12px;
+}
+
+#toast-container {
+ position: fixed;
+ z-index: 999999;
+
+ * {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+
+ > div {
+ margin: 4em 0 6px;
+ padding: 15px 15px 15px 50px;
+ width: 300px;
+ .borderRadius(3px 3px 3px 3px);
+ background-position: 15px center;
+ background-repeat: no-repeat;
+ .boxShadow(0 0 12px @grey);
+ color: @white;
+ .opacity(0.8);
+ }
+
+ > :hover {
+ .boxShadow(0 0 12px @black);
+ .opacity(1);
+ cursor: pointer;
+ }
+
+ > .toast-info {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important;
+ }
+
+ > .toast-error {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important;
+ }
+
+ > .toast-success {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important;
+ }
+
+ > .toast-warning {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important;
+ }
+
+ /*overrides*/
+ &.toast-top-full-width > div,
+ &.toast-bottom-full-width > div {
+ width: 96%;
+ margin: auto;
+ }
+}
+
+.toast {
+ background-color: @near-black;
+}
+
+.toast-success {
+ background-color: @green;
+}
+
+.toast-error {
+ background-color: @red;
+}
+
+.toast-info {
+ background-color: @blue;
+}
+
+.toast-warning {
+ background-color: @orange;
+}
+
+/*Responsive Design*/
+
+@media all and (max-width: 240px) {
+ #toast-container {
+
+ > div {
+ padding: 8px 8px 8px 50px;
+ width: 11em;
+ }
+
+ & .toast-close-button {
+ right: -0.2em;
+ top: -0.2em;
+ }
+ }
+}
+
+@media all and (min-width: 241px) and (max-width: 480px) {
+ #toast-container {
+ > div {
+ padding: 8px 8px 8px 50px;
+ width: 18em;
+ }
+
+ & .toast-close-button {
+ right: -0.2em;
+ top: -0.2em;
+ }
+ }
+}
+
+@media all and (min-width: 481px) and (max-width: 768px) {
+ #toast-container {
+ > div {
+ padding: 15px 15px 15px 50px;
+ width: 25em;
+ }
+ }
+}
https://bitbucket.org/galaxy/galaxy-central/commits/9e7cd2749bde/
Changeset: 9e7cd2749bde
User: martenson
Date: 2013-12-13 20:33:06
Summary: rewrite of galaxy.library to use global notifications; removed previous notification engine from galaxy.modal.js;
Affected #: 3 files
diff -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b -r 9e7cd2749bdeb36bab9a7c883d95a877f9eaa2ca lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -144,38 +144,6 @@
parent_resources=dict( member_name='datatype', collection_name='datatypes' ) )
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
-
- # =======================
- # ===== LIBRARY API =====
- # =======================
-
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
-
- webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
- webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
-
- webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
-
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
@@ -195,6 +163,52 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ webapp.mapper.connect( 'show_lda_item',
+ '/api/libraries/datasets/:id',
+ controller='lda_datasets',
+ action='show',
+ conditions=dict( method=[ "GET" ] ) )
+
+ webapp.mapper.connect( 'download_lda_items',
+ '/api/libraries/datasets/download/:format',
+ controller='lda_datasets',
+ action='download',
+ conditions=dict( method=[ "POST", "GET" ] ) )
+
+ webapp.mapper.resource_with_deleted( 'library',
+ 'libraries',
+ path_prefix='/api' )
+ webapp.mapper.resource( 'folder',
+ 'folders',
+ path_prefix='/api' )
+
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
# ====================
# ===== TOOLSHED =====
diff -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b -r 9e7cd2749bdeb36bab9a7c883d95a877f9eaa2ca static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -8,7 +8,11 @@
var responses = [];
// dependencies
-define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils", "libs/toastr"], function(mod_modal, mod_masthead, mod_utils, mod_toastr) {
+define([
+ "galaxy.modal",
+ "galaxy.masthead",
+ "utils/galaxy.utils",
+ "libs/toastr"], function(mod_modal, mod_masthead, mod_utils, mod_toastr) {
// MMMMMMMMMMMMMMM
// === Models ====
@@ -308,7 +312,7 @@
// event binding
events: {
'click #select-all-checkboxes' : 'selectAll',
- 'click .folder_row' : 'selectClicked',
+ 'click .folder_row' : 'selectClickedRow',
'click #toolbtn_bulk_import' : 'modalBulkImport',
'click #toolbtn_dl' : 'bulkDownload',
'click .library-dataset' : 'showDatasetDetails',
@@ -350,6 +354,7 @@
var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
// var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
that.$el.html(template);
+
}
})
},
@@ -456,12 +461,14 @@
// save the dataset into selected history
historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
- self.modal.showNotification('Dataset imported', 3000, 'success');
+ mod_toastr.success('Dataset imported');
+ // self.modal.showNotification('Dataset imported', 3000, 'success');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
}, error : function(){
- self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
+ mod_toastr.error('An error occured! Dataset not imported. Please try again.')
+ // self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
@@ -471,37 +478,69 @@
// select all datasets
selectAll : function (event) {
- var selected = event.target.checked;
- // Iterate each checkbox
- $(':checkbox').each(function () { this.checked = selected; });
- this.showTools();
- },
+ var selected = event.target.checked;
+ that = this;
+ // Iterate each checkbox
+ $(':checkbox').each(function () {
+ this.checked = selected;
+ $row = $(this.parentElement.parentElement);
+ // Change color of selected/unselected
+ (selected) ? that.makeDarkRow($row) : that.makeWhiteRow($row);
+ });
+ // Show the tools in menu
+ this.checkTools();
+ },
- // click checkbox on folder click
- selectClicked : function (event) {
- var checkbox = $("#" + event.target.parentElement.id).find(':checkbox')
- if (checkbox[0] != undefined) {
- if (checkbox[0].checked){
- checkbox[0].checked = '';
- // $(event.target.parentElement).css('background-color', '').css('color', '');
- $(event.target.parentElement).removeClass('dark');
- $(event.target.parentElement).find('a').removeClass('dark');
- $(event.target.parentElement).addClass('light');
- $(event.target.parentElement).find('a').addClass('light');
+ // Check checkbox on row itself or row checkbox click
+ selectClickedRow : function (event) {
+ var checkbox = '';
+ var $row;
+ var source;
+ if (event.target.localName === 'input'){
+ checkbox = event.target;
+ $row = $(event.target.parentElement.parentElement);
+ source = 'input';
+ } else if (event.target.localName === 'td') {
+ checkbox = $("#" + event.target.parentElement.id).find(':checkbox')[0];
+ $row = $(event.target.parentElement);
+ source = 'td';
+ }
+ if (checkbox === '') {event.stopPropagation(); return;} // button in row was clicked
+
+ if (checkbox.checked){
+ if (source==='td'){
+ checkbox.checked = '';
+ this.makeWhiteRow($row);
+ } else if (source==='input') {
+ this.makeDarkRow($row);
+ }
} else {
- checkbox[0].checked = 'selected';
- $(event.target.parentElement).removeClass('light');
- $(event.target.parentElement).find('a').removeClass('light');
- $(event.target.parentElement).addClass('dark');
- $(event.target.parentElement).find('a').addClass('dark');
- // $(event.target.parentElement).css('background-color', '#8389a1').css('color', 'white');
+ if (source==='td'){
+ checkbox.checked = 'selected';
+ this.makeDarkRow($row);
+ } else if (source==='input') {
+ this.makeWhiteRow($row);
+ }
}
- }
- this.showTools();
+ this.checkTools();
+ },
+
+ makeDarkRow: function($row){
+ $row.removeClass('light');
+ $row.find('a').removeClass('light');
+ $row.addClass('dark');
+ $row.find('a').addClass('dark');
+ },
+
+ makeWhiteRow: function($row){
+ $row.removeClass('dark');
+ $row.find('a').removeClass('dark');
+ $row.addClass('light');
+ $row.find('a').addClass('light');
},
// show toolbar in case something is selected
- showTools : function(){
+ checkTools : function(){
var checkedValues = $('#folder_table').find(':checked');
if(checkedValues.length > 0){
$('#toolbtn_bulk_import').show();
@@ -577,7 +616,9 @@
var self = this;
var popped_item = history_item_set.pop();
if (typeof popped_item === "undefined") {
- self.modal.showNotification('All datasets imported', 3000, 'success');
+ mod_toastr.success('All datasets imported');
+ this.modal.hide();
+ // self.modal.showNotification('All datasets imported', 3000, 'success');
// enable button again
self.modal.enableButton('Import');
return
@@ -646,12 +687,13 @@
//send request
$('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
.appendTo('body').submit().remove();
+ mod_toastr.info('Your download will begin soon');
};
},
// shows modal for creating folder
createFolderModal: function(){
- alert('creating folder');
+ mod_toastr.info('This will create folder...in the future');
}
});
@@ -704,7 +746,7 @@
// render
render: function () {
- //hack to show scrollbars
+ //hack to show scrollbars due to #center element inheritance
$("#center").css('overflow','auto');
var that = this;
@@ -717,18 +759,16 @@
},
error: function(model, response){
if (response.statusCode().status === 403){
- //show notification
- // var toast = mod_toastr;
mod_toastr.error('Please log in first. Redirecting to login page in 3s.');
setTimeout(that.redirectToLogin, 3000);
} else {
- library_router.navigate('#', {trigger: true, replace: true});
+ mod_toastr.error('An error occured. Please try again.');
}
}
})
},
- redirectToSplash: function(){
+ redirectToHome: function(){
window.location = '../';
},
redirectToLogin: function(){
@@ -762,19 +802,23 @@
// create the new library from modal
create_new_library_event: function(){
var libraryDetails = this.serialize_new_library();
- var valid = this.validate_new_library(libraryDetails);
- var library = new Library();
- var self = this;
- library.save(libraryDetails, {
- success: function (library) {
- self.modal.hide();
- self.clear_library_modal();
- self.render();
- },
- error: function(){
- self.modal.showNotification('An error occured', 5000, 'error');
- }
- });
+ if (this.validate_new_library(libraryDetails)){
+ var library = new Library();
+ var self = this;
+ library.save(libraryDetails, {
+ success: function (library) {
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ mod_toastr.success('Library created');
+ },
+ error: function(){
+ mod_toastr.error('An error occured :(');
+ }
+ });
+ } else {
+ mod_toastr.error('Library\'s name is missing');
+ }
return false;
},
@@ -794,14 +838,13 @@
};
},
- validate_new_library: function(library){
-
+ // validate new library info
+ validate_new_library: function(libraryDetails){
+ return libraryDetails.name !== '';
},
-
// template for new library modal
- template_new_library: function()
- {
+ template_new_library: function(){
tmpl_array = [];
tmpl_array.push('<div id="new_library_modal">');
@@ -824,34 +867,29 @@
folderContentView = new FolderContentView();
galaxyLibraryview = new GalaxyLibraryview();
-
library_router = new LibraryRouter();
library_router.on('route:libraries', function() {
// render libraries list
galaxyLibraryview.render();
- });
+ });
library_router.on('route:folder_content', function(id) {
// render folder's contents
folderContentView.render({id: id});
- });
+ });
library_router.on('route:download', function(folder_id, format) {
- // send download stream
- if (typeof folderContentView === 'undefined'){
- alert('you cant touch this!');
- // } else if (folderContentView.modal !== null){
- // folderContentView.download(folder_id, format);
- } else if ($('#center').find(':checked').length === 0) { // coming from outside of the library app
+ if ($('#center').find(':checked').length === 0) {
+ // this happens rarely when there is a server/data error and client gets an actual response instead of an attachment
+ // we don't know what was selected so we can't download again, we redirect to the folder provided
library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
- // TODO show message of redirection
} else {
+ // send download stream
folderContentView.download(folder_id, format);
library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
}
-
- });
+ });
Backbone.history.start();
diff -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b -r 9e7cd2749bdeb36bab9a7c883d95a877f9eaa2ca static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -26,16 +26,17 @@
if (options)
this.create(options);
- this.bindClick(event, self);
+ // Bind the hiding events
+ this.bindEvents(event, self);
},
// bind the click-to-hide function
- bindClick: function(event, that) {
+ bindEvents: function(event, that) {
// bind the ESC key to hide() function
$(document).on('keyup', function(event){
if (event.keyCode == 27) { self.hide(); }
})
- // bind the click anywhere to hide() function...
+ // bind the 'click anywhere' to hide() function...
$('html').on('click', function(event){
that.hide();
})
@@ -46,26 +47,24 @@
},
// unbind the click-to-hide function
- unbindClick: function(event, that){
+ unbindEvents: function(event, that){
// bind the ESC key to hide() function
$(document).off('keyup', function(event){
if (event.keyCode == 27) { that.hide(); }
})
- // unbind the click anywhere to hide() function...
+ // unbind the 'click anywhere' to hide() function...
$('html').off('click', function(event){
that.hide();
})
- // ...but don't hide if the click is on modal content
$('.modal-content').off('click', function(event){
event.stopPropagation();
})
},
-
// destroy
destroy : function(){
this.hide();
- this.unbindClick();
+ this.unbindEvents();
$('.modal').remove();
},
@@ -103,6 +102,7 @@
// set flag
this.visible = false;
+ this.unbindEvents();
},
// create
@@ -127,7 +127,6 @@
this.$footer = (this.$el).find('.modal-footer');
this.$buttons = (this.$el).find('.buttons');
this.$backdrop = (this.$el).find('.modal-backdrop');
- this.$notification = (this.$el).find('.notification-modal');
// append body
this.$body.html(this.options.body);
@@ -170,35 +169,6 @@
this.$buttons.find('#' + String(name).toLowerCase()).show();
},
- // show notification
- showNotification : function(message, duration, type) {
- var duration = typeof duration !== 'undefined' ? duration : 3000;
- var bgColor;
- var txtColor;
-
- if (type === 'error'){
- bgColor = '#f4e0e1';
- txtColor = '#a42732';
- // } else if (type === 'success'){
- } else { // success is default
- bgColor = '#e1f4e0';
- txtColor = '#32a427';
- }
-
- var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
- this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
-
- var self = this;
-
- /*animate the bar*/
- $('#notification-bar').slideDown(function() {
- setTimeout(function() {
- $('#notification-bar').slideUp(function() {self.$notification.html('');});
- }, duration);
- });
-
- },
-
// returns scroll top for body element
scrollTop: function()
{
@@ -218,7 +188,6 @@
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
- '<span class="notification-modal"></span>' +
'</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
https://bitbucket.org/galaxy/galaxy-central/commits/0522f7504094/
Changeset: 0522f7504094
User: martenson
Date: 2013-12-13 21:05:26
Summary: switch download to GET, removed toaster from grunt file
Affected #: 2 files
diff -r 9e7cd2749bdeb36bab9a7c883d95a877f9eaa2ca -r 0522f7504094bd5be2be97846e695a0c790bf929 static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -462,13 +462,11 @@
// save the dataset into selected history
historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
mod_toastr.success('Dataset imported');
- // self.modal.showNotification('Dataset imported', 3000, 'success');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
}, error : function(){
mod_toastr.error('An error occured! Dataset not imported. Please try again.')
- // self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
@@ -618,7 +616,6 @@
if (typeof popped_item === "undefined") {
mod_toastr.success('All datasets imported');
this.modal.hide();
- // self.modal.showNotification('All datasets imported', 3000, 'success');
// enable button again
self.modal.enableButton('Import');
return
@@ -669,7 +666,7 @@
var url = '/api/libraries/datasets/download/' + format;
var data = {'ldda_ids' : dataset_ids};
- this.processDownload(url, data);
+ this.processDownload(url, data, 'get');
},
// create hidden form and submit through POST to initialize download
diff -r 9e7cd2749bdeb36bab9a7c883d95a877f9eaa2ca -r 0522f7504094bd5be2be97846e695a0c790bf929 static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster', 'toastr' ];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster'];
var _ = grunt.util._;
var fmt = _.sprintf;
https://bitbucket.org/galaxy/galaxy-central/commits/5463295a3981/
Changeset: 5463295a3981
User: martenson
Date: 2013-12-16 00:26:37
Summary: Merged galaxy/galaxy-central into default
Affected #: 139 files
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a 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,92 +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
- 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 )
- # 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 )
- 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 )
- # 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,
- map_install_models=combined_install_database,
- 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 )
- 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 )
# 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 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -473,6 +473,7 @@
rval[ key ] = value
return rval
+
def configure_logging( config ):
"""
Allow some basic logging configuration to be read from ini file.
@@ -513,3 +514,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 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/datatypes/registry.py
--- a/lib/galaxy/datatypes/registry.py
+++ b/lib/galaxy/datatypes/registry.py
@@ -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 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -688,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
@@ -1435,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 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a 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 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -198,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.
@@ -212,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 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a 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 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/jobs/runners/lwr.py
--- a/lib/galaxy/jobs/runners/lwr.py
+++ b/lib/galaxy/jobs/runners/lwr.py
@@ -204,7 +204,7 @@
log.exception("failure running job %d" % job_wrapper.job_id)
return
if not LwrJobRunner.__remote_metadata( client ):
- self._handle_metadata_externally( job_wrapper )
+ self._handle_metadata_externally( job_wrapper, resolve_requirements=True )
# Finish the job
try:
job_wrapper.finish( stdout, stderr )
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/model/tool_shed_install/__init__.py
--- a/lib/galaxy/model/tool_shed_install/__init__.py
+++ b/lib/galaxy/model/tool_shed_install/__init__.py
@@ -1,11 +1,11 @@
+import logging
import os
-
from galaxy.model.item_attrs import Dictifiable
-
from galaxy.util.bunch import Bunch
from galaxy.util import asbool
+from tool_shed.util import common_util
-from tool_shed.util import common_util
+log = logging.getLogger( __name__ )
class ToolShedRepository( object ):
@@ -61,9 +61,53 @@
def can_reset_metadata( self ):
return self.status == self.installation_status.INSTALLED
- @property
- def can_uninstall( self ):
- return self.status != self.installation_status.UNINSTALLED
+ def can_uninstall( self, app ):
+ # An installed repository cannot be uninstalled if other installed repositories or installed repository
+ # contents (i.e., tool dependencies) require it.
+ if self.status == self.installation_status.UNINSTALLED:
+ return False
+ this_repository_tup = ( str( self.tool_shed ),
+ str( self.name ),
+ str( self.owner ),
+ str( self.installed_changeset_revision ) )
+ irm = app.installed_repository_manager
+ # See if this repository's current dependencies are restricted to a single circular relationship. This
+ # means that this repository has a single repository dependency which itself depends upon this repository.
+ # The repository dependency may have other repository dependencies, but that is not relevant here.
+ single_repository_dependency_tup = None
+ installed_repository_dependency_tups = \
+ irm.installed_repository_dependencies_of_installed_repositories.get( this_repository_tup, [] )
+ # If this repository defines a circular relationship to another repository, then the list of tuples
+ # defining installed repository dependencies will include itself.
+ if len( installed_repository_dependency_tups ) == 2:
+ if this_repository_tup in installed_repository_dependency_tups:
+ # We have a single circular dependency definition, so get the other repository.
+ for installed_repository_dependency_tup in installed_repository_dependency_tups:
+ if installed_repository_dependency_tup != this_repository_tup:
+ single_repository_dependency_tup = installed_repository_dependency_tup
+ break
+ if single_repository_dependency_tup is not None:
+ installed_repository_dependency_tups = \
+ irm.installed_repository_dependencies_of_installed_repositories.get( this_repository_tup, [] )
+ if this_repository_tup in installed_repository_dependency_tups:
+ # This repository is a dependency of the single repository upon which it depends, so we have
+ # a single circular relationship and this repository can be uninstalled.
+ return True
+ # Find other installed repositories that require this repository.
+ installed_dependent_repositories = \
+ irm.installed_dependent_repositories_of_installed_repositories.get( this_repository_tup, [] )
+ if installed_dependent_repositories:
+ # This repository cannot be uninstalled because other installed repositories require it.
+ return False
+ # Find installed tool dependencies that require this repository's installed tool dependencies.
+ installed_tool_dependencies = irm.installed_tool_dependencies_of_installed_repositories.get( this_repository_tup, [] )
+ for td_tup in installed_tool_dependencies:
+ installed_dependent_td_tups = irm.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.get( td_tup, [] )
+ if installed_dependent_td_tups:
+ # This repository cannot be uninstalled because it contains installed tool dependencies that
+ # are required at run time by other installed tool dependencies.
+ return False
+ return True
@property
def can_deactivate( self ):
@@ -218,7 +262,7 @@
@property
def installed_tool_dependencies( self ):
- """Return the repository's tool dependencies that are currently installed."""
+ """Return the repository's tool dependencies that are currently installed, but possibly in an error state."""
installed_dependencies = []
for tool_dependency in self.tool_dependencies:
if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED,
@@ -313,9 +357,9 @@
required_repositories_missing_or_being_installed = []
for required_repository in self.repository_dependencies:
if required_repository.status in [ self.installation_status.ERROR,
- self.installation_status.INSTALLING,
- self.installation_status.NEVER_INSTALLED,
- self.installation_status.UNINSTALLED ]:
+ self.installation_status.INSTALLING,
+ self.installation_status.NEVER_INSTALLED,
+ self.installation_status.UNINSTALLED ]:
required_repositories_missing_or_being_installed.append( required_repository )
return required_repositories_missing_or_being_installed
@@ -507,7 +551,9 @@
@property
def can_uninstall( self ):
- return self.status in [ self.installation_status.ERROR, self.installation_status.INSTALLED ]
+ # A tool dependency can be uninstalled only if it is currently in an error state. Only the containing
+ # repository can be uninstalled if a tool dependency is properly installed.
+ return self.status in [ self.installation_status.ERROR ]
@property
def can_update( self ):
@@ -516,6 +562,13 @@
self.installation_status.ERROR,
self.installation_status.UNINSTALLED ]
+ def get_env_shell_file_path( self, app ):
+ installation_directory = self.installation_directory( app )
+ file_path = os.path.join( installation_directory, 'env.sh' )
+ if os.path.exists( file_path ):
+ return file_path
+ return None
+
@property
def in_error_state( self ):
return self.status == self.installation_status.ERROR
@@ -536,6 +589,10 @@
self.tool_shed_repository.name,
self.tool_shed_repository.installed_changeset_revision )
+ @property
+ def is_installed( self ):
+ return self.status == self.installation_status.INSTALLED
+
class ToolVersion( object, Dictifiable ):
dict_element_visible_keys = ( 'id', 'tool_shed_repository' )
@@ -549,23 +606,23 @@
def get_previous_version( self, app ):
context = app.install_model.context
tva = context.query( app.install_model.ToolVersionAssociation ) \
- .filter( app.install_model.ToolVersionAssociation.table.c.tool_id == self.id ) \
- .first()
+ .filter( app.install_model.ToolVersionAssociation.table.c.tool_id == self.id ) \
+ .first()
if tva:
return context.query( app.install_model.ToolVersion ) \
- .filter( app.install_model.ToolVersion.table.c.id == tva.parent_id ) \
- .first()
+ .filter( app.install_model.ToolVersion.table.c.id == tva.parent_id ) \
+ .first()
return None
def get_next_version( self, app ):
context = app.install_model.context
tva = context.query( app.install_model.ToolVersionAssociation ) \
- .filter( app.install_model.ToolVersionAssociation.table.c.parent_id == self.id ) \
- .first()
+ .filter( app.install_model.ToolVersionAssociation.table.c.parent_id == self.id ) \
+ .first()
if tva:
return context.query( app.install_model.ToolVersion ) \
- .filter( app.install_model.ToolVersion.table.c.id == tva.tool_id ) \
- .first()
+ .filter( app.install_model.ToolVersion.table.c.id == tva.tool_id ) \
+ .first()
return None
def get_versions( self, app ):
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/model/tool_shed_install/mapping.py
--- a/lib/galaxy/model/tool_shed_install/mapping.py
+++ b/lib/galaxy/model/tool_shed_install/mapping.py
@@ -7,10 +7,8 @@
from galaxy.model.base import ModelMapping
from galaxy.model.orm.engine_factory import build_engine
-
metadata = MetaData()
-
install_model.ToolShedRepository.table = Table( "tool_shed_repository", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
@@ -72,7 +70,6 @@
Column( "repository_path", TEXT ),
Column( "version", Integer ) )
-
mapper( install_model.ToolShedRepository, install_model.ToolShedRepository.table,
properties=dict( tool_versions=relation( install_model.ToolVersion,
primaryjoin=( install_model.ToolShedRepository.table.c.id == install_model.ToolVersion.table.c.tool_shed_repository_id ),
@@ -112,17 +109,13 @@
"""Connect mappings to the database"""
# Load the appropriate db module
engine = build_engine( url, engine_options )
-
# Connect the metadata to the database.
metadata.bind = engine
-
- result = ModelMapping([install_model], engine=engine)
-
+ result = ModelMapping( [ install_model ], engine=engine )
# Create tables if needed
if create_tables:
metadata.create_all()
# metadata.engine.commit()
-
result.create_tables = create_tables
#load local galaxy security policy
return result
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -40,7 +40,7 @@
from galaxy.tools.actions import DefaultToolAction
from galaxy.tools.actions.data_source import DataSourceToolAction
from galaxy.tools.actions.data_manager import DataManagerToolAction
-from galaxy.tools.deps import DependencyManager, INDETERMINATE_DEPENDENCY
+from galaxy.tools.deps import build_dependency_manager
from galaxy.tools.deps.requirements import parse_requirements_from_xml
from galaxy.tools.parameters import check_param, params_from_strings, params_to_strings
from galaxy.tools.parameters.basic import (BaseURLToolParameter,
@@ -700,14 +700,7 @@
return stored.latest_workflow
def init_dependency_manager( self ):
- if self.app.config.use_tool_dependencies:
- dependency_manager_kwds = {
- 'default_base_path': self.app.config.tool_dependency_dir,
- 'conf_file': self.app.config.dependency_resolvers_config_file,
- }
- self.dependency_manager = DependencyManager( **dependency_manager_kwds )
- else:
- self.dependency_manager = None
+ self.dependency_manager = build_dependency_manager( self.app.config )
@property
def sa_session( self ):
@@ -1082,6 +1075,22 @@
:returns: galaxy.jobs.JobDestination -- The destination definition and runner parameters.
"""
return self.app.job_config.get_destination(self.__get_job_tool_configuration(job_params=job_params).destination)
+
+ def get_panel_section( self ):
+ for key, item in self.app.toolbox.integrated_tool_panel.items():
+ if item:
+ if key.startswith( 'tool_' ):
+ if item.id == self.id:
+ return '', ''
+ if key.startswith( 'section_' ):
+ section_id = item.id or ''
+ section_name = item.name or ''
+ for section_key, section_item in item.elems.items():
+ if section_key.startswith( 'tool_' ):
+ if section_item:
+ if section_item.id == self.id:
+ return section_id, section_name
+ return None, None
def parse( self, root, guid=None ):
"""
@@ -3017,6 +3026,8 @@
if io_details:
tool_dict[ 'inputs' ] = [ input.to_dict( trans ) for input in self.inputs.values() ]
tool_dict[ 'outputs' ] = [ output.to_dict() for output in self.outputs.values() ]
+
+ tool_dict[ 'panel_section_id' ], tool_dict[ 'panel_section_name' ] = self.get_panel_section()
return tool_dict
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/tools/deps/__init__.py
--- a/lib/galaxy/tools/deps/__init__.py
+++ b/lib/galaxy/tools/deps/__init__.py
@@ -15,6 +15,31 @@
from galaxy.util.submodules import submodules
+def build_dependency_manager( config ):
+ if config.use_tool_dependencies:
+ dependency_manager_kwds = {
+ 'default_base_path': config.tool_dependency_dir,
+ 'conf_file': config.dependency_resolvers_config_file,
+ }
+ dependency_manager = DependencyManager( **dependency_manager_kwds )
+ else:
+ dependency_manager = NullDependencyManager()
+
+ return dependency_manager
+
+
+class NullDependencyManager( object ):
+
+ def uses_tool_shed_dependencies(self):
+ return False
+
+ def dependency_shell_commands( self, requirements, **kwds ):
+ return []
+
+ def find_dep( self, name, version=None, type='package', **kwds ):
+ return INDETERMINATE_DEPENDENCY
+
+
class DependencyManager( object ):
"""
A DependencyManager attempts to resolve named and versioned dependencies by
@@ -76,6 +101,7 @@
return [
ToolShedPackageDependencyResolver(self),
GalaxyPackageDependencyResolver(self),
+ GalaxyPackageDependencyResolver(self, versionless=True),
]
def __parse_resolver_conf_xml(self, root):
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -265,6 +265,9 @@
if remove_from_disk_checked:
tool_shed_repository.status = trans.install_model.ToolShedRepository.installation_status.UNINSTALLED
tool_shed_repository.error_message = None
+ # Remove the uninstalled repository and any tool dependencies from the in-memory dictionaries in the
+ # installed_repository_manager.
+ trans.app.installed_repository_manager.handle_repository_uninstall( tool_shed_repository )
else:
tool_shed_repository.status = trans.install_model.ToolShedRepository.installation_status.DEACTIVATED
trans.install_model.context.add( tool_shed_repository )
@@ -1564,7 +1567,7 @@
tool_shed_repository = tool_dependencies[ 0 ].tool_shed_repository
if kwd.get( 'uninstall_tool_dependencies_button', False ):
errors = False
- # Filter tool dependencies to only those that are installed.
+ # Filter tool dependencies to only those that are installed but in an error state.
tool_dependencies_for_uninstallation = []
for tool_dependency in tool_dependencies:
if tool_dependency.can_uninstall:
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/webapps/galaxy/controllers/forms.py
--- a/lib/galaxy/webapps/galaxy/controllers/forms.py
+++ b/lib/galaxy/webapps/galaxy/controllers/forms.py
@@ -65,6 +65,9 @@
global_actions = [
grids.GridAction( "Create new form", dict( controller='forms', action='create_form_definition' ) )
]
+
+ def build_initial_query( self, trans, **kwargs ):
+ return trans.sa_session.query( self.model_class ).join (model.FormDefinition, self.model_class.latest_form_id == model.FormDefinition.id)
class Forms( BaseUIController ):
# Empty TextField
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -838,8 +838,8 @@
# Set imported history to be user's current history.
trans.set_history( new_history )
return trans.show_ok_message(
- message="""History "%s" has been imported. <br>You can <a href="%s">start using this history</a> or %s."""
- % ( new_history.name, web.url_for( '/' ), referer_message ), use_panels=True )
+ message="""History "%s" has been imported. <br>You can <a href="%s" onclick="parent.window.location='%s';">start using this history</a> or %s."""
+ % ( new_history.name, web.url_for( '/' ), web.url_for( '/' ), referer_message ), use_panels=True )
elif not user_history or not user_history.datasets or confirm:
new_history = import_history.copy()
new_history.name = "imported: " + new_history.name
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -932,7 +932,7 @@
repository_type = kwd.get( 'repository_type', rt_util.UNRESTRICTED )
if kwd.get( 'create_repository_button', False ):
error = False
- message = repository_maintenance_util.validate_repository_name( name, trans.user )
+ message = repository_maintenance_util.validate_repository_name( trans.app, name, trans.user )
if message:
error = True
if not description:
@@ -2085,7 +2085,7 @@
repository.long_description = long_description
flush_needed = True
if repository.times_downloaded == 0 and repo_name != repository.name:
- message = repository_maintenance_util.validate_repository_name( repo_name, user )
+ message = repository_maintenance_util.validate_repository_name( trans.app, repo_name, user )
if message:
error = True
else:
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/__init__.py
--- a/lib/tool_shed/galaxy_install/__init__.py
+++ b/lib/tool_shed/galaxy_install/__init__.py
@@ -1,54 +0,0 @@
-"""
-Classes encapsulating the management of repositories installed from Galaxy tool sheds.
-"""
-import os, logging
-import tool_shed.util.shed_util_common
-import tool_shed.util.datatype_util
-from galaxy.model.orm import and_
-from tool_shed.util import xml_util
-
-log = logging.getLogger( __name__ )
-
-class InstalledRepositoryManager( object ):
- def __init__( self, app ):
- self.app = app
- self.install_model = self.app.install_model
- self.context = self.install_model.context
- self.tool_configs = self.app.config.tool_configs
- if self.app.config.migrated_tools_config not in self.tool_configs:
- self.tool_configs.append( self.app.config.migrated_tools_config )
- self.installed_repository_dicts = []
- def get_repository_install_dir( self, tool_shed_repository ):
- for tool_config in self.tool_configs:
- tree, error_message = xml_util.parse_xml( tool_config )
- if tree is None:
- return None
- root = tree.getroot()
- tool_path = root.get( 'tool_path', None )
- if tool_path:
- ts = tool_shed.util.shed_util_common.clean_tool_shed_url( tool_shed_repository.tool_shed )
- relative_path = os.path.join( tool_path,
- ts,
- 'repos',
- tool_shed_repository.owner,
- tool_shed_repository.name,
- tool_shed_repository.installed_changeset_revision )
- if os.path.exists( relative_path ):
- return relative_path
- return None
- def load_proprietary_datatypes( self ):
- for tool_shed_repository in self.context.query( self.install_model.ToolShedRepository ) \
- .filter( and_( self.install_model.ToolShedRepository.table.c.includes_datatypes==True,
- self.install_model.ToolShedRepository.table.c.deleted==False ) ) \
- .order_by( self.install_model.ToolShedRepository.table.c.id ):
- relative_install_dir = self.get_repository_install_dir( tool_shed_repository )
- if relative_install_dir:
- installed_repository_dict = tool_shed.util.datatype_util.load_installed_datatypes( self.app, tool_shed_repository, relative_install_dir )
- if installed_repository_dict:
- self.installed_repository_dicts.append( installed_repository_dict )
- def load_proprietary_converters_and_display_applications( self, deactivate=False ):
- for installed_repository_dict in self.installed_repository_dicts:
- if installed_repository_dict[ 'converter_path' ]:
- tool_shed.util.datatype_util.load_installed_datatype_converters( self.app, installed_repository_dict, deactivate=deactivate )
- if installed_repository_dict[ 'display_path' ]:
- tool_shed.util.datatype_util.load_installed_display_applications( self.app, installed_repository_dict, deactivate=deactivate )
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/grids/admin_toolshed_grids.py
--- a/lib/tool_shed/galaxy_install/grids/admin_toolshed_grids.py
+++ b/lib/tool_shed/galaxy_install/grids/admin_toolshed_grids.py
@@ -31,6 +31,13 @@
latest_revision_tip_str = ''
return '<img src="%s/june_2007_style/blue/ok_small.png" %s/>' % ( url_for( '/static' ), latest_revision_tip_str )
+def generate_repository_can_be_uninstalled_img_str( include_mouse_over=False ):
+ if include_mouse_over:
+ can_be_uninstalled_tip_str = 'class="icon-button" title="This repository can be uninstalled"'
+ else:
+ can_be_uninstalled_tip_str = ''
+ return '<img src="%s/images/fugue/toggle-bw.png" %s/>' % ( url_for( '/static' ), can_be_uninstalled_tip_str )
+
def generate_revision_updates_img_str( include_mouse_over=False ):
if include_mouse_over:
revision_updates_tip_str = 'class="icon-button" title="Updates are available in the Tool Shed for this revision"'
@@ -73,6 +80,8 @@
tool_shed_status_str += generate_includes_workflows_img_str( include_mouse_over=True )
else:
tool_shed_status_str = generate_unknown_img_str( include_mouse_over=True )
+ if tool_shed_repository.can_uninstall( trans.app ):
+ tool_shed_status_str += generate_repository_can_be_uninstalled_img_str( include_mouse_over=True )
return tool_shed_status_str
@@ -210,6 +219,7 @@
legend_str += '%s This repository is deprecated in the Tool Shed<br/>' % generate_deprecated_repository_img_str()
legend_str += '%s This repository contains exported workflows<br/>' % generate_includes_workflows_img_str()
legend_str += '%s Unable to get information from the Tool Shed<br/>' % generate_unknown_img_str()
+ legend_str += '%s This repository can be uninstalled<br/>' % generate_repository_can_be_uninstalled_img_str()
return legend_str
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/installed_repository_manager.py
--- /dev/null
+++ b/lib/tool_shed/galaxy_install/installed_repository_manager.py
@@ -0,0 +1,368 @@
+"""
+Class encapsulating the management of repositories installed from Galaxy tool sheds.
+"""
+import logging
+import os
+import tool_shed.util.shed_util_common as suc
+from tool_shed.util import datatype_util
+from tool_shed.util import repository_dependency_util
+from tool_shed.util import tool_dependency_util
+from tool_shed.util import xml_util
+from galaxy.model.orm import and_
+
+log = logging.getLogger( __name__ )
+
+
+class InstalledRepositoryManager( object ):
+
+ def __init__( self, app ):
+ """
+ Among other things, keep in in-memory sets of tuples defining installed repositories and tool dependencies along with
+ the relationships between each of them. This will allow for quick discovery of those repositories or components that
+ can be uninstalled. The feature allowing a Galaxy administrator to uninstall a repository should not be available to
+ repositories or tool dependency packages that are required by other repositories or their contents (packages). The
+ uninstall feature should be available only at the repository hierarchy level where every dependency will be uninstalled.
+ The exception for this is if an item (repository or tool dependency package) is not in an INSTALLED state - in these
+ cases, the specific item can be uninstalled in order to attempt re-installation.
+ """
+ self.app = app
+ self.install_model = self.app.install_model
+ self.context = self.install_model.context
+ self.tool_configs = self.app.config.tool_configs
+ if self.app.config.migrated_tools_config not in self.tool_configs:
+ self.tool_configs.append( self.app.config.migrated_tools_config )
+ self.installed_repository_dicts = []
+ # Keep an in-memory dictionary whose keys are tuples defining tool_shed_repository objects (whose status is 'Installed')
+ # and whose values are a list of tuples defining tool_shed_repository objects (whose status can be anything) required by
+ # the key. The value defines the entire repository dependency tree.
+ self.repository_dependencies_of_installed_repositories = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_shed_repository objects (whose status is 'Installed')
+ # and whose values are a list of tuples defining tool_shed_repository objects (whose status is 'Installed') required by
+ # the key. The value defines the entire repository dependency tree.
+ self.installed_repository_dependencies_of_installed_repositories = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_shed_repository objects (whose status is 'Installed')
+ # and whose values are a list of tuples defining tool_shed_repository objects (whose status is 'Installed') that require
+ # the key.
+ self.installed_dependent_repositories_of_installed_repositories = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_shed_repository objects (whose status is 'Installed')
+ # and whose values are a list of tuples defining its immediate tool_dependency objects (whose status can be anything).
+ # The value defines only the immediate tool dependencies of the repository and does not include any dependencies of the
+ # tool dependencies.
+ self.tool_dependencies_of_installed_repositories = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_shed_repository objects (whose status is 'Installed')
+ # and whose values are a list of tuples defining its immediate tool_dependency objects (whose status is 'Installed').
+ # The value defines only the immediate tool dependencies of the repository and does not include any dependencies of the
+ # tool dependencies.
+ self.installed_tool_dependencies_of_installed_repositories = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_dependency objects (whose status is 'Installed') and
+ # whose values are a list of tuples defining tool_dependency objects (whose status can be anything) required by the
+ # installed tool dependency at runtime. The value defines the entire tool dependency tree.
+ self.runtime_tool_dependencies_of_installed_tool_dependencies = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_dependency objects (whose status is 'Installed') and
+ # whose values are a list of tuples defining tool_dependency objects (whose status is 'Installed') that require the key
+ # at runtime. The value defines the entire tool dependency tree.
+ self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies = {}
+ # Load defined dependency relationships for installed tool shed repositories and their contents.
+ self.load_dependency_relationships()
+
+ def add_entry_to_installed_repository_dependencies_of_installed_repositories( self, repository ):
+ """
+ Add an entry to self.installed_repository_dependencies_of_installed_repositories. A side-effect of this method
+ is the population of self.installed_dependent_repositories_of_installed_repositories. Since this method discovers
+ all repositories required by the received repository, it can use the list to add entries to the reverse dictionary.
+ """
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ # Get the list of repository dependencies for this repository.
+ status = self.install_model.ToolShedRepository.installation_status.INSTALLED
+ repository_dependency_tups = \
+ repository_dependency_util.get_repository_dependency_tups_for_repository( self.app, repository, status=status )
+ # Add an entry to self.installed_repository_dependencies_of_installed_repositories.
+ if repository_tup not in self.installed_repository_dependencies_of_installed_repositories:
+ debug_msg = "Adding an entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "to installed_repository_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ self.installed_repository_dependencies_of_installed_repositories[ repository_tup ] = repository_dependency_tups
+ # Use the repository_dependency_tups to add entries to the reverse dictionary
+ # self.installed_dependent_repositories_of_installed_repositories.
+ for required_repository_tup in repository_dependency_tups:
+ debug_msg = "Appending revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "to all dependent repositories in installed_dependent_repositories_of_installed_repositories."
+ log.debug( debug_msg )
+ if required_repository_tup in self.installed_dependent_repositories_of_installed_repositories:
+ self.installed_dependent_repositories_of_installed_repositories[ required_repository_tup ].append( repository_tup )
+ else:
+ self.installed_dependent_repositories_of_installed_repositories[ required_repository_tup ] = [ repository_tup ]
+
+ def add_entry_to_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( self, tool_dependency ):
+ """Add an entry to self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies."""
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ if tool_dependency_tup not in self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies:
+ tool_shed_repository_id, name, version, type = tool_dependency_tup
+ debug_msg = "Adding an entry for version %s of %s %s " % ( version, type, name )
+ debug_msg += "to installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies."
+ log.debug( debug_msg )
+ status = self.install_model.ToolDependency.installation_status.INSTALLED
+ installed_runtime_dependent_tool_dependency_tups = \
+ tool_dependency_util.get_runtime_dependent_tool_dependency_tuples( self.app, tool_dependency, status=status )
+ self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies[ tool_dependency_tup ] = \
+ installed_runtime_dependent_tool_dependency_tups
+
+ def add_entry_to_installed_tool_dependencies_of_installed_repositories( self, repository ):
+ """Add an entry to self.installed_tool_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup not in self.installed_tool_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Adding an entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "to installed_tool_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ installed_tool_dependency_tups = []
+ for tool_dependency in repository.tool_dependencies:
+ if tool_dependency.status == self.app.install_model.ToolDependency.installation_status.INSTALLED:
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ installed_tool_dependency_tups.append( tool_dependency_tup )
+ self.installed_tool_dependencies_of_installed_repositories[ repository_tup ] = installed_tool_dependency_tups
+
+ def add_entry_to_repository_dependencies_of_installed_repositories( self, repository ):
+ """Add an entry to self.repository_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup not in self.repository_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Adding an entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "to repository_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ repository_dependency_tups = \
+ repository_dependency_util.get_repository_dependency_tups_for_repository( self.app, repository, status=None )
+ self.repository_dependencies_of_installed_repositories[ repository_tup ] = repository_dependency_tups
+
+ def add_entry_to_runtime_tool_dependencies_of_installed_tool_dependencies( self, tool_dependency ):
+ """Add an entry to self.runtime_tool_dependencies_of_installed_tool_dependencies."""
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ if tool_dependency_tup not in self.runtime_tool_dependencies_of_installed_tool_dependencies:
+ tool_shed_repository_id, name, version, type = tool_dependency_tup
+ debug_msg = "Adding an entry for version %s of %s %s " % ( version, type, name )
+ debug_msg += "to runtime_tool_dependencies_of_installed_tool_dependencies."
+ log.debug( debug_msg )
+ runtime_dependent_tool_dependency_tups = \
+ tool_dependency_util.get_runtime_dependent_tool_dependency_tuples( self.app, tool_dependency, status=None )
+ self.runtime_tool_dependencies_of_installed_tool_dependencies[ tool_dependency_tup ] = \
+ runtime_dependent_tool_dependency_tups
+
+ def add_entry_to_tool_dependencies_of_installed_repositories( self, repository ):
+ """Add an entry to self.tool_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup not in self.tool_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Adding an entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "to tool_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ tool_dependency_tups = []
+ for tool_dependency in repository.tool_dependencies:
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ tool_dependency_tups.append( tool_dependency_tup )
+ self.tool_dependencies_of_installed_repositories[ repository_tup ] = tool_dependency_tups
+
+ def get_containing_repository_for_tool_dependency( self, tool_dependency_tup ):
+ tool_shed_repository_id, name, version, type = tool_dependency_tup
+ return self.app.install_model.context.query( self.app.install_model.ToolShedRepository ).get( tool_shed_repository_id )
+
+ def get_repository_install_dir( self, tool_shed_repository ):
+ for tool_config in self.tool_configs:
+ tree, error_message = xml_util.parse_xml( tool_config )
+ if tree is None:
+ return None
+ root = tree.getroot()
+ tool_path = root.get( 'tool_path', None )
+ if tool_path:
+ ts = suc.clean_tool_shed_url( tool_shed_repository.tool_shed )
+ relative_path = os.path.join( tool_path,
+ ts,
+ 'repos',
+ str( tool_shed_repository.owner ),
+ str( tool_shed_repository.name ),
+ str( tool_shed_repository.installed_changeset_revision ) )
+ if os.path.exists( relative_path ):
+ return relative_path
+ return None
+
+ def handle_repository_install( self, repository ):
+ """Load the dependency relationships for a repository that was just installed or reinstalled."""
+ # Populate self.repository_dependencies_of_installed_repositories.
+ self.add_entry_to_repository_dependencies_of_installed_repositories( repository )
+ # Populate self.installed_repository_dependencies_of_installed_repositories.
+ self.add_entry_to_installed_repository_dependencies_of_installed_repositories( repository )
+ # Populate self.tool_dependencies_of_installed_repositories.
+ self.add_entry_to_tool_dependencies_of_installed_repositories( repository )
+ # Populate self.installed_tool_dependencies_of_installed_repositories.
+ self.add_entry_to_installed_tool_dependencies_of_installed_repositories( repository )
+ for tool_dependency in repository.tool_dependencies:
+ # Populate self.runtime_tool_dependencies_of_installed_tool_dependencies.
+ self.add_entry_to_runtime_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+ # Populate self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.
+ self.add_entry_to_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+
+ def handle_repository_uninstall( self, repository ):
+ """Remove the dependency relationships for a repository that was just uninstalled."""
+ for tool_dependency in repository.tool_dependencies:
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ # Remove this tool_dependency from all values in
+ # self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies
+ altered_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies = {}
+ for td_tup, installed_runtime_dependent_tool_dependency_tups in \
+ self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.items():
+ if tool_dependency_tup in installed_runtime_dependent_tool_dependency_tups:
+ # Remove the tool_dependency from the list.
+ installed_runtime_dependent_tool_dependency_tups.remove( tool_dependency_tup )
+ # Add the possibly altered list to the altered dictionary.
+ altered_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies[ td_tup ] = \
+ installed_runtime_dependent_tool_dependency_tups
+ self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies = \
+ altered_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies
+ # Remove the entry for this tool_dependency from self.runtime_tool_dependencies_of_installed_tool_dependencies.
+ self.remove_entry_from_runtime_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+ # Remove the entry for this tool_dependency from
+ # self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.
+ self.remove_entry_from_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+ # Remove this repository's entry from self.installed_tool_dependencies_of_installed_repositories.
+ self.remove_entry_from_installed_tool_dependencies_of_installed_repositories( repository )
+ # Remove this repository's entry from self.tool_dependencies_of_installed_repositories
+ self.remove_entry_from_tool_dependencies_of_installed_repositories( repository )
+ # Remove this repository's entry from self.installed_repository_dependencies_of_installed_repositories.
+ self.remove_entry_from_installed_repository_dependencies_of_installed_repositories( repository )
+ # Remove this repository's entry from self.repository_dependencies_of_installed_repositories.
+ self.remove_entry_from_repository_dependencies_of_installed_repositories( repository )
+
+ def handle_tool_dependency_install( self, repository, tool_dependency ):
+ """Load the dependency relationships for a tool dependency that was just installed independently of its containing repository."""
+ # The received repository must have a status of 'Installed'. The value of tool_dependency.status will either be
+ # 'Installed' or 'Error', but we only need to change the in-memory dictionaries if it is 'Installed'.
+ if tool_dependency.is_installed:
+ # Populate self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.
+ self.add_entry_to_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+ # Populate self.installed_tool_dependencies_of_installed_repositories.
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ if repository_tup in self.installed_tool_dependencies_of_installed_repositories:
+ self.installed_tool_dependencies_of_installed_repositories[ repository_tup ].append( tool_dependency_tup )
+ else:
+ self.installed_tool_dependencies_of_installed_repositories[ repository_tup ] = [ tool_dependency_tup ]
+
+ def load_dependency_relationships( self ):
+ """Load relationships for all installed repositories and tool dependencies into in-memnory dictionaries."""
+ # Get the list of installed tool shed repositories.
+ for repository in self.context.query( self.app.install_model.ToolShedRepository ) \
+ .filter( self.app.install_model.ToolShedRepository.table.c.status ==
+ self.app.install_model.ToolShedRepository.installation_status.INSTALLED ):
+ # Populate self.repository_dependencies_of_installed_repositories.
+ self.add_entry_to_repository_dependencies_of_installed_repositories( repository )
+ # Populate self.installed_repository_dependencies_of_installed_repositories.
+ self.add_entry_to_installed_repository_dependencies_of_installed_repositories( repository )
+ # Populate self.tool_dependencies_of_installed_repositories.
+ self.add_entry_to_tool_dependencies_of_installed_repositories( repository )
+ # Populate self.installed_tool_dependencies_of_installed_repositories.
+ self.add_entry_to_installed_tool_dependencies_of_installed_repositories( repository )
+ # Get the list of installed tool dependencies.
+ for tool_dependency in self.context.query( self.app.install_model.ToolDependency ) \
+ .filter( self.app.install_model.ToolDependency.table.c.status ==
+ self.app.install_model.ToolDependency.installation_status.INSTALLED ):
+ # Populate self.runtime_tool_dependencies_of_installed_tool_dependencies.
+ self.add_entry_to_runtime_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+ # Populate self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.
+ self.add_entry_to_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+
+ def load_proprietary_datatypes( self ):
+ for tool_shed_repository in self.context.query( self.install_model.ToolShedRepository ) \
+ .filter( and_( self.install_model.ToolShedRepository.table.c.includes_datatypes==True,
+ self.install_model.ToolShedRepository.table.c.deleted==False ) ) \
+ .order_by( self.install_model.ToolShedRepository.table.c.id ):
+ relative_install_dir = self.get_repository_install_dir( tool_shed_repository )
+ if relative_install_dir:
+ installed_repository_dict = datatype_util.load_installed_datatypes( self.app, tool_shed_repository, relative_install_dir )
+ if installed_repository_dict:
+ self.installed_repository_dicts.append( installed_repository_dict )
+
+ def load_proprietary_converters_and_display_applications( self, deactivate=False ):
+ for installed_repository_dict in self.installed_repository_dicts:
+ if installed_repository_dict[ 'converter_path' ]:
+ datatype_util.load_installed_datatype_converters( self.app, installed_repository_dict, deactivate=deactivate )
+ if installed_repository_dict[ 'display_path' ]:
+ datatype_util.load_installed_display_applications( self.app, installed_repository_dict, deactivate=deactivate )
+
+ def remove_entry_from_installed_repository_dependencies_of_installed_repositories( self, repository ):
+ """
+ Remove an entry from self.installed_repository_dependencies_of_installed_repositories. A side-effect of this method
+ is removal of appropriate value items from self.installed_dependent_repositories_of_installed_repositories.
+ """
+ # Remove tuples defining this repository from value lists in self.installed_dependent_repositories_of_installed_repositories.
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ altered_installed_dependent_repositories_of_installed_repositories = {}
+ for r_tup, v_tups in self.installed_dependent_repositories_of_installed_repositories.items():
+ if repository_tup in v_tups:
+ debug_msg = "Removing entry for revision %s of repository %s owned by %s " % \
+ ( installed_changeset_revision, name, owner )
+ r_tool_shed, r_name, r_owner, r_installed_changeset_revision = r_tup
+ debug_msg += "from the dependent list for revision %s of repository %s owned by %s " % \
+ ( r_installed_changeset_revision, r_name, r_owner )
+ debug_msg += "in installed_repository_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ v_tups.remove( repository_tup )
+ altered_installed_dependent_repositories_of_installed_repositories[ r_tup ] = v_tups
+ self.installed_dependent_repositories_of_installed_repositories = \
+ altered_installed_dependent_repositories_of_installed_repositories
+ # Remove this repository's entry from self.installed_repository_dependencies_of_installed_repositories.
+ if repository_tup in self.installed_repository_dependencies_of_installed_repositories:
+ debug_msg = "Removing entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "from installed_repository_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ del self.installed_repository_dependencies_of_installed_repositories[ repository_tup ]
+
+ def remove_entry_from_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( self, tool_dependency ):
+ """Remove an entry from self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies."""
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ if tool_dependency_tup in self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies:
+ tool_shed_repository_id, name, version, type = tool_dependency_tup
+ debug_msg = "Removing entry for version %s of %s %s " % ( version, type, name )
+ debug_msg += "from installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies."
+ log.debug( debug_msg )
+ del self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies[ tool_dependency_tup ]
+
+ def remove_entry_from_installed_tool_dependencies_of_installed_repositories( self, repository ):
+ """Remove an entry from self.installed_tool_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup in self.installed_tool_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Removing entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "from installed_tool_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ del self.installed_tool_dependencies_of_installed_repositories[ repository_tup ]
+
+ def remove_entry_from_repository_dependencies_of_installed_repositories( self, repository ):
+ """Remove an entry from self.repository_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup in self.repository_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Removing entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "from repository_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ del self.repository_dependencies_of_installed_repositories[ repository_tup ]
+
+ def remove_entry_from_runtime_tool_dependencies_of_installed_tool_dependencies( self, tool_dependency ):
+ """Remove an entry from self.runtime_tool_dependencies_of_installed_tool_dependencies."""
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ if tool_dependency_tup in self.runtime_tool_dependencies_of_installed_tool_dependencies:
+ tool_shed_repository_id, name, version, type = tool_dependency_tup
+ debug_msg = "Removing entry for version %s of %s %s from runtime_tool_dependencies_of_installed_tool_dependencies." % \
+ ( version, type, name )
+ log.debug( debug_msg )
+ del self.runtime_tool_dependencies_of_installed_tool_dependencies[ tool_dependency_tup ]
+
+ def remove_entry_from_tool_dependencies_of_installed_repositories( self, repository ):
+ """Remove an entry from self.tool_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup in self.tool_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Removing entry for revision %s of repository %s owned by %s from tool_dependencies_of_installed_repositories." % \
+ ( installed_changeset_revision, name, owner )
+ log.debug( debug_msg )
+ del self.tool_dependencies_of_installed_repositories[ repository_tup ]
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/migrate/common.py
--- a/lib/tool_shed/galaxy_install/migrate/common.py
+++ b/lib/tool_shed/galaxy_install/migrate/common.py
@@ -2,18 +2,10 @@
import os
import sys
import galaxy.config
-import galaxy.datatypes.registry
-from galaxy import tools
-from galaxy.tools.data import ToolDataTableManager
-from galaxy.web import security
-import galaxy.model.mapping
-import galaxy.tools.search
-from galaxy.objectstore import build_object_store_from_config
-import tool_shed.tool_shed_registry
from tool_shed.galaxy_install import install_manager
-class MigrateToolsApplication( object ):
+class MigrateToolsApplication( object, galaxy.config.ConfiguresGalaxyMixin ):
"""Encapsulates the state of a basic Galaxy Universe application in order to initiate the Install Manager"""
def __init__( self, tools_migration_config ):
@@ -33,41 +25,23 @@
for key, value in config_parser.items( "app:main" ):
galaxy_config_dict[ key ] = value
self.config = galaxy.config.Configuration( **galaxy_config_dict )
- if not self.config.database_connection:
- self.config.database_connection = "sqlite:///%s?isolation_level=IMMEDIATE" % self.config.database
+
self.config.update_integrated_tool_panel = True
- self.object_store = build_object_store_from_config( self.config )
- # Security helper
- self.security = security.SecurityHelper( id_secret=self.config.id_secret )
- # Setup the database engine and ORM
- self.model = galaxy.model.mapping.init( self.config.file_path,
- self.config.database_connection,
- engine_options={},
- create_tables=False,
- object_store=self.object_store )
- # Create an empty datatypes registry.
- self.datatypes_registry = galaxy.datatypes.registry.Registry()
- # 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 )
- # 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=True )
- # 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 )
- # 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
+
+ self._configure_object_store()
+
+ self._configure_security()
+
+ self._configure_models()
+
+ self._configure_datatypes_registry( )
+
+ self._configure_tool_data_tables( from_shed_config=True )
+
+ self._configure_toolbox()
+
+ self._configure_tool_shed_registry()
+
# Get the latest tool migration script number to send to the Install manager.
latest_migration_script_number = int( tools_migration_config.split( '_' )[ 0 ] )
# The value of migrated_tools_config is migrated_tools_conf.xml, and is reserved for containing only those tools that have been
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/repository_util.py
--- a/lib/tool_shed/galaxy_install/repository_util.py
+++ b/lib/tool_shed/galaxy_install/repository_util.py
@@ -504,8 +504,8 @@
query = trans.install_model.context.query( trans.install_model.ToolShedRepository ).filter( or_( *clause_list ) )
return encoded_kwd, query, tool_shed_repositories, encoded_repository_ids
-def install_tool_shed_repository( trans, tool_shed_repository, repo_info_dict, tool_panel_section_key, shed_tool_conf, tool_path, install_tool_dependencies,
- reinstalling=False ):
+def install_tool_shed_repository( trans, tool_shed_repository, repo_info_dict, tool_panel_section_key, shed_tool_conf, tool_path,
+ install_tool_dependencies, reinstalling=False ):
if tool_panel_section_key:
try:
tool_section = trans.app.toolbox.tool_panel[ tool_panel_section_key ]
@@ -579,7 +579,11 @@
tool_dependencies_config=tool_dependencies_config,
tool_dependencies=tool_shed_repository.tool_dependencies )
suc.remove_dir( work_dir )
- suc.update_tool_shed_repository_status( trans.app, tool_shed_repository, trans.install_model.ToolShedRepository.installation_status.INSTALLED )
+ suc.update_tool_shed_repository_status( trans.app,
+ tool_shed_repository,
+ trans.install_model.ToolShedRepository.installation_status.INSTALLED )
+ # Add the installed repository and any tool dependencies to the in-memory dictionaries in the installed_repository_manager.
+ trans.app.installed_repository_manager.handle_repository_install( tool_shed_repository )
else:
# An error occurred while cloning the repository, so reset everything necessary to enable another attempt.
set_repository_attributes( trans,
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/update_manager.py
--- a/lib/tool_shed/galaxy_install/update_manager.py
+++ b/lib/tool_shed/galaxy_install/update_manager.py
@@ -30,7 +30,7 @@
# repository. This information includes items like newer installable repository revisions, current revision updates, whether
# the repository revision is the latest installable revision, and whether the repository has been deprecated in the tool shed.
for repository in self.context.query( self.app.install_model.ToolShedRepository ) \
- .filter( self.app.install_model.ToolShedRepository.table.c.deleted == False ):
+ .filter( self.app.install_model.ToolShedRepository.table.c.deleted == False ):
tool_shed_status_dict = suc.get_tool_shed_status_for_installed_repository( self.app, repository )
if tool_shed_status_dict:
if tool_shed_status_dict != repository.tool_shed_status:
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/scripts/check_repositories_for_functional_tests.py
--- a/lib/tool_shed/scripts/check_repositories_for_functional_tests.py
+++ b/lib/tool_shed/scripts/check_repositories_for_functional_tests.py
@@ -253,6 +253,11 @@
# a test_environment entry. If we use it we need to temporarily eliminate it from the list of tool_test_results_dicts
# since it will be re-inserted later.
tool_test_results_dict = tool_test_results_dicts.pop( 0 )
+ elif len( tool_test_results_dict ) == 2 and \
+ 'test_environment' in tool_test_results_dict and 'missing_test_components' in tool_test_results_dict:
+ # We can re-use tool_test_results_dict if its only entries are "test_environment" and "missing_test_components".
+ # In this case, some tools are missing tests components while others are not.
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
else:
# The latest tool_test_results_dict has been populated with the results of a test run, so it cannot be used.
tool_test_results_dict = {}
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/scripts/check_tool_dependency_definition_repositories.py
--- a/lib/tool_shed/scripts/check_tool_dependency_definition_repositories.py
+++ b/lib/tool_shed/scripts/check_tool_dependency_definition_repositories.py
@@ -41,6 +41,7 @@
class RepositoriesApplication( object ):
"""Encapsulates the state of a Universe application"""
+
def __init__( self, config ):
if config.database_connection is False:
config.database_connection = "sqlite:///%s?isolation_level=IMMEDIATE" % config.database
@@ -133,11 +134,12 @@
# Do not check metadata records that have an entry in the skip_tool_tests table, since they won't be tested anyway.
skip_metadata_ids = select( [ app.model.SkipToolTest.table.c.repository_metadata_id ] )
# Get the list of metadata records to check, restricting it to records that have not been flagged do_not_test.
- for repository_metadata in app.sa_session.query( app.model.RepositoryMetadata ) \
- .filter( and_( app.model.RepositoryMetadata.table.c.downloadable == True,
- app.model.RepositoryMetadata.table.c.do_not_test == False,
- app.model.RepositoryMetadata.table.c.repository_id.in_( tool_dependency_defintion_repository_ids ),
- not_( app.model.RepositoryMetadata.table.c.id.in_( skip_metadata_ids ) ) ) ):
+ for repository_metadata in \
+ app.sa_session.query( app.model.RepositoryMetadata ) \
+ .filter( and_( app.model.RepositoryMetadata.table.c.downloadable == True,
+ app.model.RepositoryMetadata.table.c.do_not_test == False,
+ app.model.RepositoryMetadata.table.c.repository_id.in_( tool_dependency_defintion_repository_ids ),
+ not_( app.model.RepositoryMetadata.table.c.id.in_( skip_metadata_ids ) ) ) ):
records_checked += 1
# Check the next repository revision.
changeset_revision = str( repository_metadata.changeset_revision )
@@ -161,9 +163,31 @@
invalid_metadata += 1
if not info_only:
# Create the tool_test_results_dict dictionary, using the dictionary from the previous test run if available.
- if repository_metadata.tool_test_results:
- tool_test_results_dict = repository_metadata.tool_test_results
+ if repository_metadata.tool_test_results is not None:
+ # We'll listify the column value in case it uses the old approach of storing the results of only a single test run.
+ tool_test_results_dicts = listify( repository_metadata.tool_test_results )
else:
+ tool_test_results_dicts = []
+ if tool_test_results_dicts:
+ # Inspect the tool_test_results_dict for the last test run in case it contains only a test_environment
+ # entry. This will occur with multiple runs of this script without running the associated
+ # install_and_test_tool_sed_repositories.sh script which will further populate the tool_test_results_dict.
+ tool_test_results_dict = tool_test_results_dicts[ 0 ]
+ if len( tool_test_results_dict ) <= 1:
+ # We can re-use the mostly empty tool_test_results_dict for this run because it is either empty or it contains only
+ # a test_environment entry. If we use it we need to temporarily eliminate it from the list of tool_test_results_dicts
+ # since it will be re-inserted later.
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
+ elif len( tool_test_results_dict ) == 2 and \
+ 'test_environment' in tool_test_results_dict and 'missing_test_components' in tool_test_results_dict:
+ # We can re-use tool_test_results_dict if its only entries are "test_environment" and "missing_test_components".
+ # In this case, some tools are missing tests components while others are not.
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
+ else:
+ # The latest tool_test_results_dict has been populated with the results of a test run, so it cannot be used.
+ tool_test_results_dict = {}
+ else:
+ # Create a new dictionary for the most recent test run.
tool_test_results_dict = {}
# Initialize the tool_test_results_dict dictionary with the information about the current test environment.
test_environment_dict = tool_test_results_dict.get( 'test_environment', {} )
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/common_install_util.py
--- a/lib/tool_shed/util/common_install_util.py
+++ b/lib/tool_shed/util/common_install_util.py
@@ -71,8 +71,8 @@
def get_dependencies_for_repository( trans, tool_shed_url, repo_info_dict, includes_tool_dependencies ):
"""
- Return dictionaries containing the sets of installed and missing tool dependencies and repository dependencies associated with the repository defined
- by the received repo_info_dict.
+ Return dictionaries containing the sets of installed and missing tool dependencies and repository dependencies associated
+ with the repository defined by the received repo_info_dict.
"""
repository = None
installed_rd = {}
@@ -102,7 +102,8 @@
# Discover all repository dependencies and retrieve information for installing them.
all_repo_info_dict = get_required_repo_info_dicts( trans, tool_shed_url, util.listify( repo_info_dict ) )
has_repository_dependencies = all_repo_info_dict.get( 'has_repository_dependencies', False )
- has_repository_dependencies_only_if_compiling_contained_td = all_repo_info_dict.get( 'has_repository_dependencies_only_if_compiling_contained_td', False )
+ has_repository_dependencies_only_if_compiling_contained_td = \
+ all_repo_info_dict.get( 'has_repository_dependencies_only_if_compiling_contained_td', False )
includes_tools_for_display_in_tool_panel = all_repo_info_dict.get( 'includes_tools_for_display_in_tool_panel', False )
includes_tool_dependencies = all_repo_info_dict.get( 'includes_tool_dependencies', False )
includes_tools = all_repo_info_dict.get( 'includes_tools', False )
@@ -138,7 +139,8 @@
# We have a single repository with (possibly) no defined repository dependencies.
all_repo_info_dict = get_required_repo_info_dicts( trans, tool_shed_url, util.listify( repo_info_dict ) )
has_repository_dependencies = all_repo_info_dict.get( 'has_repository_dependencies', False )
- has_repository_dependencies_only_if_compiling_contained_td = all_repo_info_dict.get( 'has_repository_dependencies_only_if_compiling_contained_td', False )
+ has_repository_dependencies_only_if_compiling_contained_td = \
+ all_repo_info_dict.get( 'has_repository_dependencies_only_if_compiling_contained_td', False )
includes_tools_for_display_in_tool_panel = all_repo_info_dict.get( 'includes_tools_for_display_in_tool_panel', False )
includes_tool_dependencies = all_repo_info_dict.get( 'includes_tool_dependencies', False )
includes_tools = all_repo_info_dict.get( 'includes_tools', False )
@@ -202,8 +204,8 @@
# Get the description from the metadata in case it has a value.
repository_dependencies = metadata.get( 'repository_dependencies', {} )
description = repository_dependencies.get( 'description', None )
- # We need to add a root_key entry to one or both of installed_repository_dependencies dictionary and the missing_repository_dependencies
- # dictionaries for proper display parsing.
+ # We need to add a root_key entry to one or both of installed_repository_dependencies dictionary and the
+ # missing_repository_dependencies dictionaries for proper display parsing.
root_key = container_util.generate_repository_dependencies_key_for_repository( repository.tool_shed,
repository.name,
repository.owner,
@@ -460,7 +462,7 @@
if tool_dependency.can_install:
# The database record is currently in a state that allows us to install the package on the file system.
try:
- dependencies_ignored = app.toolbox.dependency_manager and not app.toolbox.dependency_manager.uses_tool_shed_dependencies()
+ dependencies_ignored = not app.toolbox.dependency_manager.uses_tool_shed_dependencies()
if dependencies_ignored:
log.debug( "Skipping package %s because tool shed dependency resolver not enabled." % str( package_name ) )
# Tool dependency resolves have been configured and they do not include the tool shed. Do not install package.
@@ -489,6 +491,8 @@
if tool_dependency and tool_dependency.status in [ app.install_model.ToolDependency.installation_status.INSTALLED,
app.install_model.ToolDependency.installation_status.ERROR ]:
installed_tool_dependencies.append( tool_dependency )
+ # Add the tool_dependency to the in-memory dictionaries in the installed_repository_manager.
+ app.installed_repository_manager.handle_tool_dependency_install( tool_shed_repository, tool_dependency )
elif elem.tag == 'set_environment':
# <set_environment version="1.0">
# <environment_variable name="R_SCRIPT_PATH"action="set_to">$REPOSITORY_INSTALL_DIR</environment_variable>
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/container_util.py
--- a/lib/tool_shed/util/container_util.py
+++ b/lib/tool_shed/util/container_util.py
@@ -5,7 +5,6 @@
from galaxy.web.framework.helpers import time_ago
from tool_shed.util import common_util
from tool_shed.util import readme_util
-from tool_shed.util.tool_dependency_util import tool_dependency_is_orphan
import tool_shed.util.shed_util_common as suc
log = logging.getLogger( __name__ )
@@ -269,7 +268,7 @@
"""Tool dependency object"""
def __init__( self, id=None, name=None, version=None, type=None, readme=None, installation_status=None, repository_id=None,
- tool_dependency_id=None, is_orphan=None ):
+ tool_dependency_id=None ):
self.id = id
self.name = name
self.version = version
@@ -278,15 +277,6 @@
self.installation_status = installation_status
self.repository_id = repository_id
self.tool_dependency_id = tool_dependency_id
- # The designation of a ToolDependency into the "orphan" category has evolved over time, and is significantly restricted since the
- # introduction of the TOOL_DEPENDENCY_DEFINITION repository type. This designation is still critical, however, in that it handles
- # the case where a repository contains both tools and a tool_dependencies.xml file, but the definition in the tool_dependencies.xml
- # file is in no way related to anything defined by any of the contained tool's requirements tag sets. This is important in that it
- # is often a result of a typo (e.g., dependency name or version) that differs between the tool dependency definition within the
- # tool_dependencies.xml file and what is defined in the tool config's <requirements> tag sets. In these cases, the user should be
- # presented with a warning message, and this warning message is is in fact displayed if the following is_orphan attribute is True.
- # This is tricky because in some cases it may be intentional, and tool dependencies that are categorized as "orphan" are in fact valid.
- self.is_orphan = is_orphan
@property
def listify( self ):
@@ -318,42 +308,6 @@
self.repository_metadata_id = repository_metadata_id
self.repository_id = repository_id
-def add_orphan_settings_to_tool_dependencies( tool_dependencies, tools ):
- """Inspect all received tool dependencies and label those that are orphans within the repository."""
- #orphan_env_dependencies = orphan_tool_dependencies.get( 'set_environment', None )
- new_tool_dependencies = {}
- for td_key, requirements_dict in tool_dependencies.items():
- if td_key in [ 'set_environment' ]:
- # "set_environment": [{"name": "R_SCRIPT_PATH", "type": "set_environment"}]
- new_set_environment_dict_list = []
- for env_requirements_dict in requirements_dict:
- try:
- name = env_requirements_dict[ 'name' ]
- type = env_requirements_dict[ 'type' ]
- if tool_dependency_is_orphan( type, name, None, tools ):
- env_requirements_dict[ 'is_orphan' ] = True
- except Exception, e:
- name = str( e )
- type = 'unknown'
- is_orphan = 'unknown'
- new_set_environment_dict_list.append( env_requirements_dict )
- new_tool_dependencies[ td_key ] = new_set_environment_dict_list
- else:
- # {"R/2.15.1": {"name": "R", "readme": "some string", "type": "package", "version": "2.15.1"}
- try:
- name = requirements_dict[ 'name' ]
- type = requirements_dict[ 'type' ]
- version = requirements_dict[ 'version']
- if tool_dependency_is_orphan( type, name, version, tools ):
- requirements_dict[ 'is_orphan' ] = True
- except Exception, e:
- name = str( e )
- type = 'unknown'
- version = 'unknown'
- is_orphan = 'unknown'
- new_tool_dependencies[ td_key ] = requirements_dict
- return new_tool_dependencies
-
def build_data_managers_folder( trans, folder_id, data_managers, label=None ):
"""Return a folder hierarchy containing Data Managers."""
if data_managers:
@@ -849,8 +803,6 @@
if 'tools' not in exclude:
tools = metadata.get( 'tools', [] )
tools.extend( metadata.get( 'invalid_tools', [] ) )
- if tools:
- tool_dependencies = add_orphan_settings_to_tool_dependencies( tool_dependencies, tools )
folder_id, tool_dependencies_root_folder = build_tool_dependencies_folder( trans,
folder_id,
tool_dependencies,
@@ -1055,13 +1007,6 @@
tool_dependency_id += 1
if dependency_key in [ 'set_environment' ]:
for set_environment_dict in requirements_dict:
- if trans.webapp.name == 'tool_shed':
- is_orphan = set_environment_dict.get( 'is_orphan', False )
- else:
- # This is probably not necessary to display in Galaxy.
- is_orphan = False
- if is_orphan:
- folder.description = not_used_by_local_tools_description
try:
name = set_environment_dict.get( 'name', None )
type = set_environment_dict[ 'type' ]
@@ -1086,16 +1031,9 @@
readme=None,
installation_status=installation_status,
repository_id=repository_id,
- tool_dependency_id=td_id,
- is_orphan=is_orphan )
+ tool_dependency_id=td_id )
folder.tool_dependencies.append( tool_dependency )
else:
- if trans.webapp.name == 'tool_shed':
- is_orphan = requirements_dict.get( 'is_orphan', False )
- else:
- is_orphan = False
- if is_orphan:
- folder.description = not_used_by_local_tools_description
try:
name = requirements_dict[ 'name' ]
version = requirements_dict[ 'version' ]
@@ -1122,8 +1060,7 @@
readme=None,
installation_status=installation_status,
repository_id=repository_id,
- tool_dependency_id=td_id,
- is_orphan=is_orphan )
+ tool_dependency_id=td_id )
folder.tool_dependencies.append( tool_dependency )
else:
tool_dependencies_root_folder = None
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/metadata_util.py
--- a/lib/tool_shed/util/metadata_util.py
+++ b/lib/tool_shed/util/metadata_util.py
@@ -940,7 +940,12 @@
for required_file in ttb.required_files:
value, extra = required_file
required_files.append( ( value ) )
- inputs = [ input for input in ttb.inputs ]
+ inputs = []
+ for input_field, values in ttb.inputs.iteritems():
+ if len( values ) == 1:
+ inputs.append( ( input_field, values[0] ) )
+ else:
+ inputs.append( ( input_field, values ) )
outputs = []
for output in ttb.outputs:
name, file_name, extra = output
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/repository_dependency_util.py
--- a/lib/tool_shed/util/repository_dependency_util.py
+++ b/lib/tool_shed/util/repository_dependency_util.py
@@ -355,14 +355,53 @@
handled_key_rd_dicts,
circular_repository_dependencies )
elif key_rd_dicts_to_be_processed:
- handle_next_repository_dependency( trans, key_rd_dicts_to_be_processed, all_repository_dependencies, handled_key_rd_dicts, circular_repository_dependencies )
+ handle_next_repository_dependency( trans,
+ key_rd_dicts_to_be_processed,
+ all_repository_dependencies,
+ handled_key_rd_dicts,
+ circular_repository_dependencies )
elif key_rd_dicts_to_be_processed:
- handle_next_repository_dependency( trans, key_rd_dicts_to_be_processed, all_repository_dependencies, handled_key_rd_dicts, circular_repository_dependencies )
+ handle_next_repository_dependency( trans,
+ key_rd_dicts_to_be_processed,
+ all_repository_dependencies,
+ handled_key_rd_dicts,
+ circular_repository_dependencies )
elif key_rd_dicts_to_be_processed:
- handle_next_repository_dependency( trans, key_rd_dicts_to_be_processed, all_repository_dependencies, handled_key_rd_dicts, circular_repository_dependencies )
+ handle_next_repository_dependency( trans,
+ key_rd_dicts_to_be_processed,
+ all_repository_dependencies,
+ handled_key_rd_dicts,
+ circular_repository_dependencies )
all_repository_dependencies = prune_invalid_repository_dependencies( all_repository_dependencies )
return all_repository_dependencies
+def get_repository_dependency_tups_for_repository( app, repository, dependency_tups=None, status=None ):
+ """
+ Return a list of of tuples defining tool_shed_repository objects (whose status can be anything) required by the
+ received repository. The returned list defines the entire repository dependency tree.
+ """
+ if dependency_tups is None:
+ dependency_tups = []
+ repository_tup = get_repository_tuple_for_installed_repository_manager( repository )
+ for rrda in repository.required_repositories:
+ repository_dependency = rrda.repository_dependency
+ required_repository = repository_dependency.repository
+ if status is None or required_repository.status == status:
+ required_repository_tup = get_repository_tuple_for_installed_repository_manager( required_repository )
+ if required_repository_tup == repository_tup:
+ # We have a circular repository dependency relationship, skip this entry.
+ continue
+ if required_repository_tup not in dependency_tups:
+ dependency_tups.append( required_repository_tup )
+ return get_repository_dependency_tups_for_repository( app, required_repository, dependency_tups=dependency_tups )
+ return dependency_tups
+
+def get_repository_tuple_for_installed_repository_manager( repository ):
+ return ( str( repository.tool_shed ),
+ str( repository.name ),
+ str( repository.owner ),
+ str( repository.installed_changeset_revision ) )
+
def get_updated_changeset_revisions_for_repository_dependencies( trans, key_rd_dicts ):
updated_key_rd_dicts = []
for key_rd_dict in key_rd_dicts:
@@ -373,9 +412,10 @@
if suc.tool_shed_is_this_tool_shed( rd_toolshed ):
repository = suc.get_repository_by_name_and_owner( trans.app, rd_name, rd_owner )
if repository:
- repository_metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
- trans.security.encode_id( repository.id ),
- rd_changeset_revision )
+ repository_metadata = \
+ metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
+ trans.security.encode_id( repository.id ),
+ rd_changeset_revision )
if repository_metadata:
# The repository changeset_revision is installable, so no updates are available.
new_key_rd_dict = {}
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/repository_maintenance_util.py
--- a/lib/tool_shed/util/repository_maintenance_util.py
+++ b/lib/tool_shed/util/repository_maintenance_util.py
@@ -137,7 +137,7 @@
results_message += 'This Tool Shed does not have the category <b>%s</b> so it will not be associated with this repository.' % \
str( category_name )
else:
- category_ids.append( trans.security.encode_id( category.id ) )
+ category_ids.append( trans.security.encode_id( category.id ) )
# Create the repository record in the database.
repository, create_message = create_repository( trans,
name,
@@ -157,15 +157,18 @@
import_results_tups.append( ( ( str( name ), str( username ) ), results_message ) )
return import_results_tups
-def validate_repository_name( name, user ):
+def validate_repository_name( app, name, user ):
# Repository names must be unique for each user, must be at least four characters
# in length and must contain only lower-case letters, numbers, and the '_' character.
if name in [ 'None', None, '' ]:
return 'Enter the required repository name.'
if name in [ 'repos' ]:
return "The term <b>%s</b> is a reserved word in the tool shed, so it cannot be used as a repository name." % name
- for repository in user.active_repositories:
- if repository.name == name:
+ check_existing = suc.get_repository_by_name_and_owner( app, name, user.username )
+ if check_existing is not None:
+ if check_existing.deleted:
+ return 'You have a deleted repository named <b>%s</b>, so choose a different name.' % name
+ else:
return "You already have a repository named <b>%s</b>, so choose a different name." % name
if len( name ) < 4:
return "Repository names must be at least 4 characters in length."
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/tool_dependency_util.py
--- a/lib/tool_shed/util/tool_dependency_util.py
+++ b/lib/tool_shed/util/tool_dependency_util.py
@@ -4,6 +4,7 @@
from galaxy import eggs
from galaxy import util
from galaxy.model.orm import and_
+from galaxy.model.orm import or_
import tool_shed.util.shed_util_common as suc
import tool_shed.repository_types.util as rt_util
from tool_shed.util import xml_util
@@ -13,9 +14,10 @@
def add_installation_directories_to_tool_dependencies( trans, tool_dependencies ):
"""
- Determine the path to the installation directory for each of the received tool dependencies. This path will be displayed within the tool dependencies
- container on the select_tool_panel_section or reselect_tool_panel_section pages when installing or reinstalling repositories that contain tools with the
- defined tool dependencies. The list of tool dependencies may be associated with more than a single repository.
+ Determine the path to the installation directory for each of the received tool dependencies. This path will be
+ displayed within the tool dependencies container on the select_tool_panel_section or reselect_tool_panel_section
+ pages when installing or reinstalling repositories that contain tools with the defined tool dependencies. The
+ list of tool dependencies may be associated with more than a single repository.
"""
for dependency_key, requirements_dict in tool_dependencies.items():
if dependency_key in [ 'set_environment' ]:
@@ -64,7 +66,10 @@
return tool_dependency
def create_tool_dependency_objects( app, tool_shed_repository, relative_install_dir, set_status=True ):
- """Create or update a ToolDependency for each entry in tool_dependencies_config. This method is called when installing a new tool_shed_repository."""
+ """
+ Create or update a ToolDependency for each entry in tool_dependencies_config. This method is called when
+ installing a new tool_shed_repository.
+ """
tool_dependency_objects = []
shed_config_dict = tool_shed_repository.get_shed_config_dict( app )
if shed_config_dict.get( 'tool_path' ):
@@ -82,12 +87,13 @@
name = elem.get( 'name', None )
version = elem.get( 'version', None )
if name and version:
+ status = app.install_model.ToolDependency.installation_status.NEVER_INSTALLED
tool_dependency = create_or_update_tool_dependency( app,
tool_shed_repository,
name=name,
version=version,
type=tool_dependency_type,
- status=app.install_model.ToolDependency.installation_status.NEVER_INSTALLED,
+ status=status,
set_status=set_status )
tool_dependency_objects.append( tool_dependency )
elif tool_dependency_type == 'set_environment':
@@ -96,12 +102,13 @@
name = env_elem.get( 'name', None )
action = env_elem.get( 'action', None )
if name and action:
+ status = app.install_model.ToolDependency.installation_status.NEVER_INSTALLED
tool_dependency = create_or_update_tool_dependency( app,
tool_shed_repository,
name=name,
version=None,
type=tool_dependency_type,
- status=app.install_model.ToolDependency.installation_status.NEVER_INSTALLED,
+ status=status,
set_status=set_status )
tool_dependency_objects.append( tool_dependency )
return tool_dependency_objects
@@ -185,12 +192,14 @@
def generate_message_for_repository_type_change( trans, repository ):
message = ''
if repository.can_change_type_to( trans.app, rt_util.TOOL_DEPENDENCY_DEFINITION ):
- tool_dependency_definition_type_class = trans.app.repository_types_registry.get_class_by_label( rt_util.TOOL_DEPENDENCY_DEFINITION )
- message += "This repository currently contains a single file named <b>%s</b>. If additional files will " % suc.TOOL_DEPENDENCY_DEFINITION_FILENAME
- message += "not be added to this repository, then it's type should be set to <b>%s</b>.<br/>" % tool_dependency_definition_type_class.label
+ tool_dependency_definition_type_class = \
+ trans.app.repository_types_registry.get_class_by_label( rt_util.TOOL_DEPENDENCY_DEFINITION )
+ message += "This repository currently contains a single file named <b>%s</b>. If additional files will " % \
+ suc.TOOL_DEPENDENCY_DEFINITION_FILENAME
+ message += "not be added to this repository, then it's type should be set to <b>%s</b>.<br/>" % \
+ tool_dependency_definition_type_class.label
return message
-
-
+
def get_download_url_for_platform( url_templates, platform_info_dict ):
'''
Compare the dict returned by get_platform_info() with the values specified in the url_template element. Return
@@ -289,6 +298,31 @@
env_sh_file_path = os.path.join( env_sh_file_dir, 'env.sh' )
return env_sh_file_path
+def get_runtime_dependent_tool_dependency_tuples( app, tool_dependency, status=None ):
+ """
+ Return the list of tool dependency objects that require the received tool dependency at run time. The returned
+ list will be filtered by the received status if it is not None. This method is called only from Galaxy.
+ """
+ runtime_dependent_tool_dependency_tups = []
+ required_env_shell_file_path = tool_dependency.get_env_shell_file_path( app )
+ if required_env_shell_file_path:
+ required_env_shell_file_path = os.path.abspath( required_env_shell_file_path )
+ if required_env_shell_file_path is not None:
+ for td in app.install_model.context.query( app.install_model.ToolDependency ):
+ if status is None or td.status == status:
+ env_shell_file_path = td.get_env_shell_file_path( app )
+ if env_shell_file_path is not None:
+ try:
+ contents = open( env_shell_file_path, 'r' ).read()
+ except Exception, e:
+ contents = None
+ log.debug( 'Error reading file %s, so cannot determine if package %s requires package %s at run time: %s' % \
+ ( str( env_shell_file_path ), str( td.name ), str( tool_dependency.name ), str( e ) ) )
+ if contents is not None and contents.find( required_env_shell_file_path ) >= 0:
+ td_tuple = get_tool_dependency_tuple_for_installed_repository_manager( td )
+ runtime_dependent_tool_dependency_tups.append( td_tuple )
+ return runtime_dependent_tool_dependency_tups
+
def get_tool_dependency( trans, id ):
"""Get a tool_dependency from the database via id"""
return trans.install_model.context.query( trans.install_model.ToolDependency ).get( trans.security.decode_id( id ) )
@@ -353,6 +387,13 @@
repository_name,
repository_changeset_revision ) )
+def get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency ):
+ if tool_dependency.type is None:
+ type = None
+ else:
+ type = str( tool_dependency.type )
+ return ( tool_dependency.tool_shed_repository_id, str( tool_dependency.name ), str( tool_dependency.version ), type )
+
def handle_tool_dependency_installation_error( app, tool_dependency, error_message, remove_installation_path=False ):
# Since there was an installation error, remove the installation directory because the install_package method uses
# this: "if os.path.exists( install_dir ):". Setting remove_installation_path to True should rarely occur. It is
@@ -479,6 +520,7 @@
return installed_tool_dependencies, missing_tool_dependencies
def remove_tool_dependency( app, tool_dependency ):
+ """The received tool_dependency must be in an error state."""
context = app.install_model.context
dependency_install_dir = tool_dependency.installation_directory( app )
removed, error_message = remove_tool_dependency_installation_directory( dependency_install_dir )
@@ -487,6 +529,9 @@
tool_dependency.error_message = None
context.add( tool_dependency )
context.flush()
+ # Since the received tool_dependency is in an error state, nothing will need to be changed in any
+ # of the in-memory dictionaries in the installed_repository_manager because changing the state from
+ # error to uninstalled requires no in-memory changes..
return removed, error_message
def remove_tool_dependency_installation_directory( dependency_install_dir ):
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a static/scripts/galaxy.workflow_editor.canvas.js
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -360,7 +360,7 @@
workflow.has_changes = true;
canvas_manager.draw_overview();
})))
- .tooltip({delay:500, title: "Flag this as a workflow output. All non-flagged outputs will be hidden." });
+ .tooltip({delay:500, title: "Mark dataset as a workflow output. All unmarked datasets will be hidden." });
callout.css({
top: '50%',
margin:'-8px 0px 0px 0px',
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a static/scripts/mvc/base-mvc.js
--- a/static/scripts/mvc/base-mvc.js
+++ b/static/scripts/mvc/base-mvc.js
@@ -173,6 +173,7 @@
//==============================================================================
var HiddenUntilActivatedViewMixin = /** @lends hiddenUntilActivatedMixin# */{
+//TODO: since this is a mixin, consider moving toggle, hidden into HUAVOptions
/** */
hiddenUntilActivated : function( $activator, options ){
@@ -184,7 +185,9 @@
showSpeed : 'fast'
};
_.extend( this.HUAVOptions, options || {});
+ /** has this been shown already (and onshowFirstTime called)? */
this.HUAVOptions.hasBeenShown = this.HUAVOptions.$elementShown.is( ':visible' );
+ this.hidden = this.isHidden();
if( $activator ){
var mixin = this;
@@ -194,21 +197,36 @@
}
},
+ isHidden : function(){
+ return ( this.HUAVOptions.$elementShown.is( ':hidden' ) );
+ },
+
/** */
toggle : function(){
// can be called manually as well with normal toggle arguments
- if( this.HUAVOptions.$elementShown.is( ':hidden' ) ){
+ //TODO: better as a callback (when the show/hide is actually done)
+ // show
+ if( this.hidden ){
// fire the optional fns on the first/each showing - good for render()
if( !this.HUAVOptions.hasBeenShown ){
if( _.isFunction( this.HUAVOptions.onshowFirstTime ) ){
this.HUAVOptions.hasBeenShown = true;
this.HUAVOptions.onshowFirstTime.call( this );
}
- } else {
- if( _.isFunction( this.HUAVOptions.onshow ) ){
- this.HUAVOptions.onshow.call( this );
- }
}
+ if( _.isFunction( this.HUAVOptions.onshow ) ){
+ this.HUAVOptions.onshow.call( this );
+ this.trigger( 'hiddenUntilActivated:shown', this );
+ }
+ this.hidden = false;
+
+ // hide
+ } else {
+ if( _.isFunction( this.HUAVOptions.onhide ) ){
+ this.HUAVOptions.onhide.call( this );
+ this.trigger( 'hiddenUntilActivated:hidden', this );
+ }
+ this.hidden = true;
}
return this.HUAVOptions.showFn.apply( this.HUAVOptions.$elementShown, arguments );
}
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a static/scripts/mvc/dataset/hda-base.js
--- a/static/scripts/mvc/dataset/hda-base.js
+++ b/static/scripts/mvc/dataset/hda-base.js
@@ -46,11 +46,11 @@
/** is the view currently in selection mode? */
this.selectable = attributes.selectable || false;
/** is the view currently selected? */
- this.selected = attributes.selected || false;
- /** is the body of this hda view expanded/not. */
- this.expanded = attributes.expanded || false;
- /** is the body of this hda view expanded/not. */
- this.draggable = attributes.draggable || false;
+ this.selected = attributes.selected || false;
+ /** is the body of this hda view expanded/not? */
+ this.expanded = attributes.expanded || false;
+ /** is the body of this hda view expanded/not? */
+ this.draggable = attributes.draggable || false;
this._setUpListeners();
},
@@ -423,7 +423,7 @@
toggleBodyVisibility : function( event, expand ){
// bail (with propagation) if keydown and not space or enter
var KEYCODE_SPACE = 32, KEYCODE_RETURN = 13;
- if( ( event.type === 'keydown' )
+ if( event && ( event.type === 'keydown' )
&& !( event.keyCode === KEYCODE_SPACE || event.keyCode === KEYCODE_RETURN ) ){
return true;
}
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/70aca9eee9d4/
Changeset: 70aca9eee9d4
User: martenson
Date: 2013-12-16 00:22:01
Summary: activation link tweak: show proper message when trying to activate already active account
Affected #: 1 file
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 70aca9eee9d442a88e49dc99e08ff3b6bbea73d0 lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -826,19 +826,22 @@
if email is None or activation_token is None:
# We don't have the email or activation_token, show error.
- return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
+ return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
else:
# Find the user
user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email==email ).first()
+ # If the user is active already don't try to activate
+ if user.active == True:
+ return trans.show_ok_message( "Your account is already active. Nothing has changed. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
if user.activation_token == activation_token:
user.activation_token = None
user.active = True
trans.sa_session.add(user)
trans.sa_session.flush()
- return trans.show_ok_message( "Your account has been successfully activated!<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_ok_message( "Your account has been successfully activated! <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
else:
# Tokens don't match. Activation is denied.
- return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
return
def __get_user_type_form_definition( self, trans, user=None, **kwd ):
https://bitbucket.org/galaxy/galaxy-central/commits/d5be36722eb3/
Changeset: d5be36722eb3
Branch: data_library
User: martenson
Date: 2013-12-16 17:14:59
Summary: Closed branch data_library
Affected #: 0 files
https://bitbucket.org/galaxy/galaxy-central/commits/2b83e7980707/
Changeset: 2b83e7980707
User: martenson
Date: 2013-12-16 17:42:28
Summary: closing data_library branch
Affected #: 0 files
https://bitbucket.org/galaxy/galaxy-central/commits/0b2f53ad1a20/
Changeset: 0b2f53ad1a20
User: martenson
Date: 2013-12-16 18:01:35
Summary: Merge own default 2nd head
Affected #: 1 file
diff -r 2b83e798070736ea7279d5e4e33194f66f8ef7df -r 0b2f53ad1a2025dbfd7c15a76f329fc5e8a859ef lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -826,19 +826,22 @@
if email is None or activation_token is None:
# We don't have the email or activation_token, show error.
- return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
+ return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
else:
# Find the user
user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email==email ).first()
+ # If the user is active already don't try to activate
+ if user.active == True:
+ return trans.show_ok_message( "Your account is already active. Nothing has changed. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
if user.activation_token == activation_token:
user.activation_token = None
user.active = True
trans.sa_session.add(user)
trans.sa_session.flush()
- return trans.show_ok_message( "Your account has been successfully activated!<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_ok_message( "Your account has been successfully activated! <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
else:
# Tokens don't match. Activation is denied.
- return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
return
def __get_user_type_form_definition( self, trans, user=None, **kwd ):
https://bitbucket.org/galaxy/galaxy-central/commits/16d53da082b8/
Changeset: 16d53da082b8
User: martenson
Date: 2013-12-17 19:22:55
Summary: added security checks when downloading lddas in archives
Affected #: 1 file
diff -r 0b2f53ad1a2025dbfd7c15a76f329fc5e8a859ef -r 16d53da082b89db1318a2a688aa8a18470b02f59 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -13,15 +13,17 @@
import urllib
import urllib2
import zipfile
+from paste.httpexceptions import HTTPBadRequest
+from galaxy.exceptions import ItemAccessibilityException, MessageException, ItemDeletionException
from galaxy.security import Action
from galaxy import util, web
from galaxy.util.streamball import StreamBall
-from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
+from galaxy.web.base.controller import BaseAPIController, UsesVisualizationMixin
import logging
log = logging.getLogger( __name__ )
-class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
+class LibraryDatasetsController( BaseAPIController, UsesVisualizationMixin ):
@web.expose_api
def show( self, trans, id, **kwd ):
@@ -31,7 +33,7 @@
"""
# Get dataset.
try:
- dataset = self.get_library_dataset( trans, id = id )
+ dataset = self.get_library_dataset( trans, id = id, check_ownership=False, check_accessible=True )
except Exception, e:
return str( e )
try:
@@ -65,12 +67,22 @@
datasets_to_download = util.listify( datasets_to_download )
for dataset_id in datasets_to_download:
try:
- ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
- assert not ldda.dataset.purged
+ ldda = self.get_hda_or_ldda( trans, hda_ldda='ldda', dataset_id=dataset_id )
lddas.append( ldda )
- except:
- ldda = None
- message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
+ except ItemAccessibilityException:
+ trans.response.status = 403
+ return 'Insufficient rights to access library dataset with id: (%s)' % str( dataset_id )
+ except MessageException:
+ trans.response.status = 400
+ return 'Wrong library dataset id: (%s)' % str( dataset_id )
+ except ItemDeletionException:
+ trans.response.status = 400
+ return 'The item with library dataset id: (%s) is deleted' % str( dataset_id )
+ except HTTPBadRequest, e:
+ return 'http bad request' + str( e.err_msg )
+ except Exception, e:
+ trans.response.status = 500
+ return 'error of unknown kind' + str( e )
if format in [ 'zip','tgz','tbz' ]:
error = False
https://bitbucket.org/galaxy/galaxy-central/commits/2b4877fc1024/
Changeset: 2b4877fc1024
User: martenson
Date: 2013-12-17 19:35:08
Summary: exception catching rewritten, it is now behaving better, not good but better than before
Affected #: 1 file
diff -r 16d53da082b89db1318a2a688aa8a18470b02f59 -r 2b4877fc1024180fadb5128187d447c0b84c7cd9 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the dataset from library.
+API operations on the datasets from library.
"""
import glob
import logging
@@ -35,6 +35,7 @@
try:
dataset = self.get_library_dataset( trans, id = id, check_ownership=False, check_accessible=True )
except Exception, e:
+ trans.response.status = 500
return str( e )
try:
# Default: return dataset as dict.
@@ -43,24 +44,22 @@
rval = "Error in dataset API at listing contents: " + str( e )
log.error( rval + ": %s" % str(e), exc_info=True )
trans.response.status = 500
+ return "Error in dataset API at listing contents: " + str( e )
rval['id'] = trans.security.encode_id(rval['id']);
rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
-
+ trans.response.status = 200
return rval
@web.expose
def download( self, trans, format, **kwd ):
"""
- POST /api/libraries/datasets/download/{format}
- POST data: ldda_ids = []
- Downloads dataset(s) in the requested format.
+ GET /api/libraries/datasets/download/{format}
+ GET multiple params: ldda_ids = []
+ Downloads dataset(s) in the requested format or plain.
"""
lddas = []
-# is_admin = trans.user_is_admin()
-# current_user_roles = trans.get_current_user_roles()
-
datasets_to_download = kwd['ldda_ids%5B%5D']
if ( datasets_to_download != None ):
@@ -111,15 +110,13 @@
archive = StreamBall( 'w|bz2' )
outext = 'tbz2'
except ( OSError, zipfile.BadZipfile ):
- error = True
log.exception( "Unable to create archive for download" )
- message = "Unable to create archive for download, please report this error"
- status = 'error'
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
except:
- error = True
log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
- message = "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
- status = 'error'
+ trans.response.status = 500
+ return "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
if not error:
composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
seen = []
@@ -150,11 +147,9 @@
try:
archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
except IOError:
- error = True
log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
- message = "Unable to create archive for download, please report this error"
- status = 'error'
- continue
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
for fpath in flist:
efp,fname = os.path.split(fpath)
@@ -163,19 +158,16 @@
try:
archive.add( fpath,fname )
except IOError:
- error = True
log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
- message = "Unable to create archive for download, please report this error"
- status = 'error'
- continue
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
else: # simple case
try:
archive.add( ldda.dataset.file_name, path )
except IOError:
- error = True
log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
- message = "Unable to create archive for download, please report this error"
- status = 'error'
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
if not error:
lname = 'selected_dataset'
fname = lname.replace( ' ', '_' ) + '_files'
@@ -186,15 +178,18 @@
archive = util.streamball.ZipBall(tmpf, tmpd)
archive.wsgi_status = trans.response.wsgi_status()
archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
return archive.stream
else:
trans.response.set_content_type( "application/x-tar" )
trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
archive.wsgi_status = trans.response.wsgi_status()
archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
return archive.stream
elif format == 'uncompressed':
if len(lddas) != 1:
+ trans.response.status = 400
return 'Wrong request'
else:
single_dataset = lddas[0]
@@ -206,8 +201,11 @@
fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
try:
+ trans.response.status = 200
return open( single_dataset.file_name )
except:
+ trans.response.status = 500
return 'This dataset contains no content'
else:
- return 'Wrong format';
+ trans.response.status = 400
+ return 'Wrong format parameter specified';
https://bitbucket.org/galaxy/galaxy-central/commits/6f89cbaeb29c/
Changeset: 6f89cbaeb29c
User: martenson
Date: 2013-12-17 21:58:40
Summary: fix for errors thrown when requested underlying file of dataset does not exist; even better exception handling for ldda-archive downloading
Affected #: 2 files
diff -r 2b4877fc1024180fadb5128187d447c0b84c7cd9 -r 6f89cbaeb29c33ae8d662d84965bf3bdee7f179d lib/galaxy/util/streamball.py
--- a/lib/galaxy/util/streamball.py
+++ b/lib/galaxy/util/streamball.py
@@ -3,6 +3,7 @@
"""
import os
import logging, tarfile
+from galaxy.exceptions import ObjectNotFound
log = logging.getLogger( __name__ )
@@ -14,8 +15,12 @@
self.mode = mode
self.wsgi_status = None
self.wsgi_headeritems = None
- def add( self, file, relpath ):
- self.members[file] = relpath
+ def add( self, file, relpath, check_file=False):
+ if check_file and len(file)>0:
+ if not os.path.isfile(file):
+ raise ObjectNotFound
+ else:
+ self.members[file] = relpath
def stream( self, environ, start_response ):
response_write = start_response( self.wsgi_status, self.wsgi_headeritems )
class tarfileobj:
diff -r 2b4877fc1024180fadb5128187d447c0b84c7cd9 -r 6f89cbaeb29c33ae8d662d84965bf3bdee7f179d lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -14,7 +14,7 @@
import urllib2
import zipfile
from paste.httpexceptions import HTTPBadRequest
-from galaxy.exceptions import ItemAccessibilityException, MessageException, ItemDeletionException
+from galaxy.exceptions import ItemAccessibilityException, MessageException, ItemDeletionException, ObjectNotFound
from galaxy.security import Action
from galaxy import util, web
from galaxy.util.streamball import StreamBall
@@ -84,7 +84,7 @@
return 'error of unknown kind' + str( e )
if format in [ 'zip','tgz','tbz' ]:
- error = False
+ # error = False
killme = string.punctuation + string.whitespace
trantab = string.maketrans(killme,'_'*len(killme))
try:
@@ -117,12 +117,12 @@
log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
trans.response.status = 500
return "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
- if not error:
+ if True:
composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
seen = []
for ldda in lddas:
- if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
- continue
+ # if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
+ # continue
ext = ldda.extension
is_composite = ext in composite_extensions
path = ""
@@ -135,6 +135,7 @@
path = os.path.join( parent_folder.name, path )
parent_folder = parent_folder.parent
path += ldda.name
+
while path in seen:
path += '_'
seen.append( path )
@@ -145,30 +146,61 @@
if zpathext == '':
zpath = '%s.html' % zpath # fake the real nature of the html file
try:
- archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, zpath ) # add the primary of a composite set
+ else:
+ archive.add( ldda.dataset.file_name, zpath, check_file=True ) # add the primary of a composite set
except IOError:
log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
trans.response.status = 500
return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
for fpath in flist:
efp,fname = os.path.split(fpath)
if fname > '':
fname = fname.translate(trantab)
try:
- archive.add( fpath,fname )
+ if format=='zip':
+ archive.add( fpath,fname )
+ else:
+ archive.add( fpath,fname, check_file=True )
except IOError:
log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
trans.response.status = 500
return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % fpath )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
else: # simple case
try:
- archive.add( ldda.dataset.file_name, path )
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, path )
+ else:
+ archive.add( ldda.dataset.file_name, path, check_file=True )
except IOError:
log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
trans.response.status = 500
return "Unable to create archive for download, please report this error"
- if not error:
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+
+ if True:
lname = 'selected_dataset'
fname = lname.replace( ' ', '_' ) + '_files'
if format == 'zip':
https://bitbucket.org/galaxy/galaxy-central/commits/8090554de504/
Changeset: 8090554de504
User: martenson
Date: 2013-12-18 17:41:24
Summary: minor cosmetic changes
Affected #: 2 files
diff -r 6f89cbaeb29c33ae8d662d84965bf3bdee7f179d -r 8090554de504ffa05e744fdca051b9350aeae93f static/scripts/utils/galaxy.uploadbox.js
--- a/static/scripts/utils/galaxy.uploadbox.js
+++ b/static/scripts/utils/galaxy.uploadbox.js
@@ -353,3 +353,4 @@
};
}
})(jQuery);
+
diff -r 6f89cbaeb29c33ae8d662d84965bf3bdee7f179d -r 8090554de504ffa05e744fdca051b9350aeae93f static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster'];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster' ];
var _ = grunt.util._;
var fmt = _.sprintf;
https://bitbucket.org/galaxy/galaxy-central/commits/00fb5740d971/
Changeset: 00fb5740d971
User: martenson
Date: 2013-12-19 18:44:34
Summary: refactor + docs
Affected #: 2 files
diff -r 8090554de504ffa05e744fdca051b9350aeae93f -r 00fb5740d971318d4384e69fc2fcc6b2318b2d44 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
@@ -297,6 +297,14 @@
:undoc-members:
:show-inheritance:
+:mod:`lda_datasets` Module
+--------------------------
+
+.. automodule:: galaxy.webapps.galaxy.api.lda_datasets
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
:mod:`libraries` Module
-----------------------
diff -r 8090554de504ffa05e744fdca051b9350aeae93f -r 00fb5740d971318d4384e69fc2fcc6b2318b2d44 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -28,8 +28,17 @@
@web.expose_api
def show( self, trans, id, **kwd ):
"""
- GET /api/libraries/datasets/{encoded_dataset_id}
- Displays information about the dataset identified by the lda ID.
+ show( self, trans, id, **kwd )
+ * GET /api/libraries/datasets/{encoded_dataset_id}:
+ Displays information about the dataset identified by the encoded id.
+
+
+ :type id: an encoded id string
+ :param id: the encoded id of the dataset to query
+
+ :rtype: dictionary
+ :returns: detailed dataset information from
+ :func:`galaxy.web.base.controller.UsesVisualizationMixin.get_library_dataset.to_dict()`
"""
# Get dataset.
try:
@@ -55,9 +64,25 @@
@web.expose
def download( self, trans, format, **kwd ):
"""
- GET /api/libraries/datasets/download/{format}
- GET multiple params: ldda_ids = []
- Downloads dataset(s) in the requested format or plain.
+ download( self, trans, format, **kwd )
+ * GET /api/libraries/datasets/download/{format}
+
+ .. code-block::
+ example:
+ GET localhost:8080/api/libraries/datasets/download/tbz?ldda_ids%255B%255D=a0d84b45643a2678&ldda_ids%255B%255D=fe38c84dcd46c828
+
+ :type format: string
+ :param format: string representing requested archive format
+
+ .. note:: supported formats are: zip, tgz, tbz, uncompressed
+
+ :type lddas[]: an array
+ :param lddas[]: an array of encoded ids
+
+ :rtype: file
+ :returns: either archive with the requested datasets packed inside or a single uncompressed dataset
+
+ :raises: MessageException, ItemDeletionException, ItemAccessibilityException, HTTPBadRequest, OSError, IOError, ObjectNotFound
"""
lddas = []
datasets_to_download = kwd['ldda_ids%5B%5D']
@@ -117,108 +142,101 @@
log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
trans.response.status = 500
return "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
- if True:
- composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
- seen = []
- for ldda in lddas:
- # if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
- # continue
- ext = ldda.extension
- is_composite = ext in composite_extensions
- path = ""
- parent_folder = ldda.library_dataset.folder
- while parent_folder is not None:
- # Exclude the now-hidden "root folder"
- if parent_folder.parent is None:
- path = os.path.join( parent_folder.library_root[0].name, path )
- break
- path = os.path.join( parent_folder.name, path )
- parent_folder = parent_folder.parent
- path += ldda.name
-
- while path in seen:
- path += '_'
- seen.append( path )
- zpath = os.path.split(path)[-1] # comes as base_name/fname
- outfname,zpathext = os.path.splitext(zpath)
- if is_composite:
- # need to add all the components from the extra_files_path to the zip
- if zpathext == '':
- zpath = '%s.html' % zpath # fake the real nature of the html file
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite: # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, zpath ) # add the primary of a composite set
+ else:
+ archive.add( ldda.dataset.file_name, zpath, check_file=True ) # add the primary of a composite set
+ except IOError:
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
try:
if format=='zip':
- archive.add( ldda.dataset.file_name, zpath ) # add the primary of a composite set
- else:
- archive.add( ldda.dataset.file_name, zpath, check_file=True ) # add the primary of a composite set
+ archive.add( fpath,fname )
+ else:
+ archive.add( fpath,fname, check_file=True )
except IOError:
- log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
trans.response.status = 500
return "Unable to create archive for download, please report this error"
except ObjectNotFound:
- log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ log.exception( "Requested dataset %s does not exist on the host." % fpath )
trans.response.status = 500
- return "Requested dataset does not exist on the host."
- except:
- trans.response.status = 500
- return "Unknown error, please report this error"
- flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
- for fpath in flist:
- efp,fname = os.path.split(fpath)
- if fname > '':
- fname = fname.translate(trantab)
- try:
- if format=='zip':
- archive.add( fpath,fname )
- else:
- archive.add( fpath,fname, check_file=True )
- except IOError:
- log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
- trans.response.status = 500
- return "Unable to create archive for download, please report this error"
- except ObjectNotFound:
- log.exception( "Requested dataset %s does not exist on the host." % fpath )
- trans.response.status = 500
- return "Requested dataset does not exist on the host."
- except:
- trans.response.status = 500
- return "Unknown error, please report this error"
- else: # simple case
- try:
- if format=='zip':
- archive.add( ldda.dataset.file_name, path )
- else:
- archive.add( ldda.dataset.file_name, path, check_file=True )
- except IOError:
- log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
- trans.response.status = 500
- return "Unable to create archive for download, please report this error"
- except ObjectNotFound:
- log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
- trans.response.status = 500
- return "Requested dataset does not exist on the host."
+ return "Requested dataset does not exist on the host."
except:
trans.response.status = 500
return "Unknown error, please report this error"
-
- if True:
- lname = 'selected_dataset'
- fname = lname.replace( ' ', '_' ) + '_files'
- if format == 'zip':
- archive.close()
- trans.response.set_content_type( "application/octet-stream" )
- trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
- archive = util.streamball.ZipBall(tmpf, tmpd)
- archive.wsgi_status = trans.response.wsgi_status()
- archive.wsgi_headeritems = trans.response.wsgi_headeritems()
- trans.response.status = 200
- return archive.stream
- else:
- trans.response.set_content_type( "application/x-tar" )
- trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
- archive.wsgi_status = trans.response.wsgi_status()
- archive.wsgi_headeritems = trans.response.wsgi_headeritems()
- trans.response.status = 200
- return archive.stream
+ else: # simple case
+ try:
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, path )
+ else:
+ archive.add( ldda.dataset.file_name, path, check_file=True )
+ except IOError:
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
+ return archive.stream
elif format == 'uncompressed':
if len(lddas) != 1:
trans.response.status = 400
https://bitbucket.org/galaxy/galaxy-central/commits/c1963ee06cb0/
Changeset: c1963ee06cb0
User: martenson
Date: 2013-12-19 18:48:22
Summary: Merged in martenson/galaxy-central-marten (pull request #273)
Initial Data Libraries
Affected #: 24 files
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b 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
@@ -302,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
-----------------------
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1827,7 +1827,7 @@
class Library( object, Dictifiable ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
dict_collection_visible_keys = ( 'id', 'name' )
- dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
+ dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id' )
def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
@@ -1894,7 +1894,7 @@
return name
class LibraryFolder( object, Dictifiable ):
- dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
+ dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time' )
def __init__( self, name=None, description=None, item_count=0, order_id=None ):
self.name = name or "Unnamed folder"
self.description = description
@@ -2060,6 +2060,7 @@
genome_build = ldda.dbkey,
misc_info = ldda.info,
misc_blurb = ldda.blurb,
+ peek = ( lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None )( ldda ),
template_data = template_data )
if ldda.dataset.uuid is None:
rval['uuid'] = None
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1852,8 +1852,9 @@
table = self.table
trans = conn.begin()
try:
- next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
- table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
+ current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
+ next_hid = current_hid + 1
+ table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
trans.commit()
return next_hid
except:
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/util/streamball.py
--- a/lib/galaxy/util/streamball.py
+++ b/lib/galaxy/util/streamball.py
@@ -3,6 +3,7 @@
"""
import os
import logging, tarfile
+from galaxy.exceptions import ObjectNotFound
log = logging.getLogger( __name__ )
@@ -14,8 +15,12 @@
self.mode = mode
self.wsgi_status = None
self.wsgi_headeritems = None
- def add( self, file, relpath ):
- self.members[file] = relpath
+ def add( self, file, relpath, check_file=False):
+ if check_file and len(file)>0:
+ if not os.path.isfile(file):
+ raise ObjectNotFound
+ else:
+ self.members[file] = relpath
def stream( self, environ, start_response ):
response_write = start_response( self.wsgi_status, self.wsgi_headeritems )
class tarfileobj:
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/api/datasets.py
--- a/lib/galaxy/webapps/galaxy/api/datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a dataset.
+API operations on the contents of a history dataset.
"""
from galaxy import web
from galaxy.visualization.data_providers.genome import FeatureLocationIndexDataProvider
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b 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
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a library.
+API operations on the contents of a folder.
"""
import logging, os, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
@@ -11,67 +11,122 @@
log = logging.getLogger( __name__ )
class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ):
+ """
+ Class controls retrieval, creation and updating of folder contents.
+ """
+
+ def load_folder_contents( self, trans, folder ):
+ """
+ Loads all contents of the folder (folders and data sets) but only in the first level.
+ """
+ current_user_roles = trans.get_current_user_roles()
+ is_admin = trans.user_is_admin()
+ content_items = []
+ for subfolder in folder.active_folders:
+ if not is_admin:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
+ if (is_admin or can_access) and not subfolder.deleted:
+ subfolder.api_type = 'folder'
+ content_items.append( subfolder )
+ for dataset in folder.datasets:
+ if not is_admin:
+ can_access = trans.app.security_agent.can_access_dataset( current_user_roles, dataset.library_dataset_dataset_association.dataset )
+ if (is_admin or can_access) and not dataset.deleted:
+ dataset.api_type = 'file'
+ content_items.append( dataset )
+ return content_items
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
Displays a collection (list) of a folder's contents (files and folders).
- The /api/library_contents/{encoded_library_id}/contents
- lists everything in a library recursively, which is not what
- we want here. We could add a parameter to use the recursive
- style, but this is meant to act similar to an "ls" directory listing.
+ Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
+ Full path is provided as a separate object in response providing data for breadcrumb path building.
"""
- rval = []
+ folder_container = []
current_user_roles = trans.get_current_user_roles()
- def traverse( folder ):
- admin = trans.user_is_admin()
- rval = []
- for subfolder in folder.active_folders:
- if not admin:
- can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
- if (admin or can_access) and not subfolder.deleted:
- subfolder.api_type = 'folder'
- rval.append( subfolder )
- for ld in folder.datasets:
- if not admin:
- can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ld.library_dataset_dataset_association.dataset )
- if (admin or can_access) and not ld.deleted:
- ld.api_type = 'file'
- rval.append( ld )
- return rval
-
- try:
- decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
- except TypeError:
- trans.response.status = 400
- return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+ if ( folder_id.startswith( 'F' ) ):
+ try:
+ decoded_folder_id = trans.security.decode_id( folder_id[1:] )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
- parent_library = folder.parent_library
except:
folder = None
- log.error( "FolderContentsController.index: Unable to retrieve folder %s"
- % folder_id )
+ log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
- # TODO: Find the API's path to this folder if necessary.
- # This was needed in recursive descent, but it's not needed
- # for "ls"-style content checking:
- if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ # We didn't find the folder or user does not have an access to it.
+ if not folder:
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, folder.id ) )
+ trans.response.status = 400
+ return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ path_to_root = []
+ def build_path ( folder ):
+ """
+ Search the path upwards recursively and load the whole route of names and ids for breadcrumb purposes.
+ """
+ path_to_root = []
+ # We are almost in root
+ if folder.parent_id is None:
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ else:
+ # We add the current folder and traverse up one folder.
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
+ path_to_root.extend( build_path( upper_folder ) )
+ return path_to_root
+
+ # Return the reversed path so it starts with the library node.
+ full_path = build_path( folder )[::-1]
+ folder_container.append( dict( full_path = full_path ) )
+
+ folder_contents = []
+ time_updated = ''
+ time_created = ''
+ # Go through every item in the folder and include its meta-data.
+ for content_item in self.load_folder_contents( trans, folder ):
+# rval = content_item.to_dict()
+ return_item = {}
+ encoded_id = trans.security.encode_id( content_item.id )
+ time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
+
+ # For folder return also hierarchy values
+ if content_item.api_type == 'folder':
+ encoded_id = 'F' + encoded_id
+# time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count ) )
- for content in traverse( folder ):
- encoded_id = trans.security.encode_id( content.id )
- if content.api_type == 'folder':
- encoded_id = 'F' + encoded_id
- rval.append( dict( id = encoded_id,
- type = content.api_type,
- name = content.name,
- url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
- return rval
+ if content_item.api_type == 'file':
+ library_dataset_dict = content_item.to_dict()
+ library_dataset_dict['data_type']
+ library_dataset_dict['file_size']
+ library_dataset_dict['date_uploaded']
+ return_item.update ( dict ( data_type = library_dataset_dict['data_type'],
+ file_size = library_dataset_dict['file_size'],
+ date_uploaded = library_dataset_dict['date_uploaded'] ) )
+
+ # For every item return also the default meta-data
+ return_item.update( dict( id = encoded_id,
+ type = content_item.api_type,
+ name = content_item.name,
+ time_updated = time_updated,
+ time_created = time_created
+ ) )
+ folder_contents.append( return_item )
+ # Put the data in the container
+ folder_container.append( dict( folder_contents = folder_contents ) )
+ return folder_container
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -0,0 +1,261 @@
+"""
+API operations on the datasets from library.
+"""
+import glob
+import logging
+import operator
+import os
+import os.path
+import string
+import sys
+import tarfile
+import tempfile
+import urllib
+import urllib2
+import zipfile
+from paste.httpexceptions import HTTPBadRequest
+from galaxy.exceptions import ItemAccessibilityException, MessageException, ItemDeletionException, ObjectNotFound
+from galaxy.security import Action
+from galaxy import util, web
+from galaxy.util.streamball import StreamBall
+from galaxy.web.base.controller import BaseAPIController, UsesVisualizationMixin
+
+import logging
+log = logging.getLogger( __name__ )
+
+class LibraryDatasetsController( BaseAPIController, UsesVisualizationMixin ):
+
+ @web.expose_api
+ def show( self, trans, id, **kwd ):
+ """
+ show( self, trans, id, **kwd )
+ * GET /api/libraries/datasets/{encoded_dataset_id}:
+ Displays information about the dataset identified by the encoded id.
+
+
+ :type id: an encoded id string
+ :param id: the encoded id of the dataset to query
+
+ :rtype: dictionary
+ :returns: detailed dataset information from
+ :func:`galaxy.web.base.controller.UsesVisualizationMixin.get_library_dataset.to_dict()`
+ """
+ # Get dataset.
+ try:
+ dataset = self.get_library_dataset( trans, id = id, check_ownership=False, check_accessible=True )
+ except Exception, e:
+ trans.response.status = 500
+ return str( e )
+ try:
+ # Default: return dataset as dict.
+ rval = dataset.to_dict()
+ except Exception, e:
+ rval = "Error in dataset API at listing contents: " + str( e )
+ log.error( rval + ": %s" % str(e), exc_info=True )
+ trans.response.status = 500
+ return "Error in dataset API at listing contents: " + str( e )
+
+ rval['id'] = trans.security.encode_id(rval['id']);
+ rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
+ rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
+ trans.response.status = 200
+ return rval
+
+ @web.expose
+ def download( self, trans, format, **kwd ):
+ """
+ download( self, trans, format, **kwd )
+ * GET /api/libraries/datasets/download/{format}
+
+ .. code-block::
+ example:
+ GET localhost:8080/api/libraries/datasets/download/tbz?ldda_ids%255B%255D=a0d84b45643a2678&ldda_ids%255B%255D=fe38c84dcd46c828
+
+ :type format: string
+ :param format: string representing requested archive format
+
+ .. note:: supported formats are: zip, tgz, tbz, uncompressed
+
+ :type lddas[]: an array
+ :param lddas[]: an array of encoded ids
+
+ :rtype: file
+ :returns: either archive with the requested datasets packed inside or a single uncompressed dataset
+
+ :raises: MessageException, ItemDeletionException, ItemAccessibilityException, HTTPBadRequest, OSError, IOError, ObjectNotFound
+ """
+ lddas = []
+ datasets_to_download = kwd['ldda_ids%5B%5D']
+
+ if ( datasets_to_download != None ):
+ datasets_to_download = util.listify( datasets_to_download )
+ for dataset_id in datasets_to_download:
+ try:
+ ldda = self.get_hda_or_ldda( trans, hda_ldda='ldda', dataset_id=dataset_id )
+ lddas.append( ldda )
+ except ItemAccessibilityException:
+ trans.response.status = 403
+ return 'Insufficient rights to access library dataset with id: (%s)' % str( dataset_id )
+ except MessageException:
+ trans.response.status = 400
+ return 'Wrong library dataset id: (%s)' % str( dataset_id )
+ except ItemDeletionException:
+ trans.response.status = 400
+ return 'The item with library dataset id: (%s) is deleted' % str( dataset_id )
+ except HTTPBadRequest, e:
+ return 'http bad request' + str( e.err_msg )
+ except Exception, e:
+ trans.response.status = 500
+ return 'error of unknown kind' + str( e )
+
+ if format in [ 'zip','tgz','tbz' ]:
+ # error = False
+ killme = string.punctuation + string.whitespace
+ trantab = string.maketrans(killme,'_'*len(killme))
+ try:
+ outext = 'zip'
+ if format == 'zip':
+ # Can't use mkstemp - the file must not exist first
+ tmpd = tempfile.mkdtemp()
+ util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
+ tmpf = os.path.join( tmpd, 'library_download.' + format )
+ if trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
+ else:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+ archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
+ elif format == 'tgz':
+ if trans.app.config.upstream_gzip:
+ archive = StreamBall( 'w|' )
+ outext = 'tar'
+ else:
+ archive = StreamBall( 'w|gz' )
+ outext = 'tgz'
+ elif format == 'tbz':
+ archive = StreamBall( 'w|bz2' )
+ outext = 'tbz2'
+ except ( OSError, zipfile.BadZipfile ):
+ log.exception( "Unable to create archive for download" )
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except:
+ log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
+ trans.response.status = 500
+ return "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite: # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, zpath ) # add the primary of a composite set
+ else:
+ archive.add( ldda.dataset.file_name, zpath, check_file=True ) # add the primary of a composite set
+ except IOError:
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
+ try:
+ if format=='zip':
+ archive.add( fpath,fname )
+ else:
+ archive.add( fpath,fname, check_file=True )
+ except IOError:
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % fpath )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+ else: # simple case
+ try:
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, path )
+ else:
+ archive.add( ldda.dataset.file_name, path, check_file=True )
+ except IOError:
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
+ return archive.stream
+ elif format == 'uncompressed':
+ if len(lddas) != 1:
+ trans.response.status = 400
+ return 'Wrong request'
+ else:
+ single_dataset = lddas[0]
+ trans.response.set_content_type( single_dataset.get_mime() )
+ fStat = os.stat( ldda.file_name )
+ trans.response.headers[ 'Content-Length' ] = int( fStat.st_size )
+ valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ fname = ldda.name
+ fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
+ try:
+ trans.response.status = 200
+ return open( single_dataset.file_name )
+ except:
+ trans.response.status = 500
+ return 'This dataset contains no content'
+ else:
+ trans.response.status = 400
+ return 'Wrong format parameter specified';
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -49,9 +49,10 @@
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
rval = []
for library in query:
- item = library.to_dict()
+ item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
- item['id'] = trans.security.encode_id( item['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
@@ -131,6 +132,9 @@
rval['name'] = name
rval['id'] = encoded_id
return rval
+
+ def edit( self, trans, payload, **kwd ):
+ return "Not implemented yet"
@web.expose_api
def delete( self, trans, id, **kwd ):
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,12 +46,6 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
- webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
- controller='admin_toolshed',
- action='display_image_in_repository',
- repository_id=None,
- image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
@@ -75,22 +69,12 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
webapp.add_route( '/search', controller='search', action='index' )
- # Add the web API
+ # ================
+ # ===== API =====
+ # ================
+
webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
@@ -102,10 +86,6 @@
controller="datasets",
action="display",
conditions=dict(method=["GET"]))
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'user',
'users',
controller='group_users',
@@ -127,11 +107,6 @@
_add_item_tags_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
-
_add_item_annotation_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -141,7 +116,6 @@
_add_item_annotation_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
_add_item_provenance_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -170,6 +144,7 @@
parent_resources=dict( member_name='datatype', collection_name='datatypes' ) )
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
+
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
@@ -188,6 +163,64 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ webapp.mapper.connect( 'show_lda_item',
+ '/api/libraries/datasets/:id',
+ controller='lda_datasets',
+ action='show',
+ conditions=dict( method=[ "GET" ] ) )
+
+ webapp.mapper.connect( 'download_lda_items',
+ '/api/libraries/datasets/download/:format',
+ controller='lda_datasets',
+ action='download',
+ conditions=dict( method=[ "POST", "GET" ] ) )
+
+ webapp.mapper.resource_with_deleted( 'library',
+ 'libraries',
+ path_prefix='/api' )
+ webapp.mapper.resource( 'folder',
+ 'folders',
+ path_prefix='/api' )
+
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
+
+ # ====================
+ # ===== TOOLSHED =====
+ # ====================
+
+ # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
+ webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
+ controller='admin_toolshed',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
@@ -201,6 +234,7 @@
path_prefix='/api',
new={ 'install_repository_revision' : 'POST' },
parent_resources=dict( member_name='tool_shed_repository', collection_name='tool_shed_repositories' ) )
+
# Connect logger from app
if app.trace_logger:
webapp.trace_logger = app.trace_logger
@@ -221,7 +255,7 @@
galaxy.model.mapping.metadata.engine.connection_provider._pool.dispose()
except:
pass
- # Close any pooled database connections before forking
+ # Close any pooled database connections before forking
try:
galaxy.model.tool_shed_install.mapping.metadata.engine.connection_provider._pool.dispose()
except:
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -76,6 +76,17 @@
library_list_grid = LibraryListGrid()
+
+ @web.expose
+ def list( self, trans, **kwd ):
+ params = util.Params( kwd )
+ # define app configuration for generic mako template
+ app = {
+ 'jscript' : "galaxy.library"
+ }
+ # fill template
+ return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -826,19 +826,22 @@
if email is None or activation_token is None:
# We don't have the email or activation_token, show error.
- return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
+ return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
else:
# Find the user
user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email==email ).first()
+ # If the user is active already don't try to activate
+ if user.active == True:
+ return trans.show_ok_message( "Your account is already active. Nothing has changed. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
if user.activation_token == activation_token:
user.activation_token = None
user.active = True
trans.sa_session.add(user)
trans.sa_session.flush()
- return trans.show_ok_message( "Your account has been successfully activated!<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_ok_message( "Your account has been successfully activated! <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
else:
# Tokens don't match. Activation is denied.
- return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
return
def __get_user_type_form_definition( self, trans, user=None, **kwd ):
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b static/scripts/galaxy.library.js
--- /dev/null
+++ b/static/scripts/galaxy.library.js
@@ -0,0 +1,902 @@
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+// === GALAXY LIBRARY MODULE ====
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+
+// global variables
+var view = null;
+var library_router = null;
+var responses = [];
+
+// dependencies
+define([
+ "galaxy.modal",
+ "galaxy.masthead",
+ "utils/galaxy.utils",
+ "libs/toastr"], function(mod_modal, mod_masthead, mod_utils, mod_toastr) {
+
+// MMMMMMMMMMMMMMM
+// === Models ====
+// MMMMMMMMMMMMMMM
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries'
+ });
+
+ // LIBRARIES
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ urlRoot : '/api/libraries/datasets'
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // CONTAINER for folder contents (folders, items and metadata).
+ var FolderContainer = Backbone.Model.extend({
+ defaults : {
+ folder : new Folder(),
+ full_path : "unknown",
+ urlRoot : "/api/folders/",
+ id : "unknown"
+ },
+ parse : function(obj) {
+ this.full_path = obj[0].full_path;
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+ return obj;
+ }
+ })
+
+ // HISTORY ITEM
+ var HistoryItem = Backbone.Model.extend({
+ urlRoot : '/api/histories/'
+ });
+
+ // HISTORY
+ var GalaxyHistory = Backbone.Model.extend({
+ url : '/api/histories/'
+ });
+
+ // HISTORIES
+ var GalaxyHistories = Backbone.Collection.extend({
+ url : '/api/histories',
+ model : GalaxyHistory
+ });
+
+ //ROUTER
+ var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content",
+ "folders/:folder_id/download/:format" : "download"
+ }
+ });
+
+
+// MMMMMMMMMMMMMM
+// === VIEWS ====
+// MMMMMMMMMMMMMM
+
+// galaxy folder
+var FolderContentView = Backbone.View.extend({
+ // main element definition
+ el : '#center',
+ // progress percentage
+ progress: 0,
+ // progress rate per one item
+ progressStep: 1,
+ // last selected history in modal for UX
+ lastSelectedHistory: '',
+ // self modal
+ modal : null,
+ // loaded folders
+ folders : null,
+
+ // initialize
+ initialize : function(){
+ this.folders = [];
+ this.queue = jQuery.Deferred();
+ this.queue.resolve();
+ },
+
+// MMMMMMMMMMMMMMMMMM
+// === TEMPLATES ====
+// MMMMMMMMMMMMMMMMMM
+
+ // set up
+ templateFolder : function (){
+ var tmpl_array = [];
+
+ // CONTAINER
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
+ 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>');
+
+ // TOOLBAR
+ tmpl_array.push('<div id="library_folder_toolbar" >');
+ tmpl_array.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>');
+ tmpl_array.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>');
+
+ tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
+ tmpl_array.push(' </ul>');
+ tmpl_array.push(' </div>');
+
+ tmpl_array.push('</div>');
+
+ // BREADCRUMBS
+ tmpl_array.push('<div class="library_breadcrumb">');
+ tmpl_array.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<span title="You are in this folder"><%- path_item[1] %></span>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('</div>');
+
+ // FOLDER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th class="button_heading">view</th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>data type</th>');
+ tmpl_array.push(' <th>size</th>');
+ tmpl_array.push(' <th>date</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.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><%- content_item.get("name") %>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td>folder</td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>'); // size
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <td>');
+ tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-eye"></span> details');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
+ tmpl_array.push(' <td><%= _.escape(content_item.get("data_type")) %></td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>'); // size
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>'); // time updated
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+ templateDatasetModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div id="dataset_info_modal">');
+ tmpl_array.push(' <table class="table table-striped table-condensed">');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Data type</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("data_type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Genome build</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("genome_build")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Size</th>');
+ tmpl_array.push(' <td><%= _.escape(size) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Date uploaded</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Uploaded by</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr scope="row">');
+ tmpl_array.push(' <th scope="row">Data Lines</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Comment Lines</th>');
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Number of Columns</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Column Types</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' </table>');
+ tmpl_array.push(' <pre class="peek">');
+ tmpl_array.push(' </pre>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ },
+
+ templateHistorySelectInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ templateBulkImportInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ // convert size to nice string
+ size_to_string : function (size)
+ {
+ // identify unit
+ var unit = "";
+ if (size >= 100000000000) { size = size / 100000000000; unit = "TB"; } else
+ if (size >= 100000000) { size = size / 100000000; unit = "GB"; } else
+ if (size >= 100000) { size = size / 100000; unit = "MB"; } else
+ if (size >= 100) { size = size / 100; unit = "KB"; } else
+ { size = size * 10; unit = "b"; }
+ // return formatted string
+ return (Math.round(size) / 10) + unit;
+ },
+
+// MMMMMMMMMMMMMMM
+// === EVENTS ====
+// MMMMMMMMMMMMMMM
+
+ // event binding
+ 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 the folder view
+ render: function (options) {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+
+ view = this;
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+
+ folderContainer.fetch({
+ success: function (container) {
+
+ // prepare nice size strings
+ for (var i = 0; i < folderContainer.attributes.folder.models.length; i++) {
+ var model = folderContainer.attributes.folder.models[i]
+ if (model.get('type') === 'file'){
+ model.set('readable_size', that.size_to_string(model.get('file_size')))
+ }
+ };
+
+ // find the upper id
+ var path = folderContainer.full_path;
+ var upper_folder_id;
+ if (path.length === 1){ // library is above us
+ upper_folder_id = 0;
+ } else {
+ upper_folder_id = path[path.length-2][0];
+ }
+
+ var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
+ // var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
+ that.$el.html(template);
+
+ }
+ })
+ },
+
+ // handles the click on 'open' and 'upper' folder icons
+ navigateToFolder : function(event){
+ var folder_id = $(event.target).attr('data-id');
+ if (typeof folder_id === 'undefined') {
+ return false;
+ } else if (folder_id === '0'){
+ library_router.navigate('#', {trigger: true, replace: true});
+ } else {
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ }
+ },
+
+ //show modal with current dataset info
+ showDatasetDetails : function(event){
+ // prevent default
+ event.preventDefault();
+
+//TODO check whether we already have the data
+
+ //load the ID of the row
+ var id = $(event.target).parent().parent().attr('id');
+
+ //create new item
+ var item = new Item();
+ var histories = new GalaxyHistories();
+ item.id = id;
+ var self = this;
+
+ //fetch the dataset info
+ item.fetch({
+ success: function (item) {
+// TODO can render here already
+ //fetch user histories for import purposes
+ histories.fetch({
+ success: function (histories){self.renderModalAfterFetch(item, histories)}
+ });
+ }
+ });
+ },
+
+ // show the current dataset in a modal
+ 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({
+ 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
+ }
+ });
+ $(".peek").html(item.get("peek"));
+ var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+
+ // preset last selected history if we know it
+ if (self.lastSelectedHistory.length > 0) {
+ $(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
+ }
+
+ // show the prepared modal
+ this.modal.show();
+ },
+
+ // download dataset shown currently in modal
+ downloadCurrent : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var library_dataset_id = [];
+ library_dataset_id.push($('#id_row').attr('data-id'));
+ var url = '/api/libraries/datasets/download/uncompressed';
+ var data = {'ldda_ids' : library_dataset_id};
+
+ // we assume the view is existent
+ folderContentView.processDownload(url, data);
+ this.modal.enableButton('Import');
+ this.modal.enableButton('Download');
+ },
+
+ // import dataset shown currently in modal into selected history
+ importCurrentIntoHistory : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var history_id = $(this.modal.elMain).find('select[name=dataset_import_single] option:selected').val();
+ this.lastSelectedHistory = history_id; //save selected history for further use
+
+ var library_dataset_id = $('#id_row').attr('data-id');
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+
+ // save the dataset into selected history
+ historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
+ mod_toastr.success('Dataset imported');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }, error : function(){
+ mod_toastr.error('An error occured! Dataset not imported. Please try again.')
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }
+ });
+ },
+
+ // select all datasets
+ selectAll : function (event) {
+ var selected = event.target.checked;
+ that = this;
+ // Iterate each checkbox
+ $(':checkbox').each(function () {
+ this.checked = selected;
+ $row = $(this.parentElement.parentElement);
+ // Change color of selected/unselected
+ (selected) ? that.makeDarkRow($row) : that.makeWhiteRow($row);
+ });
+ // Show the tools in menu
+ this.checkTools();
+ },
+
+ // Check checkbox on row itself or row checkbox click
+ selectClickedRow : function (event) {
+ var checkbox = '';
+ var $row;
+ var source;
+ if (event.target.localName === 'input'){
+ checkbox = event.target;
+ $row = $(event.target.parentElement.parentElement);
+ source = 'input';
+ } else if (event.target.localName === 'td') {
+ checkbox = $("#" + event.target.parentElement.id).find(':checkbox')[0];
+ $row = $(event.target.parentElement);
+ source = 'td';
+ }
+ if (checkbox === '') {event.stopPropagation(); return;} // button in row was clicked
+
+ if (checkbox.checked){
+ if (source==='td'){
+ checkbox.checked = '';
+ this.makeWhiteRow($row);
+ } else if (source==='input') {
+ this.makeDarkRow($row);
+ }
+ } else {
+ if (source==='td'){
+ checkbox.checked = 'selected';
+ this.makeDarkRow($row);
+ } else if (source==='input') {
+ this.makeWhiteRow($row);
+ }
+ }
+ this.checkTools();
+ },
+
+ makeDarkRow: function($row){
+ $row.removeClass('light');
+ $row.find('a').removeClass('light');
+ $row.addClass('dark');
+ $row.find('a').addClass('dark');
+ },
+
+ makeWhiteRow: function($row){
+ $row.removeClass('dark');
+ $row.find('a').removeClass('dark');
+ $row.addClass('light');
+ $row.find('a').addClass('light');
+ },
+
+ // show toolbar in case something is selected
+ checkTools : function(){
+ var checkedValues = $('#folder_table').find(':checked');
+ if(checkedValues.length > 0){
+ $('#toolbtn_bulk_import').show();
+ $('#toolbtn_dl').show();
+ } else {
+ $('#toolbtn_bulk_import').hide();
+ $('#toolbtn_dl').hide();
+ }
+
+ },
+
+ // show bulk import modal
+ modalBulkImport : function(){
+ var self = this;
+ // fetch histories
+ var histories = new GalaxyHistories();
+ histories.fetch({
+ success: function (histories){
+ // make modal
+ var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
+ self.modal = new mod_modal.GalaxyModal({
+ title : 'Import into History',
+ body : history_modal_tmpl,
+ buttons : {
+ 'Import' : function() {self.importAllIntoHistory()},
+ 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
+ }
+ });
+ // show the prepared modal
+ self.modal.show();
+ }
+ });
+ },
+
+ // import all selected datasets into history
+ importAllIntoHistory : function (){
+ //disable the button
+ this.modal.disableButton('Import');
+
+ var history_id = $("select[name=dataset_import_bulk] option:selected").val();
+ var history_name = $("select[name=dataset_import_bulk] option:selected").text();
+
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+ var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
+ $(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
+
+ // init the progress bar
+ var progressStep = 100 / dataset_ids.length;
+ this.initProgress(progressStep);
+
+ // prepare the dataset objects to be imported
+ var datasets_to_import = [];
+ for (var i = dataset_ids.length - 1; i >= 0; i--) {
+ library_dataset_id = dataset_ids[i];
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ historyItem.content = library_dataset_id;
+ historyItem.source = 'library';
+ datasets_to_import.push(historyItem);
+ };
+
+ // call the recursive function to call ajax one after each other
+ this.chainCall(datasets_to_import);
+ },
+
+ chainCall: function(history_item_set){
+ var self = this;
+ var popped_item = history_item_set.pop();
+ if (typeof popped_item === "undefined") {
+ mod_toastr.success('All datasets imported');
+ this.modal.hide();
+ // enable button again
+ self.modal.enableButton('Import');
+ return
+ }
+ var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
+ self.updateProgress();
+ responses.push(a1);
+ self.chainCall(history_item_set);
+ });
+ },
+
+ initProgress: function(progressStep){
+ this.progress = 0;
+ this.progressStep = progressStep;
+ },
+ updateProgress: function(){
+ this.progress += this.progressStep;
+ $('.progress-bar').width(Math.round(this.progress) + '%');
+ txt_representation = Math.round(this.progress) + '% Complete';
+ $('.completion_span').text(txt_representation);
+ },
+
+ // progress bar
+ templateProgressBar : function (){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div class="import_text">');
+ tmpl_array.push('Importing selected datasets to history <b><%= _.escape(history_name) %></b>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('<div class="progress">');
+ tmpl_array.push(' <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');
+ tmpl_array.push(' <span class="completion_span">0% Complete</span>');
+ tmpl_array.push(' </div>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+
+ // download selected datasets
+ download : function(folder_id, format){
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+
+ var url = '/api/libraries/datasets/download/' + format;
+ var data = {'ldda_ids' : dataset_ids};
+ this.processDownload(url, data, 'get');
+ },
+
+ // create hidden form and submit through POST to initialize download
+ processDownload: function(url, data, method){
+ //url and data options required
+ if( url && data ){
+ //data can be string of parameters or array/object
+ data = typeof data == 'string' ? data : $.param(data);
+ //split params into form inputs
+ var inputs = '';
+ $.each(data.split('&'), function(){
+ var pair = this.split('=');
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ });
+ //send request
+ $('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
+ .appendTo('body').submit().remove();
+ mod_toastr.info('Your download will begin soon');
+ };
+ },
+
+ // shows modal for creating folder
+ createFolderModal: function(){
+ mod_toastr.info('This will create folder...in the future');
+ }
+
+ });
+
+// galaxy library view
+var GalaxyLibraryview = Backbone.View.extend({
+ el: '#center',
+
+ events: {
+ 'click #create_new_library_btn' : 'show_library_modal'
+ },
+
+ // initialize
+ initialize : function(){
+ },
+
+ // template
+ 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
+ render: function () {
+ //hack to show scrollbars due to #center element inheritance
+ $("#center").css('overflow','auto');
+
+ var that = this;
+ libraries = new Libraries();
+
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template(that.template_library_list(), { libraries : libraries.models });
+ that.$el.html(template);
+ },
+ error: function(model, response){
+ if (response.statusCode().status === 403){
+ mod_toastr.error('Please log in first. Redirecting to login page in 3s.');
+ setTimeout(that.redirectToLogin, 3000);
+ } else {
+ mod_toastr.error('An error occured. Please try again.');
+ }
+ }
+ })
+ },
+
+ redirectToHome: function(){
+ window.location = '../';
+ },
+ redirectToLogin: function(){
+ window.location = '/user/login';
+ },
+
+ // own modal
+ modal : null,
+
+ // show/hide create library modal
+ show_library_modal : function (event){
+ event.preventDefault();
+ event.stopPropagation();
+
+ // create modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal(
+ {
+ title : 'Create New Library',
+ body : this.template_new_library(),
+ buttons : {
+ 'Create' : function() {self.create_new_library_event()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+
+ // show prepared modal
+ this.modal.show();
+ },
+
+ // create the new library from modal
+ create_new_library_event: function(){
+ var libraryDetails = this.serialize_new_library();
+ if (this.validate_new_library(libraryDetails)){
+ var library = new Library();
+ var self = this;
+ library.save(libraryDetails, {
+ success: function (library) {
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ mod_toastr.success('Library created');
+ },
+ error: function(){
+ mod_toastr.error('An error occured :(');
+ }
+ });
+ } else {
+ mod_toastr.error('Library\'s name is missing');
+ }
+ return false;
+ },
+
+ // clear the library modal once saved
+ clear_library_modal : function(){
+ $("input[name='Name']").val('');
+ $("input[name='Description']").val('');
+ $("input[name='Synopsis']").val('');
+ },
+
+ // serialize data from the form
+ serialize_new_library : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val(),
+ synopsis: $("input[name='Synopsis']").val()
+ };
+ },
+
+ // validate new library info
+ validate_new_library: function(libraryDetails){
+ return libraryDetails.name !== '';
+ },
+
+ // template for new library modal
+ 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('');
+ }
+});
+
+// galaxy library wrapper View
+var GalaxyLibrary = Backbone.View.extend({
+ folderContentView : null,
+ galaxyLibraryview : null,
+ initialize : function(){
+
+ folderContentView = new FolderContentView();
+ galaxyLibraryview = new GalaxyLibraryview();
+ library_router = new LibraryRouter();
+
+ library_router.on('route:libraries', function() {
+ // render libraries list
+ galaxyLibraryview.render();
+ });
+
+ library_router.on('route:folder_content', function(id) {
+ // render folder's contents
+ folderContentView.render({id: id});
+ });
+
+ library_router.on('route:download', function(folder_id, format) {
+ if ($('#center').find(':checked').length === 0) {
+ // this happens rarely when there is a server/data error and client gets an actual response instead of an attachment
+ // we don't know what was selected so we can't download again, we redirect to the folder provided
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ } else {
+ // send download stream
+ folderContentView.download(folder_id, format);
+ library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
+ }
+ });
+
+Backbone.history.start();
+
+return this
+}
+});
+
+// return
+return {
+ GalaxyApp: GalaxyLibrary
+};
+
+});
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -57,7 +57,12 @@
tab_shared.add({
title : "Data Libraries",
- content : "library/index",
+ content : "library/index"
+ });
+
+ tab_shared.add({
+ title : "New Libraries",
+ content : "library/list",
divider : true
});
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -1,6 +1,3 @@
-/*
- galaxy modal
-*/
// dependencies
define([], function() {
@@ -25,8 +22,50 @@
// initialize
initialize : function(options) {
+ self = this;
if (options)
this.create(options);
+
+ // Bind the hiding events
+ this.bindEvents(event, self);
+ },
+
+ // bind the click-to-hide function
+ bindEvents: function(event, that) {
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the 'click anywhere' to hide() function...
+ $('html').on('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // unbind the click-to-hide function
+ unbindEvents: function(event, that){
+ // bind the ESC key to hide() function
+ $(document).off('keyup', function(event){
+ if (event.keyCode == 27) { that.hide(); }
+ })
+ // unbind the 'click anywhere' to hide() function...
+ $('html').off('click', function(event){
+ that.hide();
+ })
+ $('.modal-content').off('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // destroy
+ destroy : function(){
+ this.hide();
+ this.unbindEvents();
+ $('.modal').remove();
},
// adds and displays a new frame/window
@@ -63,6 +102,7 @@
// set flag
this.visible = false;
+ this.unbindEvents();
},
// create
@@ -120,6 +160,15 @@
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', true);
},
+ // hide buttons
+ hideButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).hide();
+ },
+ // show buttons
+ showButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).show();
+ },
+
// returns scroll top for body element
scrollTop: function()
{
@@ -139,7 +188,7 @@
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
- '</div>' +
+ '</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
'<div class="buttons" style="float: right;"></div>' +
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b static/scripts/libs/bootstrap.js
--- a/static/scripts/libs/bootstrap.js
+++ b/static/scripts/libs/bootstrap.js
@@ -575,3 +575,158 @@
}
}(window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#dropdowns
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ var $el = $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ $parent.trigger(e = $.Event('show.bs.dropdown'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown')
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index=0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus() {
+ $(backdrop).remove()
+ $(toggle).each(function (e) {
+ var $parent = getParent($(this))
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown'))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('dropdown')
+
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
\ No newline at end of file
This diff is so big that we needed to truncate the remainder.
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: guerler: Grids: Attempt to fix problems with toolshed
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e304da0a9968/
Changeset: e304da0a9968
User: guerler
Date: 2013-12-19 18:29:41
Summary: Grids: Attempt to fix problems with toolshed
Affected #: 2 files
diff -r c91f3185e8ca68a006b755a9fdcbed3174b27fd9 -r e304da0a99681c7fb8538c6861844f17c375097e static/scripts/galaxy.grids.js
--- a/static/scripts/galaxy.grids.js
+++ b/static/scripts/galaxy.grids.js
@@ -322,7 +322,6 @@
var button = $('#grid-' + i + '-popup');
button.off();
var popup = new PopupMenu(button);
-
// load details
var item = options['items'][i];
for (var j in options['operations'])
@@ -655,9 +654,6 @@
// Initialize new grid config
self.init_grid($.parseJSON(response_text));
- // Trigger custom event to indicate grid body has changed.
- $('#grid-table-body').trigger('update');
-
// Hide loading overlay.
$('.loading-elt-overlay').hide();
},
diff -r c91f3185e8ca68a006b755a9fdcbed3174b27fd9 -r e304da0a99681c7fb8538c6861844f17c375097e static/scripts/packed/galaxy.grids.js
--- a/static/scripts/packed/galaxy.grids.js
+++ b/static/scripts/packed/galaxy.grids.js
@@ -1,1 +1,1 @@
-jQuery.ajaxSettings.traditional=true;define(["mvc/ui"],function(){var a=Backbone.Model.extend({defaults:{url_base:"",async:false,async_ops:[],categorical_filters:[],filters:{},sort_key:null,show_item_checkboxes:false,advanced_search:false,cur_page:1,num_pages:1,operation:undefined,item_ids:undefined},can_async_op:function(c){return _.indexOf(this.attributes.async_ops,c)!==-1},add_filter:function(g,h,d){if(d){var e=this.attributes.filters[g],c;if(e===null||e===undefined){c=h}else{if(typeof(e)=="string"){if(e=="All"){c=h}else{var f=[];f[0]=e;f[1]=h;c=f}}else{c=e;c.push(h)}}this.attributes.filters[g]=c}else{this.attributes.filters[g]=h}},remove_filter:function(d,g){var c=this.attributes.filters[d];if(c===null||c===undefined){return false}var f=true;if(typeof(c)==="string"){if(c=="All"){f=false}else{delete this.attributes.filters[d]}}else{var e=_.indexOf(c,g);if(e!==-1){c.splice(e,1)}else{f=false}}return f},get_url_data:function(){var c={async:this.attributes.async,sort:this.attributes.sort_key,page:this.attributes.cur_page,show_item_checkboxes:this.attributes.show_item_checkboxes,advanced_search:this.attributes.advanced_search};if(this.attributes.operation){c.operation=this.attributes.operation}if(this.attributes.item_ids){c.id=this.attributes.item_ids}var d=this;_.each(_.pairs(d.attributes.filters),function(e){c["f-"+e[0]]=e[1]});return c},get_url:function(c){return this.get("url_base")+"?"+$.param(this.get_url_data())+"&"+$.param(c)}});var b=Backbone.View.extend({grid:null,initialize:function(c){this.init_grid(c);this.init_grid_controls();$("input[type=text]").each(function(){$(this).click(function(){$(this).select()}).keyup(function(){$(this).css("font-style","normal")})})},init_grid:function(e){this.grid=new a(e);var d=this.grid.attributes;var c=this.grid.get("url_base");c=c.replace(/^.*\/\/[^\/]+/,"");this.grid.set("url_base",c);$("#grid-table-body").html(this.template_body(d));$("#grid-table-footer").html(this.template_footer(d));if(d.message){$("#grid-message").html(this.template_message(d));setTimeout(function(){$("#grid-message").html("")},5000)}this.init_grid_elements()},init_grid_controls:function(){$(".submit-image").each(function(){$(this).mousedown(function(){$(this).addClass("gray-background")});$(this).mouseup(function(){$(this).removeClass("gray-background")})});var c=this;$(".sort-link").each(function(){$(this).click(function(){c.set_sort_condition($(this).attr("sort_key"));return false})});$(".categorical-filter > a").each(function(){$(this).click(function(){c.set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});$(".text-filter-form").each(function(){$(this).submit(function(){var g=$(this).attr("column_key");var f=$("#input-"+g+"-filter");var h=f.val();f.val("");c.add_filter_condition(g,h);return false})});var d=$("#input-tags-filter");if(d.length){d.autocomplete(this.grid.history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var e=$("#input-name-filter");if(e.length){e.autocomplete(this.grid.history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}$(".advanced-search-toggle").each(function(){$(this).click(function(){$("#standard-search").slideToggle("fast");$("#advanced-search").slideToggle("fast");return false})})},init_grid_elements:function(){$(".grid").each(function(){var r=$(this).find("input.grid-row-select-checkbox");var q=$(this).find("span.grid-selected-count");var s=function(){q.text($(r).filter(":checked").length)};$(r).each(function(){$(this).change(s)});s()});if($(".community_rating_star").length!==0){$(".community_rating_star").rating({})}var p=this.grid.attributes;var o=this;$(".page-link > a").each(function(){$(this).click(function(){o.set_page($(this).attr("page_num"));return false})});$(".use-inbound").each(function(){$(this).click(function(q){o.execute({href:$(this).attr("href"),inbound:true});return false})});$(".use-outbound").each(function(){$(this).click(function(q){o.execute({href:$(this).attr("href")});return false})});for(var h in p.items){var k=$("#grid-"+h+"-popup");k.off();var d=new PopupMenu(k);var n=p.items[h];for(var g in p.operations){var e=p.operations[g];var l=e.label;var c=n.operation_config[l];var f=n.encode_id;if(c.allowed&&e.allow_popup){var m={html:e.label,href:c.url_args,target:c.target,confirmation_text:e.confirm,inbound:e.inbound};m.func=function(s){s.preventDefault();var r=$(s.target).html();var q=this.findItemByHtml(r);o.execute(q)};d.addItem(m)}}}},add_filter_condition:function(e,g){if(g===""){return false}this.grid.add_filter(e,g,true);var f=$("<span>"+g+"<a href='javascript:void(0);'><span class='delete-search-icon' /></span></a>");f.addClass("text-filter-val");var d=this;f.click(function(){d.grid.remove_filter(e,g);$(this).remove();d.go_page_one();d.execute()});var c=$("#"+e+"-filtering-criteria");c.append(f);this.go_page_one();this.execute()},set_sort_condition:function(h){var g=this.grid.get("sort_key");var f=h;if(g.indexOf(h)!==-1){if(g.substring(0,1)!=="-"){f="-"+h}else{}}$(".sort-arrow").remove();var e=(f.substring(0,1)=="-")?"↑":"↓";var c=$("<span>"+e+"</span>").addClass("sort-arrow");var d=$("#"+h+"-header");d.append(c);this.grid.set("sort_key",f);this.go_page_one();this.execute()},set_categorical_filter:function(e,g){var d=this.grid.get("categorical_filters")[e],f=this.grid.get("filters")[e];var c=this;$("."+e+"-filter").each(function(){var m=$.trim($(this).text());var k=d[m];var l=k[e];if(l==g){$(this).empty();$(this).addClass("current-filter");$(this).append(m)}else{if(l==f){$(this).empty();var h=$("<a href='#'>"+m+"</a>");h.click(function(){c.set_categorical_filter(e,l)});$(this).removeClass("current-filter");$(this).append(h)}}});this.grid.add_filter(e,g);this.go_page_one();this.execute()},set_page:function(c){var d=this;$(".page-link").each(function(){var k=$(this).attr("id"),g=parseInt(k.split("-")[2],10),e=d.grid.get("cur_page"),h;if(g===c){h=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(h)}else{if(g===e){h=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var f=$("<a href='#'>"+h+"</a>");f.click(function(){d.set_page(g)});$(this).append(f)}}});if(c==="all"){this.grid.set("cur_page",c)}else{this.grid.set("cur_page",parseInt(c,10))}this.execute()},submit_operation:function(f,g){var e=$('input[name="id"]:checked').length;if(!e>0){return false}var d=$(f).val();var c=[];$("input[name=id]:checked").each(function(){c.push($(this).val())});this.execute({operation:d,id:c,confirmation_text:g});return true},execute:function(n){var f=null;var e=null;var g=null;var c=null;var m=null;if(n){e=n.href;g=n.operation;f=n.id;c=n.confirmation_text;m=n.inbound;if(e!==undefined&&e.indexOf("operation=")!=-1){var l=e.split("?");if(l.length>1){var k=l[1];var d=k.split("&");for(var h=0;h<d.length;h++){if(d[h].indexOf("operation")!=-1){g=d[h].split("=")[1];g=g.replace(/\+/g," ")}else{if(d[h].indexOf("id")!=-1){f=d[h].split("=")[1]}}}}}}if(g&&f){if(c&&c!=""&&c!="None"&&c!="null"){if(!confirm(c)){return false}}g=g.toLowerCase();this.grid.set({operation:g,item_ids:f});if(this.grid.can_async_op(g)){this.update_grid()}else{this.go_to(m,"")}return false}if(e){this.go_to(m,e);return false}if(this.grid.get("async")){this.update_grid()}else{this.go_to(m,"")}return false},go_to:function(f,d){var e=this.grid.get("async");this.grid.set("async",false);advanced_search=$("#advanced-search").is(":visible");this.grid.set("advanced_search",advanced_search);if(!d){d=this.grid.get("url_base")+"?"+$.param(this.grid.get_url_data())}this.grid.set({operation:undefined,item_ids:undefined,async:e});if(f){var c=$(".grid-header").closest(".inbound");if(c.length!==0){c.load(d);return}}window.location=d},update_grid:function(){var d=(this.grid.get("operation")?"POST":"GET");$(".loading-elt-overlay").show();var c=this;$.ajax({type:d,url:c.grid.get("url_base"),data:c.grid.get_url_data(),error:function(e){alert("Grid refresh failed")},success:function(e){c.init_grid($.parseJSON(e));$("#grid-table-body").trigger("update");$(".loading-elt-overlay").hide()},complete:function(){c.grid.set({operation:undefined,item_ids:undefined})}})},check_all_items:function(){var c=document.getElementById("check_all"),d=document.getElementsByTagName("input"),f=0,e;if(c.checked===true){for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=true;f++}}}else{for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=false}}}this.init_grid_elements()},go_page_one:function(){var c=this.grid.get("cur_page");if(c!==null&&c!==undefined&&c!=="all"){this.grid.set("cur_page",1)}},template_body:function(s){var l="";var t=0;var g=s.items.length;if(g==0){l+='<tr><td colspan="100"><em>No Items</em></td></tr>';t=1}for(i in s.items){var q=s.items[i];var c=q.encode_id;var h="grid-"+i+"-popup";l+="<tr ";if(s.current_item_id==q.id){l+='class="current"'}l+=">";if(s.show_item_checkboxes){l+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+c+'" id="'+c+'" class="grid-row-select-checkbox" /></td>'}for(j in s.columns){var f=s.columns[j];if(f.visible){var e="";if(f.nowrap){e='style="white-space:nowrap;"'}var r=q.column_config[f.label];var k=r.link;var m=r.value;var p=r.inbound;if(jQuery.type(m)==="string"){m=m.replace(/\/\//g,"/")}var d="";var o="";if(f.attach_popup){d="grid-"+i+"-popup";o="menubutton";if(k!=""){o+=" split"}o+=" popup"}l+="<td "+e+">";if(k){if(s.operations.length!=0){l+='<div id="'+d+'" class="'+o+'" style="float: left;">'}var n="";if(p){n="use-inbound"}else{n="use-outbound"}l+='<a class="label '+n+'" href="'+k+'" onclick="return false;">'+m+"</a>";if(s.operations.length!=0){l+="</div>"}}else{l+='<div id="'+d+'" class="'+o+'"><label id="'+f.label_id_prefix+c+'" for="'+c+'">'+m+"</label></div>"}l+="</td>"}}l+="</tr>";t++}return l},template_footer:function(q){var m="";if(q.use_paging&&q.num_pages>1){var o=q.num_page_links;var c=q.cur_page_num;var p=q.num_pages;var l=o/2;var k=c-l;var g=0;if(k==0){k=1;g=l-(c-k)}var f=l+g;var e=c+f;if(e<=p){max_offset=0}else{e=p;max_offset=f-(e+1-c)}if(max_offset!=0){k-=max_offset;if(k<1){k=1}}m+='<tr id="page-links-row">';if(q.show_item_checkboxes){m+="<td></td>"}m+='<td colspan="100"><span id="page-link-container">Page:';if(k>1){m+='<span class="page-link" id="page-link-1"><a href="'+this.grid.get_url({page:n})+'" page_num="1" onclick="return false;">1</a></span> ...'}for(var n=k;n<e+1;n++){if(n==q.cur_page_num){m+='<span class="page-link inactive-link" id="page-link-'+n+'">'+n+"</span>"}else{m+='<span class="page-link" id="page-link-'+n+'"><a href="'+this.grid.get_url({page:n})+'" onclick="return false;" page_num="'+n+'">'+n+"</a></span>"}}if(e<p){m+='...<span class="page-link" id="page-link-'+p+'"><a href="'+this.grid.get_url({page:p})+'" onclick="return false;" page_num="'+p+'">'+p+"</a></span>"}m+="</span>";m+='<span class="page-link" id="show-all-link-span"> | <a href="'+this.grid.get_url({page:"all"})+'" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(q.show_item_checkboxes){m+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+q.get_class_plural+": ";for(i in q.operations){var d=q.operations[i];if(d.allow_multiple){m+='<input type="button" value="'+d.label+'" class="action-button" onclick="gridView.submit_operation(this, \''+d.confirm+"')\"> "}}m+="</td></tr>"}var h=false;for(i in q.operations){if(q.operations[i].global_operation){h=true;break}}if(h){m+='<tr><td colspan="100">';for(i in q.operations){var d=q.operations[i];if(d.global_operation){m+='<a class="action-button" href="'+d.global_operation+'">'+d.label+"</a>"}}m+="</td></tr>"}if(q.legend){m+='<tr><td colspan="100">'+q.legend+"</td></tr>"}return m},template_message:function(c){return'<p><div class="'+c.status+'message transient-message">'+c.message+'</div><div style="clear: both"></div></p>'}});return{Grid:a,GridView:b}});
\ No newline at end of file
+jQuery.ajaxSettings.traditional=true;define(["mvc/ui"],function(){var a=Backbone.Model.extend({defaults:{url_base:"",async:false,async_ops:[],categorical_filters:[],filters:{},sort_key:null,show_item_checkboxes:false,advanced_search:false,cur_page:1,num_pages:1,operation:undefined,item_ids:undefined},can_async_op:function(c){return _.indexOf(this.attributes.async_ops,c)!==-1},add_filter:function(g,h,d){if(d){var e=this.attributes.filters[g],c;if(e===null||e===undefined){c=h}else{if(typeof(e)=="string"){if(e=="All"){c=h}else{var f=[];f[0]=e;f[1]=h;c=f}}else{c=e;c.push(h)}}this.attributes.filters[g]=c}else{this.attributes.filters[g]=h}},remove_filter:function(d,g){var c=this.attributes.filters[d];if(c===null||c===undefined){return false}var f=true;if(typeof(c)==="string"){if(c=="All"){f=false}else{delete this.attributes.filters[d]}}else{var e=_.indexOf(c,g);if(e!==-1){c.splice(e,1)}else{f=false}}return f},get_url_data:function(){var c={async:this.attributes.async,sort:this.attributes.sort_key,page:this.attributes.cur_page,show_item_checkboxes:this.attributes.show_item_checkboxes,advanced_search:this.attributes.advanced_search};if(this.attributes.operation){c.operation=this.attributes.operation}if(this.attributes.item_ids){c.id=this.attributes.item_ids}var d=this;_.each(_.pairs(d.attributes.filters),function(e){c["f-"+e[0]]=e[1]});return c},get_url:function(c){return this.get("url_base")+"?"+$.param(this.get_url_data())+"&"+$.param(c)}});var b=Backbone.View.extend({grid:null,initialize:function(c){this.init_grid(c);this.init_grid_controls();$("input[type=text]").each(function(){$(this).click(function(){$(this).select()}).keyup(function(){$(this).css("font-style","normal")})})},init_grid:function(e){this.grid=new a(e);var d=this.grid.attributes;var c=this.grid.get("url_base");c=c.replace(/^.*\/\/[^\/]+/,"");this.grid.set("url_base",c);$("#grid-table-body").html(this.template_body(d));$("#grid-table-footer").html(this.template_footer(d));if(d.message){$("#grid-message").html(this.template_message(d));setTimeout(function(){$("#grid-message").html("")},5000)}this.init_grid_elements()},init_grid_controls:function(){$(".submit-image").each(function(){$(this).mousedown(function(){$(this).addClass("gray-background")});$(this).mouseup(function(){$(this).removeClass("gray-background")})});var c=this;$(".sort-link").each(function(){$(this).click(function(){c.set_sort_condition($(this).attr("sort_key"));return false})});$(".categorical-filter > a").each(function(){$(this).click(function(){c.set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});$(".text-filter-form").each(function(){$(this).submit(function(){var g=$(this).attr("column_key");var f=$("#input-"+g+"-filter");var h=f.val();f.val("");c.add_filter_condition(g,h);return false})});var d=$("#input-tags-filter");if(d.length){d.autocomplete(this.grid.history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var e=$("#input-name-filter");if(e.length){e.autocomplete(this.grid.history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}$(".advanced-search-toggle").each(function(){$(this).click(function(){$("#standard-search").slideToggle("fast");$("#advanced-search").slideToggle("fast");return false})})},init_grid_elements:function(){$(".grid").each(function(){var r=$(this).find("input.grid-row-select-checkbox");var q=$(this).find("span.grid-selected-count");var s=function(){q.text($(r).filter(":checked").length)};$(r).each(function(){$(this).change(s)});s()});if($(".community_rating_star").length!==0){$(".community_rating_star").rating({})}var p=this.grid.attributes;var o=this;$(".page-link > a").each(function(){$(this).click(function(){o.set_page($(this).attr("page_num"));return false})});$(".use-inbound").each(function(){$(this).click(function(q){o.execute({href:$(this).attr("href"),inbound:true});return false})});$(".use-outbound").each(function(){$(this).click(function(q){o.execute({href:$(this).attr("href")});return false})});for(var h in p.items){var k=$("#grid-"+h+"-popup");k.off();var d=new PopupMenu(k);var n=p.items[h];for(var g in p.operations){var e=p.operations[g];var l=e.label;var c=n.operation_config[l];var f=n.encode_id;if(c.allowed&&e.allow_popup){var m={html:e.label,href:c.url_args,target:c.target,confirmation_text:e.confirm,inbound:e.inbound};m.func=function(s){s.preventDefault();var r=$(s.target).html();var q=this.findItemByHtml(r);o.execute(q)};d.addItem(m)}}}},add_filter_condition:function(e,g){if(g===""){return false}this.grid.add_filter(e,g,true);var f=$("<span>"+g+"<a href='javascript:void(0);'><span class='delete-search-icon' /></span></a>");f.addClass("text-filter-val");var d=this;f.click(function(){d.grid.remove_filter(e,g);$(this).remove();d.go_page_one();d.execute()});var c=$("#"+e+"-filtering-criteria");c.append(f);this.go_page_one();this.execute()},set_sort_condition:function(h){var g=this.grid.get("sort_key");var f=h;if(g.indexOf(h)!==-1){if(g.substring(0,1)!=="-"){f="-"+h}else{}}$(".sort-arrow").remove();var e=(f.substring(0,1)=="-")?"↑":"↓";var c=$("<span>"+e+"</span>").addClass("sort-arrow");var d=$("#"+h+"-header");d.append(c);this.grid.set("sort_key",f);this.go_page_one();this.execute()},set_categorical_filter:function(e,g){var d=this.grid.get("categorical_filters")[e],f=this.grid.get("filters")[e];var c=this;$("."+e+"-filter").each(function(){var m=$.trim($(this).text());var k=d[m];var l=k[e];if(l==g){$(this).empty();$(this).addClass("current-filter");$(this).append(m)}else{if(l==f){$(this).empty();var h=$("<a href='#'>"+m+"</a>");h.click(function(){c.set_categorical_filter(e,l)});$(this).removeClass("current-filter");$(this).append(h)}}});this.grid.add_filter(e,g);this.go_page_one();this.execute()},set_page:function(c){var d=this;$(".page-link").each(function(){var k=$(this).attr("id"),g=parseInt(k.split("-")[2],10),e=d.grid.get("cur_page"),h;if(g===c){h=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(h)}else{if(g===e){h=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var f=$("<a href='#'>"+h+"</a>");f.click(function(){d.set_page(g)});$(this).append(f)}}});if(c==="all"){this.grid.set("cur_page",c)}else{this.grid.set("cur_page",parseInt(c,10))}this.execute()},submit_operation:function(f,g){var e=$('input[name="id"]:checked').length;if(!e>0){return false}var d=$(f).val();var c=[];$("input[name=id]:checked").each(function(){c.push($(this).val())});this.execute({operation:d,id:c,confirmation_text:g});return true},execute:function(n){var f=null;var e=null;var g=null;var c=null;var m=null;if(n){e=n.href;g=n.operation;f=n.id;c=n.confirmation_text;m=n.inbound;if(e!==undefined&&e.indexOf("operation=")!=-1){var l=e.split("?");if(l.length>1){var k=l[1];var d=k.split("&");for(var h=0;h<d.length;h++){if(d[h].indexOf("operation")!=-1){g=d[h].split("=")[1];g=g.replace(/\+/g," ")}else{if(d[h].indexOf("id")!=-1){f=d[h].split("=")[1]}}}}}}if(g&&f){if(c&&c!=""&&c!="None"&&c!="null"){if(!confirm(c)){return false}}g=g.toLowerCase();this.grid.set({operation:g,item_ids:f});if(this.grid.can_async_op(g)){this.update_grid()}else{this.go_to(m,"")}return false}if(e){this.go_to(m,e);return false}if(this.grid.get("async")){this.update_grid()}else{this.go_to(m,"")}return false},go_to:function(f,d){var e=this.grid.get("async");this.grid.set("async",false);advanced_search=$("#advanced-search").is(":visible");this.grid.set("advanced_search",advanced_search);if(!d){d=this.grid.get("url_base")+"?"+$.param(this.grid.get_url_data())}this.grid.set({operation:undefined,item_ids:undefined,async:e});if(f){var c=$(".grid-header").closest(".inbound");if(c.length!==0){c.load(d);return}}window.location=d},update_grid:function(){var d=(this.grid.get("operation")?"POST":"GET");$(".loading-elt-overlay").show();var c=this;$.ajax({type:d,url:c.grid.get("url_base"),data:c.grid.get_url_data(),error:function(e){alert("Grid refresh failed")},success:function(e){c.init_grid($.parseJSON(e));$(".loading-elt-overlay").hide()},complete:function(){c.grid.set({operation:undefined,item_ids:undefined})}})},check_all_items:function(){var c=document.getElementById("check_all"),d=document.getElementsByTagName("input"),f=0,e;if(c.checked===true){for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=true;f++}}}else{for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=false}}}this.init_grid_elements()},go_page_one:function(){var c=this.grid.get("cur_page");if(c!==null&&c!==undefined&&c!=="all"){this.grid.set("cur_page",1)}},template_body:function(s){var l="";var t=0;var g=s.items.length;if(g==0){l+='<tr><td colspan="100"><em>No Items</em></td></tr>';t=1}for(i in s.items){var q=s.items[i];var c=q.encode_id;var h="grid-"+i+"-popup";l+="<tr ";if(s.current_item_id==q.id){l+='class="current"'}l+=">";if(s.show_item_checkboxes){l+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+c+'" id="'+c+'" class="grid-row-select-checkbox" /></td>'}for(j in s.columns){var f=s.columns[j];if(f.visible){var e="";if(f.nowrap){e='style="white-space:nowrap;"'}var r=q.column_config[f.label];var k=r.link;var m=r.value;var p=r.inbound;if(jQuery.type(m)==="string"){m=m.replace(/\/\//g,"/")}var d="";var o="";if(f.attach_popup){d="grid-"+i+"-popup";o="menubutton";if(k!=""){o+=" split"}o+=" popup"}l+="<td "+e+">";if(k){if(s.operations.length!=0){l+='<div id="'+d+'" class="'+o+'" style="float: left;">'}var n="";if(p){n="use-inbound"}else{n="use-outbound"}l+='<a class="label '+n+'" href="'+k+'" onclick="return false;">'+m+"</a>";if(s.operations.length!=0){l+="</div>"}}else{l+='<div id="'+d+'" class="'+o+'"><label id="'+f.label_id_prefix+c+'" for="'+c+'">'+m+"</label></div>"}l+="</td>"}}l+="</tr>";t++}return l},template_footer:function(q){var m="";if(q.use_paging&&q.num_pages>1){var o=q.num_page_links;var c=q.cur_page_num;var p=q.num_pages;var l=o/2;var k=c-l;var g=0;if(k==0){k=1;g=l-(c-k)}var f=l+g;var e=c+f;if(e<=p){max_offset=0}else{e=p;max_offset=f-(e+1-c)}if(max_offset!=0){k-=max_offset;if(k<1){k=1}}m+='<tr id="page-links-row">';if(q.show_item_checkboxes){m+="<td></td>"}m+='<td colspan="100"><span id="page-link-container">Page:';if(k>1){m+='<span class="page-link" id="page-link-1"><a href="'+this.grid.get_url({page:n})+'" page_num="1" onclick="return false;">1</a></span> ...'}for(var n=k;n<e+1;n++){if(n==q.cur_page_num){m+='<span class="page-link inactive-link" id="page-link-'+n+'">'+n+"</span>"}else{m+='<span class="page-link" id="page-link-'+n+'"><a href="'+this.grid.get_url({page:n})+'" onclick="return false;" page_num="'+n+'">'+n+"</a></span>"}}if(e<p){m+='...<span class="page-link" id="page-link-'+p+'"><a href="'+this.grid.get_url({page:p})+'" onclick="return false;" page_num="'+p+'">'+p+"</a></span>"}m+="</span>";m+='<span class="page-link" id="show-all-link-span"> | <a href="'+this.grid.get_url({page:"all"})+'" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(q.show_item_checkboxes){m+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+q.get_class_plural+": ";for(i in q.operations){var d=q.operations[i];if(d.allow_multiple){m+='<input type="button" value="'+d.label+'" class="action-button" onclick="gridView.submit_operation(this, \''+d.confirm+"')\"> "}}m+="</td></tr>"}var h=false;for(i in q.operations){if(q.operations[i].global_operation){h=true;break}}if(h){m+='<tr><td colspan="100">';for(i in q.operations){var d=q.operations[i];if(d.global_operation){m+='<a class="action-button" href="'+d.global_operation+'">'+d.label+"</a>"}}m+="</td></tr>"}if(q.legend){m+='<tr><td colspan="100">'+q.legend+"</td></tr>"}return m},template_message:function(c){return'<p><div class="'+c.status+'message transient-message">'+c.message+'</div><div style="clear: both"></div></p>'}});return{Grid:a,GridView:b}});
\ 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
commit/galaxy-central: carlfeberhard: History panel: bring error state HDA button order into line with other states
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c91f3185e8ca/
Changeset: c91f3185e8ca
User: carlfeberhard
Date: 2013-12-19 17:47:11
Summary: History panel: bring error state HDA button order into line with other states
Affected #: 2 files
diff -r 4fc04317cef36f002e071d391523450a7a956232 -r c91f3185e8ca68a006b755a9fdcbed3174b27fd9 static/scripts/mvc/dataset/hda-base.js
--- a/static/scripts/mvc/dataset/hda-base.js
+++ b/static/scripts/mvc/dataset/hda-base.js
@@ -354,7 +354,7 @@
html = '<div>' + this.model.get( 'misc_blurb' ) + '</div>' + html;
}
return this._render_stateBodyHelper( html,
- this.defaultPrimaryActionButtonRenderers.concat([ this._render_downloadButton ])
+ [ this._render_downloadButton ].concat( this.defaultPrimaryActionButtonRenderers )
);
},
diff -r 4fc04317cef36f002e071d391523450a7a956232 -r c91f3185e8ca68a006b755a9fdcbed3174b27fd9 static/scripts/packed/mvc/dataset/hda-base.js
--- a/static/scripts/packed/mvc/dataset/hda-base.js
+++ b/static/scripts/packed/mvc/dataset/hda-base.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-model"],function(b){var a=Backbone.View.extend(LoggableMixin).extend({tagName:"div",className:"dataset hda history-panel-hda",id:function(){return"hda-"+this.model.get("id")},fxSpeed:"fast",initialize:function(c){if(c.logger){this.logger=this.model.logger=c.logger}this.log(this+".initialize:",c);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton];this.linkTarget=c.linkTarget||"_blank";this.selectable=c.selectable||false;this.selected=c.selected||false;this.expanded=c.expanded||false;this.draggable=c.draggable||false;this._setUpListeners()},_setUpListeners:function(){this.model.on("change",function(d,c){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this)},render:function(e){e=(e===undefined)?(true):(e);var c=this;this.$el.find("[title]").tooltip("destroy");this.urls=this.model.urls();var d=$(a.templates.skeleton(this.model.toJSON()));d.find(".dataset-primary-actions").append(this._render_titleButtons());d.children(".dataset-body").replaceWith(this._render_body());this._setUpBehaviors(d);if(e){$(c).queue(function(f){this.$el.fadeOut(c.fxSpeed,f)})}$(c).queue(function(f){this.$el.empty().attr("class",c.className).addClass("state-"+c.model.get("state")).append(d.children());if(this.selectable){this.showSelector(0)}f()});if(e){$(c).queue(function(f){this.$el.fadeIn(c.fxSpeed,f)})}$(c).queue(function(f){this.trigger("rendered",c);if(this.model.inReadyState()){this.trigger("rendered:ready",c)}if(this.draggable){this.draggableOn()}f()});return this},_setUpBehaviors:function(c){c=c||this.$el;make_popup_menus(c);c.find("[title]").tooltip({placement:"bottom"})},_render_titleButtons:function(){return[this._render_displayButton()]},_render_displayButton:function(){if((this.model.get("state")===b.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.NEW)||(!this.model.get("accessible"))){return null}var d={target:this.linkTarget,classes:"dataset-display"};if(this.model.get("purged")){d.disabled=true;d.title=_l("Cannot display datasets removed from disk")}else{if(this.model.get("state")===b.HistoryDatasetAssociation.STATES.UPLOAD){d.disabled=true;d.title=_l("This dataset must finish uploading before it can be viewed")}else{d.title=_l("View data");d.href=this.urls.display;var c=this;d.onclick=function(){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Data Viewer: "+c.model.get("name"),type:"url",content:c.urls.display})}}}}d.faIcon="fa-eye";return faIconButton(d)},_render_downloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}var d=this.urls,e=this.model.get("meta_files");if(_.isEmpty(e)){return $(['<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>',"</a>"].join(""))}var f="dataset-"+this.model.get("id")+"-popup",c=['<div popupmenu="'+f+'">','<a href="'+d.download+'">',_l("Download Dataset"),"</a>","<a>"+_l("Additional Files")+"</a>",_.map(e,function(g){return['<a class="action-button" href="',d.meta_download+g.file_type,'">',_l("Download")," ",g.file_type,"</a>"].join("")}).join("\n"),"</div>",'<div class="icon-btn-group">','<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>','</a><a class="icon-btn popup" id="'+f+'">','<span class="fa fa-caret-down"></span>',"</a>","</div>"].join("\n");return $(c)},_render_showParamsButton:function(){return faIconButton({title:_l("View details"),href:this.urls.show_params,target:this.linkTarget,faIcon:"fa-info-circle"})},_render_body:function(){var d=$('<div>Error: unknown dataset state "'+this.model.get("state")+'".</div>'),c=this["_render_body_"+this.model.get("state")];if(_.isFunction(c)){d=c.call(this)}this._setUpBehaviors(d);if(this.expanded){d.show()}return d},_render_stateBodyHelper:function(c,f){f=f||[];var d=this,e=$(a.templates.body(_.extend(this.model.toJSON(),{body:c})));e.find(".dataset-actions .left").append(_.map(f,function(g){return g.call(d)}));return e},_render_body_new:function(){return this._render_stateBodyHelper("<div>"+_l("This is a new dataset and not all of its data are available yet")+"</div>")},_render_body_noPermission:function(){return this._render_stateBodyHelper("<div>"+_l("You do not have permission to view this dataset")+"</div>")},_render_body_discarded:function(){return this._render_stateBodyHelper("<div>"+_l("The job creating this dataset was cancelled before completion")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_queued:function(){return this._render_stateBodyHelper("<div>"+_l("This job is waiting to run")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_upload:function(){return this._render_stateBodyHelper("<div>"+_l("This dataset is currently uploading")+"</div>")},_render_body_setting_metadata:function(){return this._render_stateBodyHelper("<div>"+_l("Metadata is being auto-detected")+"</div>")},_render_body_running:function(){return this._render_stateBodyHelper("<div>"+_l("This job is currently running")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_paused:function(){return this._render_stateBodyHelper("<div>"+_l('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume')+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_error:function(){var c=['<span class="help-text">',_l("An error occurred with this dataset"),":</span>",'<div class="job-error-text">',$.trim(this.model.get("misc_info")),"</div>"].join("");if(!this.model.get("purged")){c="<div>"+this.model.get("misc_blurb")+"</div>"+c}return this._render_stateBodyHelper(c,this.defaultPrimaryActionButtonRenderers.concat([this._render_downloadButton]))},_render_body_empty:function(){return this._render_stateBodyHelper("<div>"+_l("No data")+": <i>"+this.model.get("misc_blurb")+"</i></div>",this.defaultPrimaryActionButtonRenderers)},_render_body_failed_metadata:function(){var c=$('<div class="warningmessagesmall"></div>').append($("<strong/>").text(_l("An error occurred setting the metadata for this dataset"))),d=this._render_body_ok();d.prepend(c);return d},_render_body_ok:function(){var c=this,e=$(a.templates.body(this.model.toJSON())),d=[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers);e.find(".dataset-actions .left").append(_.map(d,function(f){return f.call(c)}));if(this.model.isDeletedOrPurged()){return e}return e},events:{"click .dataset-title-bar":"toggleBodyVisibility","keydown .dataset-title-bar":"toggleBodyVisibility","click .dataset-selector":"toggleSelect",},toggleBodyVisibility:function(f,d){var c=32,e=13;if(f&&(f.type==="keydown")&&!(f.keyCode===c||f.keyCode===e)){return true}var g=this.$el.find(".dataset-body");d=(d===undefined)?(!g.is(":visible")):(d);if(d){this.expandBody()}else{this.collapseBody()}return false},expandBody:function(){var c=this;function d(){c.$el.children(".dataset-body").replaceWith(c._render_body());c.$el.children(".dataset-body").slideDown(c.fxSpeed,function(){c.expanded=true;c.trigger("body-expanded",c.model.get("id"))})}if(this.model.inReadyState()&&!this.model.hasDetails()){this.model.fetch({silent:true}).always(function(e){d()})}else{d()}},collapseBody:function(){var c=this;this.$el.children(".dataset-body").slideUp(c.fxSpeed,function(){c.expanded=false;c.trigger("body-collapsed",c.model.get("id"))})},showSelector:function(e){if(this.$el.find(".dataset-selector").css("display")!=="none"){return}e=(e!==undefined)?(e):(this.fxSpeed);if(this.selected){this.select(null,true)}var d=this,c=32;if(e){this.$el.queue("fx",function(f){$(this).find(".dataset-primary-actions").fadeOut(e,f)});this.$el.queue("fx",function(f){$(this).find(".dataset-selector").show().animate({width:c},e,f);$(this).find(".dataset-title-bar").animate({"margin-left":c},e,f);d.selectable=true;d.trigger("selectable",true,d)})}else{this.$el.find(".dataset-primary-actions").hide();this.$el.find(".dataset-selector").show().css({width:c});this.$el.find(".dataset-title-bar").show().css({"margin-left":c});d.selectable=true;d.trigger("selectable",true,d)}},hideSelector:function(c){c=(c!==undefined)?(c):(this.fxSpeed);this.selectable=false;this.trigger("selectable",false,this);if(c){this.$el.queue("fx",function(d){$(this).find(".dataset-title-bar").show().css({"margin-left":"0"});$(this).find(".dataset-selector").animate({width:"0px"},c,function(){$(this).hide();d()})});this.$el.queue("fx",function(d){$(this).find(".dataset-primary-actions").fadeIn(c,d)})}else{$(this).find(".dataset-selector").css({width:"0px"}).hide();$(this).find(".dataset-primary-actions").show()}},toggleSelector:function(c){if(!this.$el.find(".dataset-selector").is(":visible")){this.showSelector(c)}else{this.hideSelector(c)}},select:function(c){this.$el.find(".dataset-selector span").removeClass("fa-square-o").addClass("fa-check-square-o");if(!this.selected){this.trigger("selected",this);this.selected=true}return false},deselect:function(c){this.$el.find(".dataset-selector span").removeClass("fa-check-square-o").addClass("fa-square-o");if(this.selected){this.trigger("de-selected",this);this.selected=false}return false},toggleSelect:function(c){if(this.selected){this.deselect(c)}else{this.select(c)}},draggableOn:function(){this.draggable=true;this.dragStartHandler=_.bind(this._dragStartHandler,this);this.dragEndHandler=_.bind(this._dragEndHandler,this);var c=this.$el.find(".dataset-title-bar").attr("draggable",true).get(0);c.addEventListener("dragstart",this.dragStartHandler,false);c.addEventListener("dragend",this.dragEndHandler,false)},draggableOff:function(){this.draggable=false;var c=this.$el.find(".dataset-title-bar").attr("draggable",false).get(0);c.removeEventListener("dragstart",this.dragStartHandler,false);c.removeEventListener("dragend",this.dragEndHandler,false)},toggleDraggable:function(){if(this.draggable){this.draggableOff()}else{this.draggableOn()}},_dragStartHandler:function(c){this.trigger("dragstart",this);c.dataTransfer.effectAllowed="move";c.dataTransfer.setData("text",JSON.stringify(this.model.toJSON()));return false},_dragEndHandler:function(c){this.trigger("dragend",this);return false},remove:function(d){var c=this;this.$el.fadeOut(c.fxSpeed,function(){c.$el.remove();c.off();if(d){d()}})},toString:function(){var c=(this.model)?(this.model+""):("(no model)");return"HDABaseView("+c+")"}});a.templates={skeleton:Handlebars.templates["template-hda-skeleton"],body:Handlebars.templates["template-hda-body"]};return{HDABaseView:a}});
\ No newline at end of file
+define(["mvc/dataset/hda-model"],function(b){var a=Backbone.View.extend(LoggableMixin).extend({tagName:"div",className:"dataset hda history-panel-hda",id:function(){return"hda-"+this.model.get("id")},fxSpeed:"fast",initialize:function(c){if(c.logger){this.logger=this.model.logger=c.logger}this.log(this+".initialize:",c);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton];this.linkTarget=c.linkTarget||"_blank";this.selectable=c.selectable||false;this.selected=c.selected||false;this.expanded=c.expanded||false;this.draggable=c.draggable||false;this._setUpListeners()},_setUpListeners:function(){this.model.on("change",function(d,c){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this)},render:function(e){e=(e===undefined)?(true):(e);var c=this;this.$el.find("[title]").tooltip("destroy");this.urls=this.model.urls();var d=$(a.templates.skeleton(this.model.toJSON()));d.find(".dataset-primary-actions").append(this._render_titleButtons());d.children(".dataset-body").replaceWith(this._render_body());this._setUpBehaviors(d);if(e){$(c).queue(function(f){this.$el.fadeOut(c.fxSpeed,f)})}$(c).queue(function(f){this.$el.empty().attr("class",c.className).addClass("state-"+c.model.get("state")).append(d.children());if(this.selectable){this.showSelector(0)}f()});if(e){$(c).queue(function(f){this.$el.fadeIn(c.fxSpeed,f)})}$(c).queue(function(f){this.trigger("rendered",c);if(this.model.inReadyState()){this.trigger("rendered:ready",c)}if(this.draggable){this.draggableOn()}f()});return this},_setUpBehaviors:function(c){c=c||this.$el;make_popup_menus(c);c.find("[title]").tooltip({placement:"bottom"})},_render_titleButtons:function(){return[this._render_displayButton()]},_render_displayButton:function(){if((this.model.get("state")===b.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.NEW)||(!this.model.get("accessible"))){return null}var d={target:this.linkTarget,classes:"dataset-display"};if(this.model.get("purged")){d.disabled=true;d.title=_l("Cannot display datasets removed from disk")}else{if(this.model.get("state")===b.HistoryDatasetAssociation.STATES.UPLOAD){d.disabled=true;d.title=_l("This dataset must finish uploading before it can be viewed")}else{d.title=_l("View data");d.href=this.urls.display;var c=this;d.onclick=function(){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Data Viewer: "+c.model.get("name"),type:"url",content:c.urls.display})}}}}d.faIcon="fa-eye";return faIconButton(d)},_render_downloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}var d=this.urls,e=this.model.get("meta_files");if(_.isEmpty(e)){return $(['<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>',"</a>"].join(""))}var f="dataset-"+this.model.get("id")+"-popup",c=['<div popupmenu="'+f+'">','<a href="'+d.download+'">',_l("Download Dataset"),"</a>","<a>"+_l("Additional Files")+"</a>",_.map(e,function(g){return['<a class="action-button" href="',d.meta_download+g.file_type,'">',_l("Download")," ",g.file_type,"</a>"].join("")}).join("\n"),"</div>",'<div class="icon-btn-group">','<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>','</a><a class="icon-btn popup" id="'+f+'">','<span class="fa fa-caret-down"></span>',"</a>","</div>"].join("\n");return $(c)},_render_showParamsButton:function(){return faIconButton({title:_l("View details"),href:this.urls.show_params,target:this.linkTarget,faIcon:"fa-info-circle"})},_render_body:function(){var d=$('<div>Error: unknown dataset state "'+this.model.get("state")+'".</div>'),c=this["_render_body_"+this.model.get("state")];if(_.isFunction(c)){d=c.call(this)}this._setUpBehaviors(d);if(this.expanded){d.show()}return d},_render_stateBodyHelper:function(c,f){f=f||[];var d=this,e=$(a.templates.body(_.extend(this.model.toJSON(),{body:c})));e.find(".dataset-actions .left").append(_.map(f,function(g){return g.call(d)}));return e},_render_body_new:function(){return this._render_stateBodyHelper("<div>"+_l("This is a new dataset and not all of its data are available yet")+"</div>")},_render_body_noPermission:function(){return this._render_stateBodyHelper("<div>"+_l("You do not have permission to view this dataset")+"</div>")},_render_body_discarded:function(){return this._render_stateBodyHelper("<div>"+_l("The job creating this dataset was cancelled before completion")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_queued:function(){return this._render_stateBodyHelper("<div>"+_l("This job is waiting to run")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_upload:function(){return this._render_stateBodyHelper("<div>"+_l("This dataset is currently uploading")+"</div>")},_render_body_setting_metadata:function(){return this._render_stateBodyHelper("<div>"+_l("Metadata is being auto-detected")+"</div>")},_render_body_running:function(){return this._render_stateBodyHelper("<div>"+_l("This job is currently running")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_paused:function(){return this._render_stateBodyHelper("<div>"+_l('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume')+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_error:function(){var c=['<span class="help-text">',_l("An error occurred with this dataset"),":</span>",'<div class="job-error-text">',$.trim(this.model.get("misc_info")),"</div>"].join("");if(!this.model.get("purged")){c="<div>"+this.model.get("misc_blurb")+"</div>"+c}return this._render_stateBodyHelper(c,[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers))},_render_body_empty:function(){return this._render_stateBodyHelper("<div>"+_l("No data")+": <i>"+this.model.get("misc_blurb")+"</i></div>",this.defaultPrimaryActionButtonRenderers)},_render_body_failed_metadata:function(){var c=$('<div class="warningmessagesmall"></div>').append($("<strong/>").text(_l("An error occurred setting the metadata for this dataset"))),d=this._render_body_ok();d.prepend(c);return d},_render_body_ok:function(){var c=this,e=$(a.templates.body(this.model.toJSON())),d=[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers);e.find(".dataset-actions .left").append(_.map(d,function(f){return f.call(c)}));if(this.model.isDeletedOrPurged()){return e}return e},events:{"click .dataset-title-bar":"toggleBodyVisibility","keydown .dataset-title-bar":"toggleBodyVisibility","click .dataset-selector":"toggleSelect",},toggleBodyVisibility:function(f,d){var c=32,e=13;if(f&&(f.type==="keydown")&&!(f.keyCode===c||f.keyCode===e)){return true}var g=this.$el.find(".dataset-body");d=(d===undefined)?(!g.is(":visible")):(d);if(d){this.expandBody()}else{this.collapseBody()}return false},expandBody:function(){var c=this;function d(){c.$el.children(".dataset-body").replaceWith(c._render_body());c.$el.children(".dataset-body").slideDown(c.fxSpeed,function(){c.expanded=true;c.trigger("body-expanded",c.model.get("id"))})}if(this.model.inReadyState()&&!this.model.hasDetails()){this.model.fetch({silent:true}).always(function(e){d()})}else{d()}},collapseBody:function(){var c=this;this.$el.children(".dataset-body").slideUp(c.fxSpeed,function(){c.expanded=false;c.trigger("body-collapsed",c.model.get("id"))})},showSelector:function(e){if(this.$el.find(".dataset-selector").css("display")!=="none"){return}e=(e!==undefined)?(e):(this.fxSpeed);if(this.selected){this.select(null,true)}var d=this,c=32;if(e){this.$el.queue("fx",function(f){$(this).find(".dataset-primary-actions").fadeOut(e,f)});this.$el.queue("fx",function(f){$(this).find(".dataset-selector").show().animate({width:c},e,f);$(this).find(".dataset-title-bar").animate({"margin-left":c},e,f);d.selectable=true;d.trigger("selectable",true,d)})}else{this.$el.find(".dataset-primary-actions").hide();this.$el.find(".dataset-selector").show().css({width:c});this.$el.find(".dataset-title-bar").show().css({"margin-left":c});d.selectable=true;d.trigger("selectable",true,d)}},hideSelector:function(c){c=(c!==undefined)?(c):(this.fxSpeed);this.selectable=false;this.trigger("selectable",false,this);if(c){this.$el.queue("fx",function(d){$(this).find(".dataset-title-bar").show().css({"margin-left":"0"});$(this).find(".dataset-selector").animate({width:"0px"},c,function(){$(this).hide();d()})});this.$el.queue("fx",function(d){$(this).find(".dataset-primary-actions").fadeIn(c,d)})}else{$(this).find(".dataset-selector").css({width:"0px"}).hide();$(this).find(".dataset-primary-actions").show()}},toggleSelector:function(c){if(!this.$el.find(".dataset-selector").is(":visible")){this.showSelector(c)}else{this.hideSelector(c)}},select:function(c){this.$el.find(".dataset-selector span").removeClass("fa-square-o").addClass("fa-check-square-o");if(!this.selected){this.trigger("selected",this);this.selected=true}return false},deselect:function(c){this.$el.find(".dataset-selector span").removeClass("fa-check-square-o").addClass("fa-square-o");if(this.selected){this.trigger("de-selected",this);this.selected=false}return false},toggleSelect:function(c){if(this.selected){this.deselect(c)}else{this.select(c)}},draggableOn:function(){this.draggable=true;this.dragStartHandler=_.bind(this._dragStartHandler,this);this.dragEndHandler=_.bind(this._dragEndHandler,this);var c=this.$el.find(".dataset-title-bar").attr("draggable",true).get(0);c.addEventListener("dragstart",this.dragStartHandler,false);c.addEventListener("dragend",this.dragEndHandler,false)},draggableOff:function(){this.draggable=false;var c=this.$el.find(".dataset-title-bar").attr("draggable",false).get(0);c.removeEventListener("dragstart",this.dragStartHandler,false);c.removeEventListener("dragend",this.dragEndHandler,false)},toggleDraggable:function(){if(this.draggable){this.draggableOff()}else{this.draggableOn()}},_dragStartHandler:function(c){this.trigger("dragstart",this);c.dataTransfer.effectAllowed="move";c.dataTransfer.setData("text",JSON.stringify(this.model.toJSON()));return false},_dragEndHandler:function(c){this.trigger("dragend",this);return false},remove:function(d){var c=this;this.$el.fadeOut(c.fxSpeed,function(){c.$el.remove();c.off();if(d){d()}})},toString:function(){var c=(this.model)?(this.model+""):("(no model)");return"HDABaseView("+c+")"}});a.templates={skeleton:Handlebars.templates["template-hda-skeleton"],body:Handlebars.templates["template-hda-body"]};return{HDABaseView: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
commit/galaxy-central: carlfeberhard: History panel: do not update title when empty string or unchanged
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4fc04317cef3/
Changeset: 4fc04317cef3
User: carlfeberhard
Date: 2013-12-19 16:39:55
Summary: History panel: do not update title when empty string or unchanged
Affected #: 2 files
diff -r 67fe4b091f0ad66d68d06a36b8fb645aa445d032 -r 4fc04317cef36f002e071d391523450a7a956232 static/scripts/mvc/history/history-panel.js
--- a/static/scripts/mvc/history/history-panel.js
+++ b/static/scripts/mvc/history/history-panel.js
@@ -698,11 +698,16 @@
.attr( 'title', _l( 'Click to rename history' ) ).tooltip({ placement: 'bottom' })
.make_text_editable({
on_finish: function( newName ){
- $where.find( '.history-name' ).text( newName );
- panel.model.save({ name: newName })
- .fail( function(){
- $where.find( '.history-name' ).text( panel.model.previous( 'name' ) );
- });
+ var previousName = panel.model.get( 'name' );
+ if( newName && newName !== previousName ){
+ panel.$el.find( '.history-name' ).text( newName );
+ panel.model.save({ name: newName })
+ .fail( function(){
+ panel.$el.find( '.history-name' ).text( panel.model.previous( 'name' ) );
+ });
+ } else {
+ panel.$el.find( '.history-name' ).text( previousName );
+ }
}
});
this._setUpDatasetActionsPopup( $where );
diff -r 67fe4b091f0ad66d68d06a36b8fb645aa445d032 -r 4fc04317cef36f002e071d391523450a7a956232 static/scripts/packed/mvc/history/history-panel.js
--- a/static/scripts/packed/mvc/history/history-panel.js
+++ b/static/scripts/packed/mvc/history/history-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/history/history-model","mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/dataset/hda-edit"],function(c,f,a,i){var g=SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});g.storageKey=function h(){return("history-panel")};var d=SessionStorageModel.extend({defaults:{expandedHdas:{},show_deleted:false,show_hidden:false},addExpandedHda:function(j){this.save("expandedHdas",_.extend(this.get("expandedHdas"),_.object([j],[true])))},removeExpandedHda:function(j){this.save("expandedHdas",_.omit(this.get("expandedHdas"),j))},toString:function(){return"HistoryPrefs("+this.id+")"}});d.historyStorageKey=function b(j){if(!j){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+j)}return("history:"+j)};var e=Backbone.View.extend(LoggableMixin).extend({defaultHDAViewClass:i.HDAEditView,tagName:"div",className:"history-panel",fxSpeed:"fast",datasetsSelector:".datasets-list",emptyMsgSelector:".empty-history-message",msgsSelector:".message-container",initialize:function(j){j=j||{};if(j.logger){this.logger=j.logger}this.log(this+".initialize:",j);this.HDAViewClass=j.HDAViewClass||this.defaultHDAViewClass;this.linkTarget=j.linkTarget||"_blank";this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);this.preferences=new g(_.extend({id:g.storageKey()},_.pick(j,_.keys(g.prototype.defaults))));this.filters=[];this.selecting=j.selecting||false;this.annotationEditorShown=j.annotationEditorShown||false;this._setUpListeners();if(this.model){this._setUpWebStorage(j.initiallyExpanded,j.show_deleted,j.show_hidden);this._setUpModelEventHandlers()}if(j.onready){j.onready.call(this)}},_setUpListeners:function(){this.on("error",function(k,n,j,m,l){this.errorHandler(k,n,j,m,l)});this.on("loading-history",function(){this.showLoadingIndicator("loading history...")});this.on("loading-done",function(){this.hideLoadingIndicator()});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});this.on("switched-history current-history new-history",function(){if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});if(this.logger){this.on("all",function(j){this.log(this+"",arguments)},this)}},errorHandler:function(l,o,k,n,m){var j=this._parseErrorMessage(l,o,k,n,m);if(o&&o.status===0&&o.readyState===0){}else{if(o&&o.status===502){}else{if(!this.$el.find(this.msgsSelector).is(":visible")){this.once("rendered",function(){this.displayMessage("error",j.message,j.details)})}else{this.displayMessage("error",j.message,j.details)}}}},_parseErrorMessage:function(m,q,l,p,o){var k=Galaxy.currUser,j={message:this._bePolite(p),details:{user:(k instanceof User)?(k.toJSON()):(k+""),source:(m instanceof Backbone.Model)?(m.toJSON()):(m+""),xhr:q,options:(q)?(_.omit(l,"xhr")):(l)}};_.extend(j.details,o||{});if(q&&_.isFunction(q.getAllResponseHeaders)){var n=q.getAllResponseHeaders();n=_.compact(n.split("\n"));n=_.map(n,function(r){return r.split(": ")});j.details.xhr.responseHeaders=_.object(n)}return j},_bePolite:function(j){j=j||_l("An error occurred while getting updates from the server");return j+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadCurrentHistory:function(k){var j=this;return this.loadHistoryWithHDADetails("current",k).then(function(m,l){j.trigger("current-history",j)})},switchToHistory:function(m,l){var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories/"+m+"/set_as_current")};return this.loadHistoryWithHDADetails(m,l,k).then(function(o,n){j.trigger("switched-history",j)})},createNewHistory:function(l){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,l,k).then(function(n,m){j.trigger("new-history",j)})},loadHistoryWithHDADetails:function(m,l,k,o){var j=this,n=function(p){return j.getExpandedHdaIds(p.id)};return this.loadHistory(m,l,k,o,n)},loadHistory:function(m,l,k,p,n){this.trigger("loading-history",this);l=l||{};var j=this;var o=c.History.getHistoryData(m,{historyFn:k,hdaFn:p,hdaDetailIds:l.initiallyExpanded||n});return this._loadHistoryFromXHR(o,l).fail(function(s,q,r){j.trigger("error",j,s,l,_l("An error was encountered while "+q),{historyId:m,history:r||{}})}).always(function(){j.trigger("loading-done",j)})},_loadHistoryFromXHR:function(l,k){var j=this;l.then(function(m,n){j.setModel(m,n,k)});l.fail(function(n,m){j.render()});return l},setModel:function(l,j,k){k=k||{};if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.hdaViews={};if(Galaxy&&Galaxy.currUser){l.user=Galaxy.currUser.toJSON()}this.model=new c.History(l,j,k);this._setUpWebStorage(k.initiallyExpanded,k.show_deleted,k.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this);this.render();return this},_setUpWebStorage:function(k,j,l){this.storage=new d({id:d.historyStorageKey(this.model.get("id"))});if(_.isObject(k)){this.storage.set("exandedHdas",k)}if(_.isBoolean(j)){this.storage.set("show_deleted",j)}if(_.isBoolean(l)){this.storage.set("show_hidden",l)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get())},clearWebStorage:function(){for(var j in sessionStorage){if(j.indexOf("history:")===0){sessionStorage.removeItem(j)}}},getStoredOptions:function(k){if(!k||k==="current"){return(this.storage)?(this.storage.get()):({})}var j=sessionStorage.getItem(d.historyStorageKey(k));return(j===null)?({}):(JSON.parse(j))},getExpandedHdaIds:function(j){var k=this.getStoredOptions(j).expandedHdas;return((_.isEmpty(k))?([]):(_.keys(k)))},_setUpModelEventHandlers:function(){this.model.on("error error:hdas",function(k,m,j,l){this.errorHandler(k,m,j,l)},this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("add",this.addHdaView,this);this.model.hdas.on("change:deleted",this.handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this.handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(j){this.model.fetch()},this);this.model.hdas.on("state:ready",function(k,l,j){if((!k.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[k.id])}},this)},render:function(l,m){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k;if(this.model){k=this.renderModel()}else{k=this.renderWithoutModel()}$(j).queue("fx",[function(n){if(l&&j.$el.is(":visible")){j.$el.fadeOut(l,n)}else{n()}},function(n){j.$el.empty();if(k){j.$el.append(k.children());j.renderBasedOnPrefs()}n()},function(n){if(l&&!j.$el.is(":visible")){j.$el.fadeIn(l,n)}else{n()}},function(n){if(m){m.call(this)}j.trigger("rendered",this);n()}]);return this},renderWithoutModel:function(){var j=$("<div/>"),k=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return j.append(k)},renderModel:function(){var j=$("<div/>");if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){j.append(e.templates.anonHistoryPanel(this.model.toJSON()))}else{j.append(e.templates.historyPanel(this.model.toJSON()));if(Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(j);this._renderAnnotation(j)}}j.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(j);this.renderHdas(j);return j},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.showSearchControls(0)}},_renderTags:function(j){var k=this;this.tagsEditor=new TagsEditor({model:this.model,el:j.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("tagsEditorShown",true);k.toggleHDATagEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("tagsEditorShown",false);k.toggleHDATagEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}},_renderAnnotation:function(j){var k=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:j.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("annotationEditorShown",true);k.toggleHDAAnnotationEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("annotationEditorShown",false);k.toggleHDAAnnotationEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history Annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}},_renderSearchButton:function(j){return faIconButton({title:_l("Search datasets"),classes:"history-search-btn",faIcon:"fa-search"})},_renderSelectButton:function(j){return faIconButton({title:_l("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(j){j=j||this.$el;j.find("[title]").tooltip({placement:"bottom"});if((!this.model)||(!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var k=this;j.find(".history-name").attr("title",_l("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(l){j.find(".history-name").text(l);k.model.save({name:l}).fail(function(){j.find(".history-name").text(k.model.previous("name"))})}});this._setUpDatasetActionsPopup(j)},_setUpDatasetActionsPopup:function(j){var k=this;(new PopupMenu(j.find(".history-dataset-action-popup-btn"),[{html:_l("Hide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.hide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Unhide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.unhide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Delete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype["delete"];k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Undelete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.undelete;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Permanently delete datasets"),func:function(){if(confirm(_l("This will permanently remove the data in your datasets. Are you sure?"))){var l=f.HistoryDatasetAssociation.prototype.purge;k.getSelectedHdaCollection().ajaxQueue(l)}}}]))},refreshHdas:function(k,j){if(this.model){return this.model.refresh(k,j)}return $.when()},addHdaView:function(m){this.log("add."+this,m);var k=this;if(!m.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return}$({}).queue([function l(o){var n=k.$el.find(k.emptyMsgSelector);if(n.is(":visible")){n.fadeOut(k.fxSpeed,o)}else{o()}},function j(o){k.scrollToTop();var n=k.$el.find(k.datasetsSelector);k.createHdaView(m).$el.hide().prependTo(n).slideDown(k.fxSpeed)}])},createHdaView:function(l){var k=l.get("id"),j=this.storage.get("expandedHdas")[k],m=new this.HDAViewClass({model:l,linkTarget:this.linkTarget,expanded:j,tagsEditorShown:this.preferences.get("tagsEditorShown"),annotationEditorShown:this.preferences.get("annotationEditorShown"),selectable:this.selecting,hasUser:this.model.ownedByCurrUser(),logger:this.logger});this._setUpHdaListeners(m);this.hdaViews[k]=m;return m.render()},_setUpHdaListeners:function(k){var j=this;k.on("body-expanded",function(l){j.storage.addExpandedHda(l)});k.on("body-collapsed",function(l){j.storage.removeExpandedHda(l)});k.on("error",function(m,o,l,n){j.errorHandler(m,o,l,n)})},handleHdaDeletionChange:function(j){if(j.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[j.id])}},handleHdaVisibleChange:function(j){if(j.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[j.id])}},removeHdaView:function(k){if(!k){return}var j=this;k.$el.fadeOut(j.fxSpeed,function(){k.off();k.remove();delete j.hdaViews[k.model.id];if(_.isEmpty(j.hdaViews)){j.$el.find(j.emptyMsgSelector).fadeIn(j.fxSpeed,function(){j.trigger("empty-history",j)})}})},renderHdas:function(l){l=l||this.$el;this.hdaViews={};var k=this,j=l.find(this.datasetsSelector),m=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);j.empty();if(m.length){m.each(function(n){j.prepend(k.createHdaView(n).$el)});l.find(this.emptyMsgSelector).hide()}else{l.find(this.emptyMsgSelector).show()}return this.hdaViews},toggleHDATagEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.tagsEditor){l.tagsEditor.toggle.apply(l.tagsEditor,k)}})},toggleHDAAnnotationEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.annotationEditor){l.annotationEditor.toggle.apply(l.annotationEditor,k)}})},events:{"click .message-container":"clearMessages","click .history-search-btn":"toggleSearchControls","click .history-select-btn":function(j){this.toggleSelectors(this.fxSpeed)},"click .history-select-all-datasets-btn":"selectAllDatasets"},updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(j){j.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},toggleShowDeleted:function(){this.storage.set("show_deleted",!this.storage.get("show_deleted"));this.renderHdas();return this.storage.get("show_deleted")},toggleShowHidden:function(){this.storage.set("show_hidden",!this.storage.get("show_hidden"));this.renderHdas();return this.storage.get("show_hidden")},renderSearchControls:function(k){var l=this;function n(o){l.searchFor=o;l.filters=[function(p){return p.matchesAll(l.searchFor)}];l.trigger("search:searching",o,l);l.renderHdas()}function j(o){if(l.model.hdas.haveDetails()){n(o);return}l.$el.find(".history-search-controls").searchInput("toggle-loading");l.model.hdas.fetchAllDetails({silent:true}).always(function(){l.$el.find(".history-search-controls").searchInput("toggle-loading")}).done(function(){n(o)})}function m(){l.searchFor="";l.filters=[];l.trigger("search:clear",l);l.renderHdas()}return k.searchInput({initialVal:l.searchFor,name:"history-search",placeholder:"search datasets",classes:"history-search",onfirstsearch:j,onsearch:n,onclear:m})},showSearchControls:function(l){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k=this.$el.find(".history-search-controls");if(!k.children().size()){k=this.renderSearchControls(k).hide()}k.show(l,function(){$(this).find("input").focus();j.preferences.set("searching",true)})},hideSearchControls:function(){speed=(speed===undefined)?(this.fxSpeed):(speed);var j=this;this.$el.find(".history-search-controls").hide(speed,function(){j.preferences.set("searching",false)})},toggleSearchControls:function(j){speed=(jQuery.type(j)==="number")?(j):(this.fxSpeed);if(this.$el.find(".history-search-controls").is(":visible")){this.hideSearchControls(speed)}else{this.showSearchControls(speed)}},showSelectors:function(j){this.selecting=true;this.$el.find(".history-dataset-actions").slideDown(j);_.each(this.hdaViews,function(k){k.showSelector(j)})},hideSelectors:function(j){this.selecting=false;this.$el.find(".history-dataset-actions").slideUp(j);_.each(this.hdaViews,function(k){k.hideSelector(j)})},toggleSelectors:function(j){if(!this.selecting){this.showSelectors(j)}else{this.hideSelectors(j)}},selectAllDatasets:function(k){var j=this.$el.find(".history-select-all-datasets-btn");currMode=j.data("mode");if(currMode==="select"){_.each(this.hdaViews,function(l){l.select(k)});j.data("mode","deselect");j.text(_l("De-select all"))}else{if(currMode==="deselect"){_.each(this.hdaViews,function(l){l.deselect(k)});j.data("mode","select");j.text(_l("Select all"))}}},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(j){return j.selected})},getSelectedHdaCollection:function(){return new f.HDACollection(_.map(this.getSelectedHdaViews(),function(j){return j.model}),{historyId:this.model.id})},showLoadingIndicator:function(k,j,l){j=(j!==undefined)?(j):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,l)}else{this.$el.fadeOut(j);this.indicator.show(k,j,l)}},hideLoadingIndicator:function(j,k){j=(j!==undefined)?(j):(this.fxSpeed);if(this.indicator){this.indicator.hide(j,k)}},displayMessage:function(o,p,n){var l=this;this.scrollToTop();var m=this.$el.find(this.msgsSelector),j=$("<div/>").addClass(o+"message").html(p);if(!_.isEmpty(n)){var k=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(l.messageToModalOptions(o,p,n));return false});j.append(" ",k)}return m.html(j)},messageToModalOptions:function(n,p,m){var j=this,o=$("<div/>"),l={title:"Details"};function k(q){q=_.omit(q,_.functions(q));return["<table>",_.map(q,function(s,r){s=(_.isObject(s))?(k(s)):(s);return'<tr><td style="vertical-align: top; color: grey">'+r+'</td><td style="padding-left: 8px">'+s+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(m)){l.body=o.append(k(m))}else{l.body=o.html(m)}l.buttons={Ok:function(){Galaxy.modal.hide();j.clearMessages()}};return l},clearMessages:function(){var j=this.$el.find(this.msgsSelector);j.empty()},scrollPosition:function(){return this.$el.parent().scrollTop()},scrollTo:function(j){this.$el.parent().scrollTop(j)},scrollToTop:function(){this.$el.parent().scrollTop(0);return this},scrollIntoView:function(k,l){if(!l){this.$el.parent().parent().scrollTop(k);return this}var j=window,m=this.$el.parent().parent(),o=$(j).innerHeight(),n=(o/2)-(l/2);$(m).scrollTop(k-n);return this},scrollToId:function(k){if((!k)||(!this.hdaViews[k])){return this}var j=this.hdaViews[k].$el;this.scrollIntoView(j.offset().top,j.outerHeight());return this},scrollToHid:function(j){var k=this.model.hdas.getByHid(j);if(!k){return this}return this.scrollToId(k.id)},connectToQuotaMeter:function(j){if(!j){return this}this.listenTo(j,"quota:over",this.showQuotaMessage);this.listenTo(j,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(j&&j.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var j=this.$el.find(".quota-message");if(j.is(":hidden")){j.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var j=this.$el.find(".quota-message");if(!j.is(":hidden")){j.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(j){if(!j){return this}this.on("new-storage",function(l,k){if(j&&l){j.findItemByHtml(_l("Include Deleted Datasets")).checked=l.get("show_deleted");j.findItemByHtml(_l("Include Hidden Datasets")).checked=l.get("show_hidden")}});return this},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});e.templates={historyPanel:Handlebars.templates["template-history-historyPanel"],anonHistoryPanel:Handlebars.templates["template-history-historyPanel-anon"]};return{HistoryPanel:e}});
\ No newline at end of file
+define(["mvc/history/history-model","mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/dataset/hda-edit"],function(c,f,a,i){var g=SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});g.storageKey=function h(){return("history-panel")};var d=SessionStorageModel.extend({defaults:{expandedHdas:{},show_deleted:false,show_hidden:false},addExpandedHda:function(j){this.save("expandedHdas",_.extend(this.get("expandedHdas"),_.object([j],[true])))},removeExpandedHda:function(j){this.save("expandedHdas",_.omit(this.get("expandedHdas"),j))},toString:function(){return"HistoryPrefs("+this.id+")"}});d.historyStorageKey=function b(j){if(!j){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+j)}return("history:"+j)};var e=Backbone.View.extend(LoggableMixin).extend({defaultHDAViewClass:i.HDAEditView,tagName:"div",className:"history-panel",fxSpeed:"fast",datasetsSelector:".datasets-list",emptyMsgSelector:".empty-history-message",msgsSelector:".message-container",initialize:function(j){j=j||{};if(j.logger){this.logger=j.logger}this.log(this+".initialize:",j);this.HDAViewClass=j.HDAViewClass||this.defaultHDAViewClass;this.linkTarget=j.linkTarget||"_blank";this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);this.preferences=new g(_.extend({id:g.storageKey()},_.pick(j,_.keys(g.prototype.defaults))));this.filters=[];this.selecting=j.selecting||false;this.annotationEditorShown=j.annotationEditorShown||false;this._setUpListeners();if(this.model){this._setUpWebStorage(j.initiallyExpanded,j.show_deleted,j.show_hidden);this._setUpModelEventHandlers()}if(j.onready){j.onready.call(this)}},_setUpListeners:function(){this.on("error",function(k,n,j,m,l){this.errorHandler(k,n,j,m,l)});this.on("loading-history",function(){this.showLoadingIndicator("loading history...")});this.on("loading-done",function(){this.hideLoadingIndicator()});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});this.on("switched-history current-history new-history",function(){if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});if(this.logger){this.on("all",function(j){this.log(this+"",arguments)},this)}},errorHandler:function(l,o,k,n,m){var j=this._parseErrorMessage(l,o,k,n,m);if(o&&o.status===0&&o.readyState===0){}else{if(o&&o.status===502){}else{if(!this.$el.find(this.msgsSelector).is(":visible")){this.once("rendered",function(){this.displayMessage("error",j.message,j.details)})}else{this.displayMessage("error",j.message,j.details)}}}},_parseErrorMessage:function(m,q,l,p,o){var k=Galaxy.currUser,j={message:this._bePolite(p),details:{user:(k instanceof User)?(k.toJSON()):(k+""),source:(m instanceof Backbone.Model)?(m.toJSON()):(m+""),xhr:q,options:(q)?(_.omit(l,"xhr")):(l)}};_.extend(j.details,o||{});if(q&&_.isFunction(q.getAllResponseHeaders)){var n=q.getAllResponseHeaders();n=_.compact(n.split("\n"));n=_.map(n,function(r){return r.split(": ")});j.details.xhr.responseHeaders=_.object(n)}return j},_bePolite:function(j){j=j||_l("An error occurred while getting updates from the server");return j+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadCurrentHistory:function(k){var j=this;return this.loadHistoryWithHDADetails("current",k).then(function(m,l){j.trigger("current-history",j)})},switchToHistory:function(m,l){var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories/"+m+"/set_as_current")};return this.loadHistoryWithHDADetails(m,l,k).then(function(o,n){j.trigger("switched-history",j)})},createNewHistory:function(l){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,l,k).then(function(n,m){j.trigger("new-history",j)})},loadHistoryWithHDADetails:function(m,l,k,o){var j=this,n=function(p){return j.getExpandedHdaIds(p.id)};return this.loadHistory(m,l,k,o,n)},loadHistory:function(m,l,k,p,n){this.trigger("loading-history",this);l=l||{};var j=this;var o=c.History.getHistoryData(m,{historyFn:k,hdaFn:p,hdaDetailIds:l.initiallyExpanded||n});return this._loadHistoryFromXHR(o,l).fail(function(s,q,r){j.trigger("error",j,s,l,_l("An error was encountered while "+q),{historyId:m,history:r||{}})}).always(function(){j.trigger("loading-done",j)})},_loadHistoryFromXHR:function(l,k){var j=this;l.then(function(m,n){j.setModel(m,n,k)});l.fail(function(n,m){j.render()});return l},setModel:function(l,j,k){k=k||{};if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.hdaViews={};if(Galaxy&&Galaxy.currUser){l.user=Galaxy.currUser.toJSON()}this.model=new c.History(l,j,k);this._setUpWebStorage(k.initiallyExpanded,k.show_deleted,k.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this);this.render();return this},_setUpWebStorage:function(k,j,l){this.storage=new d({id:d.historyStorageKey(this.model.get("id"))});if(_.isObject(k)){this.storage.set("exandedHdas",k)}if(_.isBoolean(j)){this.storage.set("show_deleted",j)}if(_.isBoolean(l)){this.storage.set("show_hidden",l)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get())},clearWebStorage:function(){for(var j in sessionStorage){if(j.indexOf("history:")===0){sessionStorage.removeItem(j)}}},getStoredOptions:function(k){if(!k||k==="current"){return(this.storage)?(this.storage.get()):({})}var j=sessionStorage.getItem(d.historyStorageKey(k));return(j===null)?({}):(JSON.parse(j))},getExpandedHdaIds:function(j){var k=this.getStoredOptions(j).expandedHdas;return((_.isEmpty(k))?([]):(_.keys(k)))},_setUpModelEventHandlers:function(){this.model.on("error error:hdas",function(k,m,j,l){this.errorHandler(k,m,j,l)},this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("add",this.addHdaView,this);this.model.hdas.on("change:deleted",this.handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this.handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(j){this.model.fetch()},this);this.model.hdas.on("state:ready",function(k,l,j){if((!k.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[k.id])}},this)},render:function(l,m){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k;if(this.model){k=this.renderModel()}else{k=this.renderWithoutModel()}$(j).queue("fx",[function(n){if(l&&j.$el.is(":visible")){j.$el.fadeOut(l,n)}else{n()}},function(n){j.$el.empty();if(k){j.$el.append(k.children());j.renderBasedOnPrefs()}n()},function(n){if(l&&!j.$el.is(":visible")){j.$el.fadeIn(l,n)}else{n()}},function(n){if(m){m.call(this)}j.trigger("rendered",this);n()}]);return this},renderWithoutModel:function(){var j=$("<div/>"),k=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return j.append(k)},renderModel:function(){var j=$("<div/>");if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){j.append(e.templates.anonHistoryPanel(this.model.toJSON()))}else{j.append(e.templates.historyPanel(this.model.toJSON()));if(Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(j);this._renderAnnotation(j)}}j.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(j);this.renderHdas(j);return j},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.showSearchControls(0)}},_renderTags:function(j){var k=this;this.tagsEditor=new TagsEditor({model:this.model,el:j.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("tagsEditorShown",true);k.toggleHDATagEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("tagsEditorShown",false);k.toggleHDATagEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}},_renderAnnotation:function(j){var k=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:j.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("annotationEditorShown",true);k.toggleHDAAnnotationEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("annotationEditorShown",false);k.toggleHDAAnnotationEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history Annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}},_renderSearchButton:function(j){return faIconButton({title:_l("Search datasets"),classes:"history-search-btn",faIcon:"fa-search"})},_renderSelectButton:function(j){return faIconButton({title:_l("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(j){j=j||this.$el;j.find("[title]").tooltip({placement:"bottom"});if((!this.model)||(!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var k=this;j.find(".history-name").attr("title",_l("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(l){var m=k.model.get("name");if(l&&l!==m){k.$el.find(".history-name").text(l);k.model.save({name:l}).fail(function(){k.$el.find(".history-name").text(k.model.previous("name"))})}else{k.$el.find(".history-name").text(m)}}});this._setUpDatasetActionsPopup(j)},_setUpDatasetActionsPopup:function(j){var k=this;(new PopupMenu(j.find(".history-dataset-action-popup-btn"),[{html:_l("Hide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.hide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Unhide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.unhide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Delete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype["delete"];k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Undelete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.undelete;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Permanently delete datasets"),func:function(){if(confirm(_l("This will permanently remove the data in your datasets. Are you sure?"))){var l=f.HistoryDatasetAssociation.prototype.purge;k.getSelectedHdaCollection().ajaxQueue(l)}}}]))},refreshHdas:function(k,j){if(this.model){return this.model.refresh(k,j)}return $.when()},addHdaView:function(m){this.log("add."+this,m);var k=this;if(!m.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return}$({}).queue([function l(o){var n=k.$el.find(k.emptyMsgSelector);if(n.is(":visible")){n.fadeOut(k.fxSpeed,o)}else{o()}},function j(o){k.scrollToTop();var n=k.$el.find(k.datasetsSelector);k.createHdaView(m).$el.hide().prependTo(n).slideDown(k.fxSpeed)}])},createHdaView:function(l){var k=l.get("id"),j=this.storage.get("expandedHdas")[k],m=new this.HDAViewClass({model:l,linkTarget:this.linkTarget,expanded:j,tagsEditorShown:this.preferences.get("tagsEditorShown"),annotationEditorShown:this.preferences.get("annotationEditorShown"),selectable:this.selecting,hasUser:this.model.ownedByCurrUser(),logger:this.logger});this._setUpHdaListeners(m);this.hdaViews[k]=m;return m.render()},_setUpHdaListeners:function(k){var j=this;k.on("body-expanded",function(l){j.storage.addExpandedHda(l)});k.on("body-collapsed",function(l){j.storage.removeExpandedHda(l)});k.on("error",function(m,o,l,n){j.errorHandler(m,o,l,n)})},handleHdaDeletionChange:function(j){if(j.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[j.id])}},handleHdaVisibleChange:function(j){if(j.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[j.id])}},removeHdaView:function(k){if(!k){return}var j=this;k.$el.fadeOut(j.fxSpeed,function(){k.off();k.remove();delete j.hdaViews[k.model.id];if(_.isEmpty(j.hdaViews)){j.$el.find(j.emptyMsgSelector).fadeIn(j.fxSpeed,function(){j.trigger("empty-history",j)})}})},renderHdas:function(l){l=l||this.$el;this.hdaViews={};var k=this,j=l.find(this.datasetsSelector),m=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);j.empty();if(m.length){m.each(function(n){j.prepend(k.createHdaView(n).$el)});l.find(this.emptyMsgSelector).hide()}else{l.find(this.emptyMsgSelector).show()}return this.hdaViews},toggleHDATagEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.tagsEditor){l.tagsEditor.toggle.apply(l.tagsEditor,k)}})},toggleHDAAnnotationEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.annotationEditor){l.annotationEditor.toggle.apply(l.annotationEditor,k)}})},events:{"click .message-container":"clearMessages","click .history-search-btn":"toggleSearchControls","click .history-select-btn":function(j){this.toggleSelectors(this.fxSpeed)},"click .history-select-all-datasets-btn":"selectAllDatasets"},updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(j){j.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},toggleShowDeleted:function(){this.storage.set("show_deleted",!this.storage.get("show_deleted"));this.renderHdas();return this.storage.get("show_deleted")},toggleShowHidden:function(){this.storage.set("show_hidden",!this.storage.get("show_hidden"));this.renderHdas();return this.storage.get("show_hidden")},renderSearchControls:function(k){var l=this;function n(o){l.searchFor=o;l.filters=[function(p){return p.matchesAll(l.searchFor)}];l.trigger("search:searching",o,l);l.renderHdas()}function j(o){if(l.model.hdas.haveDetails()){n(o);return}l.$el.find(".history-search-controls").searchInput("toggle-loading");l.model.hdas.fetchAllDetails({silent:true}).always(function(){l.$el.find(".history-search-controls").searchInput("toggle-loading")}).done(function(){n(o)})}function m(){l.searchFor="";l.filters=[];l.trigger("search:clear",l);l.renderHdas()}return k.searchInput({initialVal:l.searchFor,name:"history-search",placeholder:"search datasets",classes:"history-search",onfirstsearch:j,onsearch:n,onclear:m})},showSearchControls:function(l){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k=this.$el.find(".history-search-controls");if(!k.children().size()){k=this.renderSearchControls(k).hide()}k.show(l,function(){$(this).find("input").focus();j.preferences.set("searching",true)})},hideSearchControls:function(){speed=(speed===undefined)?(this.fxSpeed):(speed);var j=this;this.$el.find(".history-search-controls").hide(speed,function(){j.preferences.set("searching",false)})},toggleSearchControls:function(j){speed=(jQuery.type(j)==="number")?(j):(this.fxSpeed);if(this.$el.find(".history-search-controls").is(":visible")){this.hideSearchControls(speed)}else{this.showSearchControls(speed)}},showSelectors:function(j){this.selecting=true;this.$el.find(".history-dataset-actions").slideDown(j);_.each(this.hdaViews,function(k){k.showSelector(j)})},hideSelectors:function(j){this.selecting=false;this.$el.find(".history-dataset-actions").slideUp(j);_.each(this.hdaViews,function(k){k.hideSelector(j)})},toggleSelectors:function(j){if(!this.selecting){this.showSelectors(j)}else{this.hideSelectors(j)}},selectAllDatasets:function(k){var j=this.$el.find(".history-select-all-datasets-btn");currMode=j.data("mode");if(currMode==="select"){_.each(this.hdaViews,function(l){l.select(k)});j.data("mode","deselect");j.text(_l("De-select all"))}else{if(currMode==="deselect"){_.each(this.hdaViews,function(l){l.deselect(k)});j.data("mode","select");j.text(_l("Select all"))}}},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(j){return j.selected})},getSelectedHdaCollection:function(){return new f.HDACollection(_.map(this.getSelectedHdaViews(),function(j){return j.model}),{historyId:this.model.id})},showLoadingIndicator:function(k,j,l){j=(j!==undefined)?(j):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,l)}else{this.$el.fadeOut(j);this.indicator.show(k,j,l)}},hideLoadingIndicator:function(j,k){j=(j!==undefined)?(j):(this.fxSpeed);if(this.indicator){this.indicator.hide(j,k)}},displayMessage:function(o,p,n){var l=this;this.scrollToTop();var m=this.$el.find(this.msgsSelector),j=$("<div/>").addClass(o+"message").html(p);if(!_.isEmpty(n)){var k=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(l.messageToModalOptions(o,p,n));return false});j.append(" ",k)}return m.html(j)},messageToModalOptions:function(n,p,m){var j=this,o=$("<div/>"),l={title:"Details"};function k(q){q=_.omit(q,_.functions(q));return["<table>",_.map(q,function(s,r){s=(_.isObject(s))?(k(s)):(s);return'<tr><td style="vertical-align: top; color: grey">'+r+'</td><td style="padding-left: 8px">'+s+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(m)){l.body=o.append(k(m))}else{l.body=o.html(m)}l.buttons={Ok:function(){Galaxy.modal.hide();j.clearMessages()}};return l},clearMessages:function(){var j=this.$el.find(this.msgsSelector);j.empty()},scrollPosition:function(){return this.$el.parent().scrollTop()},scrollTo:function(j){this.$el.parent().scrollTop(j)},scrollToTop:function(){this.$el.parent().scrollTop(0);return this},scrollIntoView:function(k,l){if(!l){this.$el.parent().parent().scrollTop(k);return this}var j=window,m=this.$el.parent().parent(),o=$(j).innerHeight(),n=(o/2)-(l/2);$(m).scrollTop(k-n);return this},scrollToId:function(k){if((!k)||(!this.hdaViews[k])){return this}var j=this.hdaViews[k].$el;this.scrollIntoView(j.offset().top,j.outerHeight());return this},scrollToHid:function(j){var k=this.model.hdas.getByHid(j);if(!k){return this}return this.scrollToId(k.id)},connectToQuotaMeter:function(j){if(!j){return this}this.listenTo(j,"quota:over",this.showQuotaMessage);this.listenTo(j,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(j&&j.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var j=this.$el.find(".quota-message");if(j.is(":hidden")){j.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var j=this.$el.find(".quota-message");if(!j.is(":hidden")){j.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(j){if(!j){return this}this.on("new-storage",function(l,k){if(j&&l){j.findItemByHtml(_l("Include Deleted Datasets")).checked=l.get("show_deleted");j.findItemByHtml(_l("Include Hidden Datasets")).checked=l.get("show_hidden")}});return this},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});e.templates={historyPanel:Handlebars.templates["template-history-historyPanel"],anonHistoryPanel:Handlebars.templates["template-history-historyPanel-anon"]};return{HistoryPanel:e}});
\ 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
commit/galaxy-central: greg: Fix for determining a guid in the install_manager.
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a9a0ac9c1afa/
Changeset: a9a0ac9c1afa
Branch: stable
User: greg
Date: 2013-12-19 01:03:05
Summary: Fix for determining a guid in the install_manager.
Affected #: 1 file
diff -r 26da838b778c68a5577d4cca9836f6ef70b8ed1b -r a9a0ac9c1afae8dd0d662c9c800d0228c883c107 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
@@ -244,21 +244,20 @@
def get_guid( self, repository_clone_url, relative_install_dir, tool_config ):
if self.shed_config_dict.get( 'tool_path' ):
- relative_install_dir = os.path.join( self.shed_config_dict['tool_path'], relative_install_dir )
- found = False
+ relative_install_dir = os.path.join( self.shed_config_dict[ 'tool_path' ], relative_install_dir )
+ tool_config_filename = suc.strip_path( tool_config )
for root, dirs, files in os.walk( relative_install_dir ):
if root.find( '.hg' ) < 0 and root.find( 'hgrc' ) < 0:
if '.hg' in dirs:
dirs.remove( '.hg' )
for name in files:
- if name == tool_config:
- found = True
- break
- if found:
- break
- full_path = str( os.path.abspath( os.path.join( root, name ) ) )
- tool = self.toolbox.load_tool( full_path )
- return suc.generate_tool_guid( repository_clone_url, tool )
+ filename = suc.strip_path( name )
+ if filename == tool_config_filename:
+ full_path = str( os.path.abspath( os.path.join( root, name ) ) )
+ tool = self.toolbox.load_tool( full_path )
+ return suc.generate_tool_guid( repository_clone_url, tool )
+ # Not quite sure what should happen here, throw an exception or what?
+ return None
def get_prior_install_required_dict( self, tool_shed_repositories, repository_dependencies_dict ):
"""
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: greg: Don't uninstall repositories when testing tool dependency definitions in the tool shed's install and test process.
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/67fe4b091f0a/
Changeset: 67fe4b091f0a
User: greg
Date: 2013-12-19 14:47:20
Summary: Don't uninstall repositories when testing tool dependency definitions in the tool shed's install and test process.
Affected #: 1 file
diff -r 5bfa1d47b3999a695a04294adb13da0b5b3ec041 -r 67fe4b091f0ad66d68d06a36b8fb645aa445d032 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -201,28 +201,18 @@
else:
log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
( changeset_revision, name, owner ) )
- # Keep statistics for this repository's tool dependencies that resulted in installation errors.
- for missing_tool_dependency in repository.missing_tool_dependencies:
- name = str( missing_tool_dependency.name )
- type = str( missing_tool_dependency.type )
- version = str( missing_tool_dependency.version )
- error_message = unicodify( missing_tool_dependency.error_message )
- missing_tool_dependency_info_dict = dict( type=type,
- name=name,
- version=version,
- error_message=error_message )
- install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ].append( missing_tool_dependency_info_dict )
- # Attempt to uninstall this repository and all of its dependencies if its repository dependencies or
- # tool dependencies resulted in an installation error.
- missing_tool_dependencies = install_and_test_base_util.get_missing_tool_dependencies( repository )
- if missing_tool_dependencies or repository.missing_repository_dependencies:
- install_and_test_base_util.handle_missing_dependencies( app=app,
- repository=repository,
- missing_tool_dependencies=missing_tool_dependencies,
- repository_dict=repository_dict,
- tool_test_results_dicts=tool_test_results_dicts,
- tool_test_results_dict=tool_test_results_dict,
- can_update_tool_shed=can_update_tool_shed )
+ if repository.missing_tool_dependencies:
+ # Keep statistics for this repository's tool dependencies that resulted in installation errors.
+ for missing_tool_dependency in repository.missing_tool_dependencies:
+ name = str( missing_tool_dependency.name )
+ type = str( missing_tool_dependency.type )
+ version = str( missing_tool_dependency.version )
+ error_message = unicodify( missing_tool_dependency.error_message )
+ missing_tool_dependency_info_dict = dict( type=type,
+ name=name,
+ version=version,
+ error_message=error_message )
+ install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ].append( missing_tool_dependency_info_dict )
else:
# This repository and all of its dependencies were successfully installed.
install_and_test_statistics_dict[ 'successful_installations' ].append( repository_identifier_dict )
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: greg: Fix for handling missing dependencies in the tool shed's install and test framework.
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/5bfa1d47b399/
Changeset: 5bfa1d47b399
User: greg
Date: 2013-12-19 02:32:15
Summary: Fix for handling missing dependencies in the tool shed's install and test framework.
Affected #: 2 files
diff -r 4077a5a1f801a772acd01d19c9695329a42c5958 -r 5bfa1d47b3999a695a04294adb13da0b5b3ec041 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -286,16 +286,15 @@
if 'missing_test_components' not in tool_test_results_dict:
tool_test_results_dict[ 'missing_test_components' ] = []
missing_tool_dependencies = install_and_test_base_util.get_missing_tool_dependencies( repository )
- if missing_tool_dependencies or repository.missing_repository_dependencies:
+ if missing_tool_dependencies or repository.missing_repository_dependencies:
install_and_test_statistics_dict = \
- install_and_test_base_util.handle_missing_dependencies( app,
- repository,
- missing_tool_dependencies,
- repository_dict,
- tool_test_results_dicts,
- tool_test_results_dict,
- install_and_test_statistics_dict,
- can_update_tool_shed )
+ install_and_test_base_util.handle_missing_dependencies( app=app,
+ repository=repository,
+ missing_tool_dependencies=missing_tool_dependencies,
+ repository_dict=repository_dict,
+ tool_test_results_dicts=tool_test_results_dicts,
+ tool_test_results_dict=tool_test_results_dict,
+ can_update_tool_shed=can_update_tool_shed )
# Set the test_toolbox.toolbox module-level variable to the new app.toolbox.
test_toolbox.toolbox = app.toolbox
else:
diff -r 4077a5a1f801a772acd01d19c9695329a42c5958 -r 5bfa1d47b3999a695a04294adb13da0b5b3ec041 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -216,14 +216,13 @@
# tool dependencies resulted in an installation error.
missing_tool_dependencies = install_and_test_base_util.get_missing_tool_dependencies( repository )
if missing_tool_dependencies or repository.missing_repository_dependencies:
- install_and_test_base_util.handle_missing_dependencies( app,
- repository,
- missing_tool_dependencies,
- repository_dict,
- tool_test_results_dicts,
- tool_test_results_dict,
- install_and_test_statistics_dict,
- can_update_tool_shed )
+ install_and_test_base_util.handle_missing_dependencies( app=app,
+ repository=repository,
+ missing_tool_dependencies=missing_tool_dependencies,
+ repository_dict=repository_dict,
+ tool_test_results_dicts=tool_test_results_dicts,
+ tool_test_results_dict=tool_test_results_dict,
+ can_update_tool_shed=can_update_tool_shed )
else:
# This repository and all of its dependencies were successfully installed.
install_and_test_statistics_dict[ 'successful_installations' ].append( repository_identifier_dict )
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: greg: Fix imports in the tool shed's functional test framework.
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4077a5a1f801/
Changeset: 4077a5a1f801
User: greg
Date: 2013-12-19 01:09:11
Summary: Fix imports in the tool shed's functional test framework.
Affected #: 3 files
diff -r a53f2f700699c9d88b1896814b3eb2ec8cc028b8 -r 4077a5a1f801a772acd01d19c9695329a42c5958 test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -595,7 +595,6 @@
exclude_list.append( exclude_dict )
log.debug( 'The xml document %s containing the exclude list defines the following %s repositories to be excluded from testing...' % \
( str( xml_filename ), str( exclude_count ) ) )
- #if '-list_repositories' in sys.argv:
for name, owner, changeset_revision in exclude_verbose:
if changeset_revision:
log.debug( 'Repository %s owned by %s, changeset revision %s.' % ( str( name ), str( owner ), str( changeset_revision ) ) )
diff -r a53f2f700699c9d88b1896814b3eb2ec8cc028b8 -r 4077a5a1f801a772acd01d19c9695329a42c5958 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -45,6 +45,7 @@
from galaxy.app import UniverseApplication
from galaxy.util.json import from_json_string
from galaxy.util.json import to_json_string
+from galaxy.util import unicodify
from galaxy.web import buildapp
from functional_tests import generate_config_file
from nose.plugins import Plugin
diff -r a53f2f700699c9d88b1896814b3eb2ec8cc028b8 -r 4077a5a1f801a772acd01d19c9695329a42c5958 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -45,6 +45,7 @@
from galaxy.app import UniverseApplication
from galaxy.util.json import from_json_string
from galaxy.util.json import to_json_string
+from galaxy.util import unicodify
from galaxy.web import buildapp
from functional_tests import generate_config_file
from nose.plugins import Plugin
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: greg: Fix for determining a guid in the install_manager.
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a53f2f700699/
Changeset: a53f2f700699
User: greg
Date: 2013-12-19 01:03:05
Summary: Fix for determining a guid in the install_manager.
Affected #: 1 file
diff -r 9fa754a203b7312d58211b5e1e7140bf36aec76a -r a53f2f700699c9d88b1896814b3eb2ec8cc028b8 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
@@ -244,21 +244,20 @@
def get_guid( self, repository_clone_url, relative_install_dir, tool_config ):
if self.shed_config_dict.get( 'tool_path' ):
- relative_install_dir = os.path.join( self.shed_config_dict['tool_path'], relative_install_dir )
- found = False
+ relative_install_dir = os.path.join( self.shed_config_dict[ 'tool_path' ], relative_install_dir )
+ tool_config_filename = suc.strip_path( tool_config )
for root, dirs, files in os.walk( relative_install_dir ):
if root.find( '.hg' ) < 0 and root.find( 'hgrc' ) < 0:
if '.hg' in dirs:
dirs.remove( '.hg' )
for name in files:
- if name == tool_config:
- found = True
- break
- if found:
- break
- full_path = str( os.path.abspath( os.path.join( root, name ) ) )
- tool = self.toolbox.load_tool( full_path )
- return suc.generate_tool_guid( repository_clone_url, tool )
+ filename = suc.strip_path( name )
+ if filename == tool_config_filename:
+ full_path = str( os.path.abspath( os.path.join( root, name ) ) )
+ tool = self.toolbox.load_tool( full_path )
+ return suc.generate_tool_guid( repository_clone_url, tool )
+ # Not quite sure what should happen here, throw an exception or what?
+ return None
def get_prior_install_required_dict( self, tool_shed_repositories, repository_dependencies_dict ):
"""
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: guerler: Grids: Pack scripts, remove grid_body from mako
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/9fa754a203b7/
Changeset: 9fa754a203b7
User: guerler
Date: 2013-12-18 23:28:16
Summary: Grids: Pack scripts, remove grid_body from mako
Affected #: 6 files
diff -r a3e2c3464298ebea3fd2ab93a43aac479ba9665a -r 9fa754a203b7312d58211b5e1e7140bf36aec76a static/scripts/packed/galaxy.grids.js
--- a/static/scripts/packed/galaxy.grids.js
+++ b/static/scripts/packed/galaxy.grids.js
@@ -1,1 +1,1 @@
-jQuery.ajaxSettings.traditional=true;define(["mvc/ui"],function(){var a=Backbone.Model.extend({defaults:{url_base:"",async:false,async_ops:[],categorical_filters:[],filters:{},sort_key:null,show_item_checkboxes:false,advanced_search:false,cur_page:1,num_pages:1,operation:undefined,item_ids:undefined},can_async_op:function(c){return _.indexOf(this.attributes.async_ops,c)!==-1},add_filter:function(g,h,d){if(d){var e=this.attributes.filters[g],c;if(e===null||e===undefined){c=h}else{if(typeof(e)=="string"){if(e=="All"){c=h}else{var f=[];f[0]=e;f[1]=h;c=f}}else{c=e;c.push(h)}}this.attributes.filters[g]=c}else{this.attributes.filters[g]=h}},remove_filter:function(d,g){var c=this.attributes.filters[d];if(c===null||c===undefined){return false}var f=true;if(typeof(c)==="string"){if(c=="All"){f=false}else{delete this.attributes.filters[d]}}else{var e=_.indexOf(c,g);if(e!==-1){c.splice(e,1)}else{f=false}}return f},get_url_data:function(){var c={async:this.attributes.async,sort:this.attributes.sort_key,page:this.attributes.cur_page,show_item_checkboxes:this.attributes.show_item_checkboxes,advanced_search:this.attributes.advanced_search};if(this.attributes.operation){c.operation=this.attributes.operation}if(this.attributes.item_ids){c.id=this.attributes.item_ids}var d=this;_.each(_.pairs(d.attributes.filters),function(e){c["f-"+e[0]]=e[1]});return c}});var b=Backbone.View.extend({grid:null,initialize:function(c){this.init_grid(c);this.init_grid_controls();$("input[type=text]").each(function(){$(this).click(function(){$(this).select()}).keyup(function(){$(this).css("font-style","normal")})})},init_grid:function(c){this.grid=c;this.init_grid_elements()},init_grid_controls:function(){$(".submit-image").each(function(){$(this).mousedown(function(){$(this).addClass("gray-background")});$(this).mouseup(function(){$(this).removeClass("gray-background")})});var c=this;$(".sort-link").each(function(){$(this).click(function(){c.set_sort_condition($(this).attr("sort_key"));return false})});$(".categorical-filter > a").each(function(){$(this).click(function(){c.set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});$(".text-filter-form").each(function(){$(this).submit(function(){var g=$(this).attr("column_key");var f=$("#input-"+g+"-filter");var h=f.val();f.val("");c.add_filter_condition(g,h);return false})});var d=$("#input-tags-filter");if(d.length){d.autocomplete(this.grid.history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var e=$("#input-name-filter");if(e.length){e.autocomplete(this.grid.history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}$(".advanced-search-toggle").each(function(){$(this).click(function(){$("#standard-search").slideToggle("fast");$("#advanced-search").slideToggle("fast");return false})})},init_grid_elements:function(){$(".grid").each(function(){var j=$(this).find("input.grid-row-select-checkbox");var i=$(this).find("span.grid-selected-count");var q=function(){i.text($(j).filter(":checked").length)};$(j).each(function(){$(this).change(q)});q()});if($(".community_rating_star").length!==0){$(".community_rating_star").rating({})}var p=this.grid.attributes;var o=this;$(".page-link > a").each(function(){$(this).click(function(){o.set_page($(this).attr("page_num"));return false})});$(".use-inbound").each(function(){$(this).click(function(i){o.execute({href:$(this).attr("href"),inbound:true});return false})});$(".use-outbound").each(function(){$(this).click(function(i){o.execute({href:$(this).attr("href")});return false})});for(var h in p.items){var k=$("#grid-"+h+"-popup");k.off();var d=new PopupMenu(k);var n=p.items[h];for(var g in p.operations){var e=p.operations[g];var l=e.label;var c=n.operation_config[l];var f=n.encode_id;if(c.allowed&&e.allow_popup){var m={html:e.label,href:c.url_args,target:c.target,confirmation_text:e.confirm,inbound:e.inbound};m.func=function(q){q.preventDefault();var j=$(q.target).html();var i=this.findItemByHtml(j);o.execute(i)};d.addItem(m)}}}},add_filter_condition:function(e,g){if(g===""){return false}this.grid.add_filter(e,g,true);var f=$("<span>"+g+"<a href='javascript:void(0);'><span class='delete-search-icon' /></span></a>");f.addClass("text-filter-val");var d=this;f.click(function(){d.grid.remove_filter(e,g);$(this).remove();d.go_page_one();d.execute()});var c=$("#"+e+"-filtering-criteria");c.append(f);this.go_page_one();this.execute()},set_sort_condition:function(h){var g=this.grid.get("sort_key");var f=h;if(g.indexOf(h)!==-1){if(g.substring(0,1)!=="-"){f="-"+h}else{}}$(".sort-arrow").remove();var e=(f.substring(0,1)=="-")?"↑":"↓";var c=$("<span>"+e+"</span>").addClass("sort-arrow");var d=$("#"+h+"-header");d.append(c);this.grid.set("sort_key",f);this.go_page_one();this.execute()},set_categorical_filter:function(e,g){var d=this.grid.get("categorical_filters")[e],f=this.grid.get("filters")[e];var c=this;$("."+e+"-filter").each(function(){var k=$.trim($(this).text());var i=d[k];var j=i[e];if(j==g){$(this).empty();$(this).addClass("current-filter");$(this).append(k)}else{if(j==f){$(this).empty();var h=$("<a href='#'>"+k+"</a>");h.click(function(){c.set_categorical_filter(e,j)});$(this).removeClass("current-filter");$(this).append(h)}}});this.grid.add_filter(e,g);this.go_page_one();this.execute()},set_page:function(c){var d=this;$(".page-link").each(function(){var i=$(this).attr("id"),g=parseInt(i.split("-")[2],10),e=d.grid.get("cur_page"),h;if(g===c){h=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(h)}else{if(g===e){h=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var f=$("<a href='#'>"+h+"</a>");f.click(function(){d.set_page(g)});$(this).append(f)}}});if(c==="all"){this.grid.set("cur_page",c)}else{this.grid.set("cur_page",parseInt(c,10))}this.execute()},submit_operation:function(f,g){var e=$('input[name="id"]:checked').length;if(!e>0){return false}var d=$(f).val();var c=[];$("input[name=id]:checked").each(function(){c.push($(this).val())});this.execute({operation:d,id:c,confirmation_text:g});return true},execute:function(l){var f=null;var e=null;var g=null;var c=null;var k=null;if(l){e=l.href;g=l.operation;f=l.id;c=l.confirmation_text;k=l.inbound;if(e!==undefined&&e.indexOf("operation=")!=-1){var j=e.split("?");if(j.length>1){var i=j[1];var d=i.split("&");for(var h=0;h<d.length;h++){if(d[h].indexOf("operation")!=-1){g=d[h].split("=")[1];g=g.replace(/\+/g," ")}else{if(d[h].indexOf("id")!=-1){f=d[h].split("=")[1]}}}}}}if(g&&f){if(c&&c!=""&&c!="None"){if(!confirm(c)){return false}}g=g.toLowerCase();this.grid.set({operation:g,item_ids:f});if(this.grid.can_async_op(g)){this.update_grid()}else{this.go_to(k,"")}return false}if(e){this.go_to(k,e);return false}if(this.grid.get("async")){this.update_grid()}else{this.go_to(k,"")}return false},go_to:function(f,d){var e=this.grid.get("async");this.grid.set("async",false);advanced_search=$("#advanced-search").is(":visible");this.grid.set("advanced_search",advanced_search);if(!d){d=this.grid.get("url_base")+"?"+$.param(this.grid.get_url_data())}this.grid.set({operation:undefined,item_ids:undefined,async:e});if(f){var c=$(".grid-header").closest(".inbound");if(c.length!==0){c.load(d);return}}window.location=d},update_grid:function(){var d=(this.grid.get("operation")?"POST":"GET");$(".loading-elt-overlay").show();var c=this;$.ajax({type:d,url:c.grid.get("url_base"),data:c.grid.get_url_data(),error:function(e){alert("Grid refresh failed")},success:function(f){var e=f.split("*****");$("#grid-table-body").html(e[0]);$("#grid-table-footer").html(e[1]);$("#grid-table-body").trigger("update");$(".loading-elt-overlay").hide();var g=$.trim(e[2]);if(g!==""){$("#grid-message").html(g).show();setTimeout(function(){$("#grid-message").hide()},5000)}},complete:function(){c.grid.set({operation:undefined,item_ids:undefined})}})},check_all_items:function(){var c=document.getElementById("check_all"),d=document.getElementsByTagName("input"),f=0,e;if(c.checked===true){for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=true;f++}}}else{for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=false}}}this.init_grid_elements()},go_page_one:function(){var c=this.grid.get("cur_page");if(c!==null&&c!==undefined&&c!=="all"){this.grid.set("cur_page",1)}}});return{Grid:a,GridView:b}});
\ No newline at end of file
+jQuery.ajaxSettings.traditional=true;define(["mvc/ui"],function(){var a=Backbone.Model.extend({defaults:{url_base:"",async:false,async_ops:[],categorical_filters:[],filters:{},sort_key:null,show_item_checkboxes:false,advanced_search:false,cur_page:1,num_pages:1,operation:undefined,item_ids:undefined},can_async_op:function(c){return _.indexOf(this.attributes.async_ops,c)!==-1},add_filter:function(g,h,d){if(d){var e=this.attributes.filters[g],c;if(e===null||e===undefined){c=h}else{if(typeof(e)=="string"){if(e=="All"){c=h}else{var f=[];f[0]=e;f[1]=h;c=f}}else{c=e;c.push(h)}}this.attributes.filters[g]=c}else{this.attributes.filters[g]=h}},remove_filter:function(d,g){var c=this.attributes.filters[d];if(c===null||c===undefined){return false}var f=true;if(typeof(c)==="string"){if(c=="All"){f=false}else{delete this.attributes.filters[d]}}else{var e=_.indexOf(c,g);if(e!==-1){c.splice(e,1)}else{f=false}}return f},get_url_data:function(){var c={async:this.attributes.async,sort:this.attributes.sort_key,page:this.attributes.cur_page,show_item_checkboxes:this.attributes.show_item_checkboxes,advanced_search:this.attributes.advanced_search};if(this.attributes.operation){c.operation=this.attributes.operation}if(this.attributes.item_ids){c.id=this.attributes.item_ids}var d=this;_.each(_.pairs(d.attributes.filters),function(e){c["f-"+e[0]]=e[1]});return c},get_url:function(c){return this.get("url_base")+"?"+$.param(this.get_url_data())+"&"+$.param(c)}});var b=Backbone.View.extend({grid:null,initialize:function(c){this.init_grid(c);this.init_grid_controls();$("input[type=text]").each(function(){$(this).click(function(){$(this).select()}).keyup(function(){$(this).css("font-style","normal")})})},init_grid:function(e){this.grid=new a(e);var d=this.grid.attributes;var c=this.grid.get("url_base");c=c.replace(/^.*\/\/[^\/]+/,"");this.grid.set("url_base",c);$("#grid-table-body").html(this.template_body(d));$("#grid-table-footer").html(this.template_footer(d));if(d.message){$("#grid-message").html(this.template_message(d));setTimeout(function(){$("#grid-message").html("")},5000)}this.init_grid_elements()},init_grid_controls:function(){$(".submit-image").each(function(){$(this).mousedown(function(){$(this).addClass("gray-background")});$(this).mouseup(function(){$(this).removeClass("gray-background")})});var c=this;$(".sort-link").each(function(){$(this).click(function(){c.set_sort_condition($(this).attr("sort_key"));return false})});$(".categorical-filter > a").each(function(){$(this).click(function(){c.set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});$(".text-filter-form").each(function(){$(this).submit(function(){var g=$(this).attr("column_key");var f=$("#input-"+g+"-filter");var h=f.val();f.val("");c.add_filter_condition(g,h);return false})});var d=$("#input-tags-filter");if(d.length){d.autocomplete(this.grid.history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var e=$("#input-name-filter");if(e.length){e.autocomplete(this.grid.history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}$(".advanced-search-toggle").each(function(){$(this).click(function(){$("#standard-search").slideToggle("fast");$("#advanced-search").slideToggle("fast");return false})})},init_grid_elements:function(){$(".grid").each(function(){var r=$(this).find("input.grid-row-select-checkbox");var q=$(this).find("span.grid-selected-count");var s=function(){q.text($(r).filter(":checked").length)};$(r).each(function(){$(this).change(s)});s()});if($(".community_rating_star").length!==0){$(".community_rating_star").rating({})}var p=this.grid.attributes;var o=this;$(".page-link > a").each(function(){$(this).click(function(){o.set_page($(this).attr("page_num"));return false})});$(".use-inbound").each(function(){$(this).click(function(q){o.execute({href:$(this).attr("href"),inbound:true});return false})});$(".use-outbound").each(function(){$(this).click(function(q){o.execute({href:$(this).attr("href")});return false})});for(var h in p.items){var k=$("#grid-"+h+"-popup");k.off();var d=new PopupMenu(k);var n=p.items[h];for(var g in p.operations){var e=p.operations[g];var l=e.label;var c=n.operation_config[l];var f=n.encode_id;if(c.allowed&&e.allow_popup){var m={html:e.label,href:c.url_args,target:c.target,confirmation_text:e.confirm,inbound:e.inbound};m.func=function(s){s.preventDefault();var r=$(s.target).html();var q=this.findItemByHtml(r);o.execute(q)};d.addItem(m)}}}},add_filter_condition:function(e,g){if(g===""){return false}this.grid.add_filter(e,g,true);var f=$("<span>"+g+"<a href='javascript:void(0);'><span class='delete-search-icon' /></span></a>");f.addClass("text-filter-val");var d=this;f.click(function(){d.grid.remove_filter(e,g);$(this).remove();d.go_page_one();d.execute()});var c=$("#"+e+"-filtering-criteria");c.append(f);this.go_page_one();this.execute()},set_sort_condition:function(h){var g=this.grid.get("sort_key");var f=h;if(g.indexOf(h)!==-1){if(g.substring(0,1)!=="-"){f="-"+h}else{}}$(".sort-arrow").remove();var e=(f.substring(0,1)=="-")?"↑":"↓";var c=$("<span>"+e+"</span>").addClass("sort-arrow");var d=$("#"+h+"-header");d.append(c);this.grid.set("sort_key",f);this.go_page_one();this.execute()},set_categorical_filter:function(e,g){var d=this.grid.get("categorical_filters")[e],f=this.grid.get("filters")[e];var c=this;$("."+e+"-filter").each(function(){var m=$.trim($(this).text());var k=d[m];var l=k[e];if(l==g){$(this).empty();$(this).addClass("current-filter");$(this).append(m)}else{if(l==f){$(this).empty();var h=$("<a href='#'>"+m+"</a>");h.click(function(){c.set_categorical_filter(e,l)});$(this).removeClass("current-filter");$(this).append(h)}}});this.grid.add_filter(e,g);this.go_page_one();this.execute()},set_page:function(c){var d=this;$(".page-link").each(function(){var k=$(this).attr("id"),g=parseInt(k.split("-")[2],10),e=d.grid.get("cur_page"),h;if(g===c){h=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(h)}else{if(g===e){h=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var f=$("<a href='#'>"+h+"</a>");f.click(function(){d.set_page(g)});$(this).append(f)}}});if(c==="all"){this.grid.set("cur_page",c)}else{this.grid.set("cur_page",parseInt(c,10))}this.execute()},submit_operation:function(f,g){var e=$('input[name="id"]:checked').length;if(!e>0){return false}var d=$(f).val();var c=[];$("input[name=id]:checked").each(function(){c.push($(this).val())});this.execute({operation:d,id:c,confirmation_text:g});return true},execute:function(n){var f=null;var e=null;var g=null;var c=null;var m=null;if(n){e=n.href;g=n.operation;f=n.id;c=n.confirmation_text;m=n.inbound;if(e!==undefined&&e.indexOf("operation=")!=-1){var l=e.split("?");if(l.length>1){var k=l[1];var d=k.split("&");for(var h=0;h<d.length;h++){if(d[h].indexOf("operation")!=-1){g=d[h].split("=")[1];g=g.replace(/\+/g," ")}else{if(d[h].indexOf("id")!=-1){f=d[h].split("=")[1]}}}}}}if(g&&f){if(c&&c!=""&&c!="None"&&c!="null"){if(!confirm(c)){return false}}g=g.toLowerCase();this.grid.set({operation:g,item_ids:f});if(this.grid.can_async_op(g)){this.update_grid()}else{this.go_to(m,"")}return false}if(e){this.go_to(m,e);return false}if(this.grid.get("async")){this.update_grid()}else{this.go_to(m,"")}return false},go_to:function(f,d){var e=this.grid.get("async");this.grid.set("async",false);advanced_search=$("#advanced-search").is(":visible");this.grid.set("advanced_search",advanced_search);if(!d){d=this.grid.get("url_base")+"?"+$.param(this.grid.get_url_data())}this.grid.set({operation:undefined,item_ids:undefined,async:e});if(f){var c=$(".grid-header").closest(".inbound");if(c.length!==0){c.load(d);return}}window.location=d},update_grid:function(){var d=(this.grid.get("operation")?"POST":"GET");$(".loading-elt-overlay").show();var c=this;$.ajax({type:d,url:c.grid.get("url_base"),data:c.grid.get_url_data(),error:function(e){alert("Grid refresh failed")},success:function(e){c.init_grid($.parseJSON(e));$("#grid-table-body").trigger("update");$(".loading-elt-overlay").hide()},complete:function(){c.grid.set({operation:undefined,item_ids:undefined})}})},check_all_items:function(){var c=document.getElementById("check_all"),d=document.getElementsByTagName("input"),f=0,e;if(c.checked===true){for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=true;f++}}}else{for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=false}}}this.init_grid_elements()},go_page_one:function(){var c=this.grid.get("cur_page");if(c!==null&&c!==undefined&&c!=="all"){this.grid.set("cur_page",1)}},template_body:function(s){var l="";var t=0;var g=s.items.length;if(g==0){l+='<tr><td colspan="100"><em>No Items</em></td></tr>';t=1}for(i in s.items){var q=s.items[i];var c=q.encode_id;var h="grid-"+i+"-popup";l+="<tr ";if(s.current_item_id==q.id){l+='class="current"'}l+=">";if(s.show_item_checkboxes){l+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+c+'" id="'+c+'" class="grid-row-select-checkbox" /></td>'}for(j in s.columns){var f=s.columns[j];if(f.visible){var e="";if(f.nowrap){e='style="white-space:nowrap;"'}var r=q.column_config[f.label];var k=r.link;var m=r.value;var p=r.inbound;if(jQuery.type(m)==="string"){m=m.replace(/\/\//g,"/")}var d="";var o="";if(f.attach_popup){d="grid-"+i+"-popup";o="menubutton";if(k!=""){o+=" split"}o+=" popup"}l+="<td "+e+">";if(k){if(s.operations.length!=0){l+='<div id="'+d+'" class="'+o+'" style="float: left;">'}var n="";if(p){n="use-inbound"}else{n="use-outbound"}l+='<a class="label '+n+'" href="'+k+'" onclick="return false;">'+m+"</a>";if(s.operations.length!=0){l+="</div>"}}else{l+='<div id="'+d+'" class="'+o+'"><label id="'+f.label_id_prefix+c+'" for="'+c+'">'+m+"</label></div>"}l+="</td>"}}l+="</tr>";t++}return l},template_footer:function(q){var m="";if(q.use_paging&&q.num_pages>1){var o=q.num_page_links;var c=q.cur_page_num;var p=q.num_pages;var l=o/2;var k=c-l;var g=0;if(k==0){k=1;g=l-(c-k)}var f=l+g;var e=c+f;if(e<=p){max_offset=0}else{e=p;max_offset=f-(e+1-c)}if(max_offset!=0){k-=max_offset;if(k<1){k=1}}m+='<tr id="page-links-row">';if(q.show_item_checkboxes){m+="<td></td>"}m+='<td colspan="100"><span id="page-link-container">Page:';if(k>1){m+='<span class="page-link" id="page-link-1"><a href="'+this.grid.get_url({page:n})+'" page_num="1" onclick="return false;">1</a></span> ...'}for(var n=k;n<e+1;n++){if(n==q.cur_page_num){m+='<span class="page-link inactive-link" id="page-link-'+n+'">'+n+"</span>"}else{m+='<span class="page-link" id="page-link-'+n+'"><a href="'+this.grid.get_url({page:n})+'" onclick="return false;" page_num="'+n+'">'+n+"</a></span>"}}if(e<p){m+='...<span class="page-link" id="page-link-'+p+'"><a href="'+this.grid.get_url({page:p})+'" onclick="return false;" page_num="'+p+'">'+p+"</a></span>"}m+="</span>";m+='<span class="page-link" id="show-all-link-span"> | <a href="'+this.grid.get_url({page:"all"})+'" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(q.show_item_checkboxes){m+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+q.get_class_plural+": ";for(i in q.operations){var d=q.operations[i];if(d.allow_multiple){m+='<input type="button" value="'+d.label+'" class="action-button" onclick="gridView.submit_operation(this, \''+d.confirm+"')\"> "}}m+="</td></tr>"}var h=false;for(i in q.operations){if(q.operations[i].global_operation){h=true;break}}if(h){m+='<tr><td colspan="100">';for(i in q.operations){var d=q.operations[i];if(d.global_operation){m+='<a class="action-button" href="'+d.global_operation+'">'+d.label+"</a>"}}m+="</td></tr>"}if(q.legend){m+='<tr><td colspan="100">'+q.legend+"</td></tr>"}return m},template_message:function(c){return'<p><div class="'+c.status+'message transient-message">'+c.message+'</div><div style="clear: both"></div></p>'}});return{Grid:a,GridView:b}});
\ No newline at end of file
diff -r a3e2c3464298ebea3fd2ab93a43aac479ba9665a -r 9fa754a203b7312d58211b5e1e7140bf36aec76a static/scripts/packed/galaxy.workflow_editor.canvas.js
--- a/static/scripts/packed/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/packed/galaxy.workflow_editor.canvas.js
@@ -1,1 +1,1 @@
-function Terminal(a){this.element=a;this.connectors=[]}$.extend(Terminal.prototype,{connect:function(a){this.connectors.push(a);if(this.node){this.node.changed()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.changed()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});function OutputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}OutputTerminal.prototype=new Terminal();function InputTerminal(b,c,a){Terminal.call(this,b);this.datatypes=c;this.multiple=a}InputTerminal.prototype=new Terminal();$.extend(InputTerminal.prototype,{can_accept:function(a){if(this.connectors.length<1||this.multiple){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){if(f[b]=="input"||issubtype(f[b],this.datatypes[c])){return true}}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},redraw:function(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};if(!this.handle1||!this.handle2){return}var h=n(this.handle1.element)+5;var g=i(this.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*f;this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canvas.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});function Node(a){this.element=a;this.input_terminals={};this.output_terminals={};this.tool_errors={}}$.extend(Node.prototype,{enable_input_terminal:function(f,b,c,a){var d=this;$(f).each(function(){var g=this.terminal=new InputTerminal(this,c,a);g.node=d;g.name=b;$(this).bind("dropinit",function(h,i){return $(i.drag).hasClass("output-terminal")&&g.can_accept(i.drag.terminal)}).bind("dropstart",function(h,i){if(i.proxy.terminal){i.proxy.terminal.connectors[0].inner_color="#BBFFBB"}}).bind("dropend",function(h,i){if(i.proxy.terminal){i.proxy.terminal.connectors[0].inner_color="#FFFFFF"}}).bind("drop",function(h,i){(new Connector(i.drag.terminal,g)).redraw()}).bind("hover",function(){if(g.connectors.length>0){var h=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='button'></div>").append($("<div/>").addClass("fa-icon-button fa fa-times").click(function(){$.each(g.connectors,function(j,i){if(i){i.destroy()}});h.remove()}))).bind("mouseleave",function(){$(this).remove()});h.css({top:$(this).offset().top-2,left:$(this).offset().left-h.width(),"padding-right":$(this).width()}).show()}});d.input_terminals[b]=g})},enable_output_terminal:function(d,a,b){var c=this;$(d).each(function(){var g=this;var f=this.terminal=new OutputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dragstart",function(j,k){$(k.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var i=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);i.terminal=new OutputTerminal(i);var l=new Connector();l.dragging=true;l.connect(this.terminal,i.terminal);return i}).bind("drag",function(i,j){var h=function(){var l=$(j.proxy).offsetParent().offset(),k=j.offsetX-l.left,m=j.offsetY-l.top;$(j.proxy).css({left:k,top:m});j.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};h();$("#canvas-container").get(0).scroll_panel.test(i,h)}).bind("dragend",function(h,i){i.proxy.terminal.connectors[0].destroy();$(i.proxy).remove();$(i.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()});c.output_terminals[a]=f})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(h){var g=this.element;if(h.type){this.type=h.type}this.name=h.name;this.form_html=h.form_html;this.tool_state=h.tool_state;this.tool_errors=h.tool_errors;this.tooltip=h.tooltip?h.tooltip:"";this.annotation=h.annotation;this.post_job_actions=h.post_job_actions?h.post_job_actions:{};this.workflow_outputs=h.workflow_outputs?h.workflow_outputs:[];if(this.tool_errors){g.addClass("tool-node-error")}else{g.removeClass("tool-node-error")}var d=this;var c=Math.max(150,g.width());var a=g.find(".toolFormBody");a.find("div").remove();var i=$("<div class='inputs'></div>").appendTo(a);$.each(h.data_inputs,function(k,f){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,f.name,f.extensions,f.multiple);var b=$("<div class='form-row dataRow input-data-row' name='"+f.name+"'>"+f.label+"</div>");b.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(b);c=Math.max(c,b.outerWidth());b.css({position:"",left:"",top:"",display:""});b.remove();i.append(b.prepend(j))});if((h.data_inputs.length>0)&&(h.data_outputs.length>0)){a.append($("<div class='rule'></div>"))}$.each(h.data_outputs,function(k,b){var j=$("<div class='terminal output-terminal'></div>");d.enable_output_terminal(j,b.name,b.extensions);var f=b.name;if(b.extensions.indexOf("input")<0){f=f+" ("+b.extensions.join(", ")+")"}var m=$("<div class='form-row dataRow'>"+f+"</div>");if(d.type=="tool"){var l=$("<div class='callout "+f+"'></div>").css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(b.name,d.workflow_outputs)!=-1){d.workflow_outputs.splice($.inArray(b.name,d.workflow_outputs),1);l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{d.workflow_outputs.push(b.name);l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Flag this as a workflow output. All non-flagged outputs will be hidden."});l.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});l.show();m.append(l);if($.inArray(b.name,d.workflow_outputs)===-1){l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}m.hover(function(){l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")},function(){if($.inArray(b.name,d.workflow_outputs)===-1){l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}})}m.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(m);c=Math.max(c,m.outerWidth()+17);m.css({position:"",left:"",top:"",display:""});m.detach();a.append(m.append(j))});g.css("width",Math.min(250,Math.max(g.width(),c)));workflow.node_changed(this)},update_field_data:function(f){var c=$(this.element),d=this;this.tool_state=f.tool_state;this.form_html=f.form_html;this.tool_errors=f.tool_errors;this.annotation=f.annotation;var g=$.parseJSON(f.post_job_actions);this.post_job_actions=g?g:{};if(this.tool_errors){c.addClass("tool-node-error")}else{c.removeClass("tool-node-error")}var h=c.find("div.inputs");var b=$("<div class='inputs'></div>");var a=h.find("div.input-data-row");$.each(f.data_inputs,function(l,j){var k=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(k,j.name,j.extensions,j.multiple);h.find("div[name='"+j.name+"']").each(function(){$(this).find(".input-terminal").each(function(){var i=this.terminal.connectors[0];if(i){k[0].terminal.connectors[0]=i;i.handle2=k[0].terminal}});$(this).remove()});b.append($("<div class='form-row dataRow input-data-row' name='"+j.name+"'>"+j.label+"</div>").prepend(k))});h.replaceWith(b);h.find("div.input-data-row > .terminal").each(function(){this.terminal.destroy()});this.changed();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},changed:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node("tool",f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if($.isArray(h)){$.each(h,function(m,k){var n=wf.nodes[k.id];var o=new Connector();o.connect(n.output_terminals[k.output_name],d.input_terminals[i]);o.redraw()})}else{var j=wf.nodes[h.id];var l=new Connector();l.connect(j.output_terminals[h.output_name],d.input_terminals[i]);l.redraw()}}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node(i);g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+galaxy_config.root+"static/images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<div>").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.tool_errors){i.fillStyle="#FFCCCC";i.strokeStyle="#AA6666"}else{if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
\ No newline at end of file
+function Terminal(a){this.element=a;this.connectors=[]}$.extend(Terminal.prototype,{connect:function(a){this.connectors.push(a);if(this.node){this.node.changed()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.changed()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});function OutputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}OutputTerminal.prototype=new Terminal();function InputTerminal(b,c,a){Terminal.call(this,b);this.datatypes=c;this.multiple=a}InputTerminal.prototype=new Terminal();$.extend(InputTerminal.prototype,{can_accept:function(a){if(this.connectors.length<1||this.multiple){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){if(f[b]=="input"||issubtype(f[b],this.datatypes[c])){return true}}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},redraw:function(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};if(!this.handle1||!this.handle2){return}var h=n(this.handle1.element)+5;var g=i(this.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*f;this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canvas.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});function Node(a){this.element=a;this.input_terminals={};this.output_terminals={};this.tool_errors={}}$.extend(Node.prototype,{enable_input_terminal:function(f,b,c,a){var d=this;$(f).each(function(){var g=this.terminal=new InputTerminal(this,c,a);g.node=d;g.name=b;$(this).bind("dropinit",function(h,i){return $(i.drag).hasClass("output-terminal")&&g.can_accept(i.drag.terminal)}).bind("dropstart",function(h,i){if(i.proxy.terminal){i.proxy.terminal.connectors[0].inner_color="#BBFFBB"}}).bind("dropend",function(h,i){if(i.proxy.terminal){i.proxy.terminal.connectors[0].inner_color="#FFFFFF"}}).bind("drop",function(h,i){(new Connector(i.drag.terminal,g)).redraw()}).bind("hover",function(){if(g.connectors.length>0){var h=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='button'></div>").append($("<div/>").addClass("fa-icon-button fa fa-times").click(function(){$.each(g.connectors,function(j,i){if(i){i.destroy()}});h.remove()}))).bind("mouseleave",function(){$(this).remove()});h.css({top:$(this).offset().top-2,left:$(this).offset().left-h.width(),"padding-right":$(this).width()}).show()}});d.input_terminals[b]=g})},enable_output_terminal:function(d,a,b){var c=this;$(d).each(function(){var g=this;var f=this.terminal=new OutputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dragstart",function(j,k){$(k.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var i=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);i.terminal=new OutputTerminal(i);var l=new Connector();l.dragging=true;l.connect(this.terminal,i.terminal);return i}).bind("drag",function(i,j){var h=function(){var l=$(j.proxy).offsetParent().offset(),k=j.offsetX-l.left,m=j.offsetY-l.top;$(j.proxy).css({left:k,top:m});j.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};h();$("#canvas-container").get(0).scroll_panel.test(i,h)}).bind("dragend",function(h,i){i.proxy.terminal.connectors[0].destroy();$(i.proxy).remove();$(i.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()});c.output_terminals[a]=f})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(h){var g=this.element;if(h.type){this.type=h.type}this.name=h.name;this.form_html=h.form_html;this.tool_state=h.tool_state;this.tool_errors=h.tool_errors;this.tooltip=h.tooltip?h.tooltip:"";this.annotation=h.annotation;this.post_job_actions=h.post_job_actions?h.post_job_actions:{};this.workflow_outputs=h.workflow_outputs?h.workflow_outputs:[];if(this.tool_errors){g.addClass("tool-node-error")}else{g.removeClass("tool-node-error")}var d=this;var c=Math.max(150,g.width());var a=g.find(".toolFormBody");a.find("div").remove();var i=$("<div class='inputs'></div>").appendTo(a);$.each(h.data_inputs,function(k,f){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,f.name,f.extensions,f.multiple);var b=$("<div class='form-row dataRow input-data-row' name='"+f.name+"'>"+f.label+"</div>");b.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(b);c=Math.max(c,b.outerWidth());b.css({position:"",left:"",top:"",display:""});b.remove();i.append(b.prepend(j))});if((h.data_inputs.length>0)&&(h.data_outputs.length>0)){a.append($("<div class='rule'></div>"))}$.each(h.data_outputs,function(k,b){var j=$("<div class='terminal output-terminal'></div>");d.enable_output_terminal(j,b.name,b.extensions);var f=b.name;if(b.extensions.indexOf("input")<0){f=f+" ("+b.extensions.join(", ")+")"}var m=$("<div class='form-row dataRow'>"+f+"</div>");if(d.type=="tool"){var l=$("<div class='callout "+f+"'></div>").css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(b.name,d.workflow_outputs)!=-1){d.workflow_outputs.splice($.inArray(b.name,d.workflow_outputs),1);l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{d.workflow_outputs.push(b.name);l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Mark dataset as a workflow output. All unmarked datasets will be hidden."});l.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});l.show();m.append(l);if($.inArray(b.name,d.workflow_outputs)===-1){l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}m.hover(function(){l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")},function(){if($.inArray(b.name,d.workflow_outputs)===-1){l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}})}m.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(m);c=Math.max(c,m.outerWidth()+17);m.css({position:"",left:"",top:"",display:""});m.detach();a.append(m.append(j))});g.css("width",Math.min(250,Math.max(g.width(),c)));workflow.node_changed(this)},update_field_data:function(f){var c=$(this.element),d=this;this.tool_state=f.tool_state;this.form_html=f.form_html;this.tool_errors=f.tool_errors;this.annotation=f.annotation;var g=$.parseJSON(f.post_job_actions);this.post_job_actions=g?g:{};if(this.tool_errors){c.addClass("tool-node-error")}else{c.removeClass("tool-node-error")}var h=c.find("div.inputs");var b=$("<div class='inputs'></div>");var a=h.find("div.input-data-row");$.each(f.data_inputs,function(l,j){var k=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(k,j.name,j.extensions,j.multiple);h.find("div[name='"+j.name+"']").each(function(){$(this).find(".input-terminal").each(function(){var i=this.terminal.connectors[0];if(i){k[0].terminal.connectors[0]=i;i.handle2=k[0].terminal}});$(this).remove()});b.append($("<div class='form-row dataRow input-data-row' name='"+j.name+"'>"+j.label+"</div>").prepend(k))});h.replaceWith(b);h.find("div.input-data-row > .terminal").each(function(){this.terminal.destroy()});this.changed();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},changed:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node("tool",f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if($.isArray(h)){$.each(h,function(m,k){var n=wf.nodes[k.id];var o=new Connector();o.connect(n.output_terminals[k.output_name],d.input_terminals[i]);o.redraw()})}else{var j=wf.nodes[h.id];var l=new Connector();l.connect(j.output_terminals[h.output_name],d.input_terminals[i]);l.redraw()}}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node(i);g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+galaxy_config.root+"static/images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<div>").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.tool_errors){i.fillStyle="#FFCCCC";i.strokeStyle="#AA6666"}else{if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
\ No newline at end of file
diff -r a3e2c3464298ebea3fd2ab93a43aac479ba9665a -r 9fa754a203b7312d58211b5e1e7140bf36aec76a static/scripts/packed/mvc/base-mvc.js
--- a/static/scripts/packed/mvc/base-mvc.js
+++ b/static/scripts/packed/mvc/base-mvc.js
@@ -1,1 +1,1 @@
-var BaseModel=Backbone.Model.extend({defaults:{name:null,hidden:false},show:function(){this.set("hidden",false)},hide:function(){this.set("hidden",true)},is_visible:function(){return !this.attributes.hidden}});var BaseView=Backbone.View.extend({initialize:function(){this.model.on("change:hidden",this.update_visible,this);this.update_visible()},update_visible:function(){if(this.model.attributes.hidden){this.$el.hide()}else{this.$el.show()}}});var LoggableMixin={logger:null,log:function(){if(this.logger){var a=this.logger.log;if(typeof this.logger.log==="object"){a=Function.prototype.bind.call(this.logger.log,this.logger)}return a.apply(this.logger,arguments)}return undefined}};var SessionStorageModel=Backbone.Model.extend({initialize:function(b){b.id=(!_.isString(b.id))?(_.uniqueId()):(b.id);this.id=b.id;var a=(!this.isNew())?(this._read(this)):({});this.clear({silent:true});this.save(_.extend({},this.defaults,a,b),{silent:true});this.on("change",function(){this.save()})},sync:function(d,b,a){if(!a.silent){b.trigger("request",b,{},a)}var c;switch(d){case"create":c=this._create(b);break;case"read":c=this._read(b);break;case"update":c=this._update(b);break;case"delete":c=this._delete(b);break}if(c!==undefined||c!==null){if(a.success){a.success()}}else{if(a.error){a.error()}}return c},_create:function(a){var b=a.toJSON(),c=sessionStorage.setItem(a.id,JSON.stringify(b));return(c===null)?(c):(b)},_read:function(a){return JSON.parse(sessionStorage.getItem(a.id))},_update:function(a){return a._create(a)},_delete:function(a){return sessionStorage.removeItem(a.id)},isNew:function(){return !sessionStorage.hasOwnProperty(this.id)},_log:function(){return JSON.stringify(this.toJSON(),null," ")},toString:function(){return"SessionStorageModel("+this.id+")"}});(function(){SessionStorageModel.prototype=_.omit(SessionStorageModel.prototype,"url","urlRoot")}());var HiddenUntilActivatedViewMixin={hiddenUntilActivated:function(a,c){c=c||{};this.HUAVOptions={$elementShown:this.$el,showFn:jQuery.prototype.toggle,showSpeed:"fast"};_.extend(this.HUAVOptions,c||{});this.HUAVOptions.hasBeenShown=this.HUAVOptions.$elementShown.is(":visible");this.hidden=this.isHidden();if(a){var b=this;a.on("click",function(d){b.toggle(b.HUAVOptions.showSpeed)})}},isHidden:function(){return(this.HUAVOptions.$elementShown.is(":hidden"))},toggle:function(){if(this.isHidden()){if(!this.HUAVOptions.hasBeenShown){if(_.isFunction(this.HUAVOptions.onshowFirstTime)){this.HUAVOptions.hasBeenShown=true;this.HUAVOptions.onshowFirstTime.call(this)}}if(_.isFunction(this.HUAVOptions.onshow)){this.HUAVOptions.onshow.call(this)}this.hidden=false}else{if(_.isFunction(this.HUAVOptions.onhide)){this.HUAVOptions.onhide.call(this)}this.hidden=true}this.trigger("hiddenUntilActivated:"+((this.isHidden())?("shown"):("hidden")),this);return this.HUAVOptions.showFn.apply(this.HUAVOptions.$elementShown,arguments)}};
\ No newline at end of file
+var BaseView=Backbone.View.extend({initialize:function(){this.model.on("change:hidden",this.update_visible,this);this.update_visible()},update_visible:function(){if(this.model.attributes.hidden){this.$el.hide()}else{this.$el.show()}}});var LoggableMixin={logger:null,log:function(){if(this.logger){var a=this.logger.log;if(typeof this.logger.log==="object"){a=Function.prototype.bind.call(this.logger.log,this.logger)}return a.apply(this.logger,arguments)}return undefined}};var SessionStorageModel=Backbone.Model.extend({initialize:function(b){b.id=(!_.isString(b.id))?(_.uniqueId()):(b.id);this.id=b.id;var a=(!this.isNew())?(this._read(this)):({});this.clear({silent:true});this.save(_.extend({},this.defaults,a,b),{silent:true});this.on("change",function(){this.save()})},sync:function(d,b,a){if(!a.silent){b.trigger("request",b,{},a)}var c;switch(d){case"create":c=this._create(b);break;case"read":c=this._read(b);break;case"update":c=this._update(b);break;case"delete":c=this._delete(b);break}if(c!==undefined||c!==null){if(a.success){a.success()}}else{if(a.error){a.error()}}return c},_create:function(a){var b=a.toJSON(),c=sessionStorage.setItem(a.id,JSON.stringify(b));return(c===null)?(c):(b)},_read:function(a){return JSON.parse(sessionStorage.getItem(a.id))},_update:function(a){return a._create(a)},_delete:function(a){return sessionStorage.removeItem(a.id)},isNew:function(){return !sessionStorage.hasOwnProperty(this.id)},_log:function(){return JSON.stringify(this.toJSON(),null," ")},toString:function(){return"SessionStorageModel("+this.id+")"}});(function(){SessionStorageModel.prototype=_.omit(SessionStorageModel.prototype,"url","urlRoot")}());var HiddenUntilActivatedViewMixin={hiddenUntilActivated:function(a,c){c=c||{};this.HUAVOptions={$elementShown:this.$el,showFn:jQuery.prototype.toggle,showSpeed:"fast"};_.extend(this.HUAVOptions,c||{});this.HUAVOptions.hasBeenShown=this.HUAVOptions.$elementShown.is(":visible");this.hidden=this.isHidden();if(a){var b=this;a.on("click",function(d){b.toggle(b.HUAVOptions.showSpeed)})}},isHidden:function(){return(this.HUAVOptions.$elementShown.is(":hidden"))},toggle:function(){if(this.hidden){if(!this.HUAVOptions.hasBeenShown){if(_.isFunction(this.HUAVOptions.onshowFirstTime)){this.HUAVOptions.hasBeenShown=true;this.HUAVOptions.onshowFirstTime.call(this)}}if(_.isFunction(this.HUAVOptions.onshow)){this.HUAVOptions.onshow.call(this);this.trigger("hiddenUntilActivated:shown",this)}this.hidden=false}else{if(_.isFunction(this.HUAVOptions.onhide)){this.HUAVOptions.onhide.call(this);this.trigger("hiddenUntilActivated:hidden",this)}this.hidden=true}return this.HUAVOptions.showFn.apply(this.HUAVOptions.$elementShown,arguments)}};
\ No newline at end of file
diff -r a3e2c3464298ebea3fd2ab93a43aac479ba9665a -r 9fa754a203b7312d58211b5e1e7140bf36aec76a static/scripts/packed/mvc/history/history-panel.js
--- a/static/scripts/packed/mvc/history/history-panel.js
+++ b/static/scripts/packed/mvc/history/history-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/history/history-model","mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/dataset/hda-edit"],function(c,f,a,i){var g=SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});g.storageKey=function h(){return("history-panel")};var d=SessionStorageModel.extend({defaults:{expandedHdas:{},show_deleted:false,show_hidden:false},addExpandedHda:function(j){this.save("expandedHdas",_.extend(this.get("expandedHdas"),_.object([j],[true])))},removeExpandedHda:function(j){this.save("expandedHdas",_.omit(this.get("expandedHdas"),j))},toString:function(){return"HistoryPrefs("+this.id+")"}});d.historyStorageKey=function b(j){if(!j){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+j)}return("history:"+j)};var e=Backbone.View.extend(LoggableMixin).extend({defaultHDAViewClass:i.HDAEditView,tagName:"div",className:"history-panel",fxSpeed:"fast",datasetsSelector:".datasets-list",emptyMsgSelector:".empty-history-message",msgsSelector:".message-container",initialize:function(j){j=j||{};if(j.logger){this.logger=j.logger}this.log(this+".initialize:",j);this.HDAViewClass=j.HDAViewClass||this.defaultHDAViewClass;this.linkTarget=j.linkTarget||"_blank";this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);this.preferences=new g(_.extend({id:g.storageKey()},_.pick(j,_.keys(g.prototype.defaults))));this.filters=[];this.selecting=j.selecting||false;this.annotationEditorShown=j.annotationEditorShown||false;this._setUpListeners();if(this.model){this._setUpWebStorage(j.initiallyExpanded,j.show_deleted,j.show_hidden);this._setUpModelEventHandlers()}if(j.onready){j.onready.call(this)}},_setUpListeners:function(){this.on("error",function(k,n,j,m,l){this.errorHandler(k,n,j,m,l)});this.on("loading-history",function(){this.showLoadingIndicator("loading history...")});this.on("loading-done",function(){this.hideLoadingIndicator()});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});this.on("switched-history current-history new-history",function(){if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});if(this.logger){this.on("all",function(j){this.log(this+"",arguments)},this)}},errorHandler:function(l,o,k,n,m){var j=this._parseErrorMessage(l,o,k,n,m);if(o&&o.status===0&&o.readyState===0){}else{if(o&&o.status===502){}else{if(!this.$el.find(this.msgsSelector).is(":visible")){this.once("rendered",function(){this.displayMessage("error",j.message,j.details)})}else{this.displayMessage("error",j.message,j.details)}}}},_parseErrorMessage:function(m,q,l,p,o){var k=Galaxy.currUser,j={message:this._bePolite(p),details:{user:(k instanceof User)?(k.toJSON()):(k+""),source:(m instanceof Backbone.Model)?(m.toJSON()):(m+""),xhr:q,options:(q)?(_.omit(l,"xhr")):(l)}};_.extend(j.details,o||{});if(q&&_.isFunction(q.getAllResponseHeaders)){var n=q.getAllResponseHeaders();n=_.compact(n.split("\n"));n=_.map(n,function(r){return r.split(": ")});j.details.xhr.responseHeaders=_.object(n)}return j},_bePolite:function(j){j=j||_l("An error occurred while getting updates from the server");return j+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadCurrentHistory:function(k){var j=this;return this.loadHistoryWithHDADetails("current",k).then(function(m,l){j.trigger("current-history",j)})},switchToHistory:function(m,l){var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories/"+m+"/set_as_current")};return this.loadHistoryWithHDADetails(m,l,k).then(function(o,n){j.trigger("switched-history",j)})},createNewHistory:function(l){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,l,k).then(function(n,m){j.trigger("new-history",j)})},loadHistoryWithHDADetails:function(m,l,k,o){var j=this,n=function(p){return j.getExpandedHdaIds(p.id)};return this.loadHistory(m,l,k,o,n)},loadHistory:function(m,l,k,p,n){this.trigger("loading-history",this);l=l||{};var j=this;var o=c.History.getHistoryData(m,{historyFn:k,hdaFn:p,hdaDetailIds:l.initiallyExpanded||n});return this._loadHistoryFromXHR(o,l).fail(function(s,q,r){j.trigger("error",j,s,l,_l("An error was encountered while "+q),{historyId:m,history:r||{}})}).always(function(){j.trigger("loading-done",j)})},_loadHistoryFromXHR:function(l,k){var j=this;l.then(function(m,n){j.setModel(m,n,k)});l.fail(function(n,m){j.render()});return l},setModel:function(l,j,k){k=k||{};if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.hdaViews={};if(Galaxy&&Galaxy.currUser){l.user=Galaxy.currUser.toJSON()}this.model=new c.History(l,j,k);this._setUpWebStorage(k.initiallyExpanded,k.show_deleted,k.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this);this.render();return this},_setUpWebStorage:function(k,j,l){this.storage=new d({id:d.historyStorageKey(this.model.get("id"))});if(_.isObject(k)){this.storage.set("exandedHdas",k)}if(_.isBoolean(j)){this.storage.set("show_deleted",j)}if(_.isBoolean(l)){this.storage.set("show_hidden",l)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get())},clearWebStorage:function(){for(var j in sessionStorage){if(j.indexOf("history:")===0){sessionStorage.removeItem(j)}}},getStoredOptions:function(k){if(!k||k==="current"){return(this.storage)?(this.storage.get()):({})}var j=sessionStorage.getItem(d.historyStorageKey(k));return(j===null)?({}):(JSON.parse(j))},getExpandedHdaIds:function(j){var k=this.getStoredOptions(j).expandedHdas;return((_.isEmpty(k))?([]):(_.keys(k)))},_setUpModelEventHandlers:function(){this.model.on("error error:hdas",function(k,m,j,l){this.errorHandler(k,m,j,l)},this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("add",this.addHdaView,this);this.model.hdas.on("change:deleted",this.handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this.handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(j){this.model.fetch()},this);this.model.hdas.on("state:ready",function(k,l,j){if((!k.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[k.id])}},this)},render:function(l,m){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k;if(this.model){k=this.renderModel()}else{k=this.renderWithoutModel()}$(j).queue("fx",[function(n){if(l&&j.$el.is(":visible")){j.$el.fadeOut(l,n)}else{n()}},function(n){j.$el.empty();if(k){j.$el.append(k.children());j.renderBasedOnPrefs()}n()},function(n){if(l&&!j.$el.is(":visible")){j.$el.fadeIn(l,n)}else{n()}},function(n){if(m){m.call(this)}j.trigger("rendered",this);n()}]);return this},renderWithoutModel:function(){var j=$("<div/>"),k=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return j.append(k)},renderModel:function(){var j=$("<div/>");if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){j.append(e.templates.anonHistoryPanel(this.model.toJSON()))}else{j.append(e.templates.historyPanel(this.model.toJSON()));if(Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(j);this._renderAnnotation(j)}}j.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(j);this.renderHdas(j);return j},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.showSearchControls(0)}},_renderTags:function(j){var k=this;this.tagsEditor=new TagsEditor({model:this.model,el:j.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("tagsEditorShown",true);k.toggleHDATagEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("tagsEditorShown",false);k.toggleHDATagEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}},_renderAnnotation:function(j){var k=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:j.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("annotationEditorShown",true);k.toggleHDAAnnotationEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("annotationEditorShown",false);k.toggleHDAAnnotationEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history Annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}},_renderSearchButton:function(j){return faIconButton({title:_l("Search datasets"),classes:"history-search-btn",faIcon:"fa-search"})},_renderSelectButton:function(j){return faIconButton({title:_l("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(j){j=j||this.$el;j.find("[title]").tooltip({placement:"bottom"});if((!this.model)||(!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var k=this;j.find(".history-name").attr("title",_l("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(l){j.find(".history-name").text(l);k.model.save({name:l}).fail(function(){j.find(".history-name").text(k.model.previous("name"))})}});this._setUpDatasetActionsPopup(j)},_setUpDatasetActionsPopup:function(j){var k=this;(new PopupMenu(j.find(".history-dataset-action-popup-btn"),[{html:_l("Hide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.hide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Unhide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.unhide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Delete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype["delete"];k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Undelete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.undelete;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Permanently delete datasets"),func:function(){if(confirm(_l("This will permanently remove the data in your datasets. Are you sure?"))){var l=f.HistoryDatasetAssociation.prototype.purge;k.getSelectedHdaCollection().ajaxQueue(l)}}}]))},refreshHdas:function(k,j){if(this.model){return this.model.refresh(k,j)}return $.when()},addHdaView:function(m){this.log("add."+this,m);var k=this;if(!m.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return}$({}).queue([function l(o){var n=k.$el.find(k.emptyMsgSelector);if(n.is(":visible")){n.fadeOut(k.fxSpeed,o)}else{o()}},function j(o){k.scrollToTop();var n=k.$el.find(k.datasetsSelector);k.createHdaView(m).$el.hide().prependTo(n).slideDown(k.fxSpeed)}])},createHdaView:function(l){var k=l.get("id"),j=this.storage.get("expandedHdas")[k],m=new this.HDAViewClass({model:l,linkTarget:this.linkTarget,expanded:j,tagsEditorShown:this.preferences.get("tagsEditorShown"),annotationEditorShown:this.preferences.get("annotationEditorShown"),selectable:this.selecting,hasUser:this.model.ownedByCurrUser(),logger:this.logger});this._setUpHdaListeners(m);this.hdaViews[k]=m;return m.render()},_setUpHdaListeners:function(k){var j=this;k.on("body-expanded",function(l){j.storage.addExpandedHda(l)});k.on("body-collapsed",function(l){j.storage.removeExpandedHda(l)});k.on("error",function(m,o,l,n){j.errorHandler(m,o,l,n)})},handleHdaDeletionChange:function(j){if(j.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[j.id])}},handleHdaVisibleChange:function(j){if(j.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[j.id])}},removeHdaView:function(k){if(!k){return}var j=this;k.$el.fadeOut(j.fxSpeed,function(){k.off();k.remove();delete j.hdaViews[k.model.id];if(_.isEmpty(j.hdaViews)){j.$el.find(j.emptyMsgSelector).fadeIn(j.fxSpeed,function(){j.trigger("empty-history",j)})}})},renderHdas:function(l){l=l||this.$el;this.hdaViews={};var k=this,j=l.find(this.datasetsSelector),m=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);j.empty();if(m.length){m.each(function(n){j.prepend(k.createHdaView(n).$el)});l.find(this.emptyMsgSelector).hide()}else{l.find(this.emptyMsgSelector).show()}return this.hdaViews},toggleHDATagEditors:function(){var j=arguments;_.each(this.hdaViews,function(k){if(k.tagsEditor){k.tagsEditor.toggle.apply(k.tagsEditor,j)}})},toggleHDAAnnotationEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.annotationEditor){l.annotationEditor.toggle.apply(l.annotationEditor,k)}})},events:{"click .message-container":"clearMessages","click .history-search-btn":"toggleSearchControls","click .history-select-btn":function(j){this.toggleSelectors(this.fxSpeed)},"click .history-select-all-datasets-btn":"selectAllDatasets"},updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(j){j.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},toggleShowDeleted:function(){this.storage.set("show_deleted",!this.storage.get("show_deleted"));this.renderHdas();return this.storage.get("show_deleted")},toggleShowHidden:function(){this.storage.set("show_hidden",!this.storage.get("show_hidden"));this.renderHdas();return this.storage.get("show_hidden")},renderSearchControls:function(k){var l=this;function n(o){l.searchFor=o;l.filters=[function(p){return p.matchesAll(l.searchFor)}];l.trigger("search:searching",o,l);l.renderHdas()}function j(o){if(l.model.hdas.haveDetails()){n(o);return}l.$el.find(".history-search-controls").searchInput("toggle-loading");l.model.hdas.fetchAllDetails({silent:true}).always(function(){l.$el.find(".history-search-controls").searchInput("toggle-loading")}).done(function(){n(o)})}function m(){l.searchFor="";l.filters=[];l.trigger("search:clear",l);l.renderHdas()}return k.searchInput({initialVal:l.searchFor,name:"history-search",placeholder:"search datasets",classes:"history-search",onfirstsearch:j,onsearch:n,onclear:m})},showSearchControls:function(l){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k=this.$el.find(".history-search-controls");if(!k.children().size()){k=this.renderSearchControls(k).hide()}k.show(l,function(){$(this).find("input").focus();j.preferences.set("searching",true)})},hideSearchControls:function(){speed=(speed===undefined)?(this.fxSpeed):(speed);var j=this;this.$el.find(".history-search-controls").hide(speed,function(){j.preferences.set("searching",false)})},toggleSearchControls:function(j){speed=(jQuery.type(j)==="number")?(j):(this.fxSpeed);if(this.$el.find(".history-search-controls").is(":visible")){this.hideSearchControls(speed)}else{this.showSearchControls(speed)}},showSelectors:function(j){this.selecting=true;this.$el.find(".history-dataset-actions").slideDown(j);_.each(this.hdaViews,function(k){k.showSelector(j)})},hideSelectors:function(j){this.selecting=false;this.$el.find(".history-dataset-actions").slideUp(j);_.each(this.hdaViews,function(k){k.hideSelector(j)})},toggleSelectors:function(j){if(!this.selecting){this.showSelectors(j)}else{this.hideSelectors(j)}},selectAllDatasets:function(k){var j=this.$el.find(".history-select-all-datasets-btn");currMode=j.data("mode");if(currMode==="select"){_.each(this.hdaViews,function(l){l.select(k)});j.data("mode","deselect");j.text(_l("De-select all"))}else{if(currMode==="deselect"){_.each(this.hdaViews,function(l){l.deselect(k)});j.data("mode","select");j.text(_l("Select all"))}}},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(j){return j.selected})},getSelectedHdaCollection:function(){return new f.HDACollection(_.map(this.getSelectedHdaViews(),function(j){return j.model}),{historyId:this.model.id})},showLoadingIndicator:function(k,j,l){j=(j!==undefined)?(j):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,l)}else{this.$el.fadeOut(j);this.indicator.show(k,j,l)}},hideLoadingIndicator:function(j,k){j=(j!==undefined)?(j):(this.fxSpeed);if(this.indicator){this.indicator.hide(j,k)}},displayMessage:function(o,p,n){var l=this;this.scrollToTop();var m=this.$el.find(this.msgsSelector),j=$("<div/>").addClass(o+"message").html(p);if(!_.isEmpty(n)){var k=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(l.messageToModalOptions(o,p,n));return false});j.append(" ",k)}return m.html(j)},messageToModalOptions:function(n,p,m){var j=this,o=$("<div/>"),l={title:"Details"};function k(q){q=_.omit(q,_.functions(q));return["<table>",_.map(q,function(s,r){s=(_.isObject(s))?(k(s)):(s);return'<tr><td style="vertical-align: top; color: grey">'+r+'</td><td style="padding-left: 8px">'+s+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(m)){l.body=o.append(k(m))}else{l.body=o.html(m)}l.buttons={Ok:function(){Galaxy.modal.hide();j.clearMessages()}};return l},clearMessages:function(){var j=this.$el.find(this.msgsSelector);j.empty()},scrollPosition:function(){return this.$el.parent().scrollTop()},scrollTo:function(j){this.$el.parent().scrollTop(j)},scrollToTop:function(){this.$el.parent().scrollTop(0);return this},scrollIntoView:function(k,l){if(!l){this.$el.parent().parent().scrollTop(k);return this}var j=window,m=this.$el.parent().parent(),o=$(j).innerHeight(),n=(o/2)-(l/2);$(m).scrollTop(k-n);return this},scrollToId:function(k){if((!k)||(!this.hdaViews[k])){return this}var j=this.hdaViews[k].$el;this.scrollIntoView(j.offset().top,j.outerHeight());return this},scrollToHid:function(j){var k=this.model.hdas.getByHid(j);if(!k){return this}return this.scrollToId(k.id)},connectToQuotaMeter:function(j){if(!j){return this}this.listenTo(j,"quota:over",this.showQuotaMessage);this.listenTo(j,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(j&&j.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var j=this.$el.find(".quota-message");if(j.is(":hidden")){j.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var j=this.$el.find(".quota-message");if(!j.is(":hidden")){j.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(j){if(!j){return this}this.on("new-storage",function(l,k){if(j&&l){j.findItemByHtml(_l("Include Deleted Datasets")).checked=l.get("show_deleted");j.findItemByHtml(_l("Include Hidden Datasets")).checked=l.get("show_hidden")}});return this},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});e.templates={historyPanel:Handlebars.templates["template-history-historyPanel"],anonHistoryPanel:Handlebars.templates["template-history-historyPanel-anon"]};return{HistoryPanel:e}});
\ No newline at end of file
+define(["mvc/history/history-model","mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/dataset/hda-edit"],function(c,f,a,i){var g=SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});g.storageKey=function h(){return("history-panel")};var d=SessionStorageModel.extend({defaults:{expandedHdas:{},show_deleted:false,show_hidden:false},addExpandedHda:function(j){this.save("expandedHdas",_.extend(this.get("expandedHdas"),_.object([j],[true])))},removeExpandedHda:function(j){this.save("expandedHdas",_.omit(this.get("expandedHdas"),j))},toString:function(){return"HistoryPrefs("+this.id+")"}});d.historyStorageKey=function b(j){if(!j){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+j)}return("history:"+j)};var e=Backbone.View.extend(LoggableMixin).extend({defaultHDAViewClass:i.HDAEditView,tagName:"div",className:"history-panel",fxSpeed:"fast",datasetsSelector:".datasets-list",emptyMsgSelector:".empty-history-message",msgsSelector:".message-container",initialize:function(j){j=j||{};if(j.logger){this.logger=j.logger}this.log(this+".initialize:",j);this.HDAViewClass=j.HDAViewClass||this.defaultHDAViewClass;this.linkTarget=j.linkTarget||"_blank";this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);this.preferences=new g(_.extend({id:g.storageKey()},_.pick(j,_.keys(g.prototype.defaults))));this.filters=[];this.selecting=j.selecting||false;this.annotationEditorShown=j.annotationEditorShown||false;this._setUpListeners();if(this.model){this._setUpWebStorage(j.initiallyExpanded,j.show_deleted,j.show_hidden);this._setUpModelEventHandlers()}if(j.onready){j.onready.call(this)}},_setUpListeners:function(){this.on("error",function(k,n,j,m,l){this.errorHandler(k,n,j,m,l)});this.on("loading-history",function(){this.showLoadingIndicator("loading history...")});this.on("loading-done",function(){this.hideLoadingIndicator()});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});this.on("switched-history current-history new-history",function(){if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});if(this.logger){this.on("all",function(j){this.log(this+"",arguments)},this)}},errorHandler:function(l,o,k,n,m){var j=this._parseErrorMessage(l,o,k,n,m);if(o&&o.status===0&&o.readyState===0){}else{if(o&&o.status===502){}else{if(!this.$el.find(this.msgsSelector).is(":visible")){this.once("rendered",function(){this.displayMessage("error",j.message,j.details)})}else{this.displayMessage("error",j.message,j.details)}}}},_parseErrorMessage:function(m,q,l,p,o){var k=Galaxy.currUser,j={message:this._bePolite(p),details:{user:(k instanceof User)?(k.toJSON()):(k+""),source:(m instanceof Backbone.Model)?(m.toJSON()):(m+""),xhr:q,options:(q)?(_.omit(l,"xhr")):(l)}};_.extend(j.details,o||{});if(q&&_.isFunction(q.getAllResponseHeaders)){var n=q.getAllResponseHeaders();n=_.compact(n.split("\n"));n=_.map(n,function(r){return r.split(": ")});j.details.xhr.responseHeaders=_.object(n)}return j},_bePolite:function(j){j=j||_l("An error occurred while getting updates from the server");return j+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadCurrentHistory:function(k){var j=this;return this.loadHistoryWithHDADetails("current",k).then(function(m,l){j.trigger("current-history",j)})},switchToHistory:function(m,l){var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories/"+m+"/set_as_current")};return this.loadHistoryWithHDADetails(m,l,k).then(function(o,n){j.trigger("switched-history",j)})},createNewHistory:function(l){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,l,k).then(function(n,m){j.trigger("new-history",j)})},loadHistoryWithHDADetails:function(m,l,k,o){var j=this,n=function(p){return j.getExpandedHdaIds(p.id)};return this.loadHistory(m,l,k,o,n)},loadHistory:function(m,l,k,p,n){this.trigger("loading-history",this);l=l||{};var j=this;var o=c.History.getHistoryData(m,{historyFn:k,hdaFn:p,hdaDetailIds:l.initiallyExpanded||n});return this._loadHistoryFromXHR(o,l).fail(function(s,q,r){j.trigger("error",j,s,l,_l("An error was encountered while "+q),{historyId:m,history:r||{}})}).always(function(){j.trigger("loading-done",j)})},_loadHistoryFromXHR:function(l,k){var j=this;l.then(function(m,n){j.setModel(m,n,k)});l.fail(function(n,m){j.render()});return l},setModel:function(l,j,k){k=k||{};if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.hdaViews={};if(Galaxy&&Galaxy.currUser){l.user=Galaxy.currUser.toJSON()}this.model=new c.History(l,j,k);this._setUpWebStorage(k.initiallyExpanded,k.show_deleted,k.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this);this.render();return this},_setUpWebStorage:function(k,j,l){this.storage=new d({id:d.historyStorageKey(this.model.get("id"))});if(_.isObject(k)){this.storage.set("exandedHdas",k)}if(_.isBoolean(j)){this.storage.set("show_deleted",j)}if(_.isBoolean(l)){this.storage.set("show_hidden",l)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get())},clearWebStorage:function(){for(var j in sessionStorage){if(j.indexOf("history:")===0){sessionStorage.removeItem(j)}}},getStoredOptions:function(k){if(!k||k==="current"){return(this.storage)?(this.storage.get()):({})}var j=sessionStorage.getItem(d.historyStorageKey(k));return(j===null)?({}):(JSON.parse(j))},getExpandedHdaIds:function(j){var k=this.getStoredOptions(j).expandedHdas;return((_.isEmpty(k))?([]):(_.keys(k)))},_setUpModelEventHandlers:function(){this.model.on("error error:hdas",function(k,m,j,l){this.errorHandler(k,m,j,l)},this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("add",this.addHdaView,this);this.model.hdas.on("change:deleted",this.handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this.handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(j){this.model.fetch()},this);this.model.hdas.on("state:ready",function(k,l,j){if((!k.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[k.id])}},this)},render:function(l,m){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k;if(this.model){k=this.renderModel()}else{k=this.renderWithoutModel()}$(j).queue("fx",[function(n){if(l&&j.$el.is(":visible")){j.$el.fadeOut(l,n)}else{n()}},function(n){j.$el.empty();if(k){j.$el.append(k.children());j.renderBasedOnPrefs()}n()},function(n){if(l&&!j.$el.is(":visible")){j.$el.fadeIn(l,n)}else{n()}},function(n){if(m){m.call(this)}j.trigger("rendered",this);n()}]);return this},renderWithoutModel:function(){var j=$("<div/>"),k=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return j.append(k)},renderModel:function(){var j=$("<div/>");if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){j.append(e.templates.anonHistoryPanel(this.model.toJSON()))}else{j.append(e.templates.historyPanel(this.model.toJSON()));if(Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(j);this._renderAnnotation(j)}}j.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(j);this.renderHdas(j);return j},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.showSearchControls(0)}},_renderTags:function(j){var k=this;this.tagsEditor=new TagsEditor({model:this.model,el:j.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("tagsEditorShown",true);k.toggleHDATagEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("tagsEditorShown",false);k.toggleHDATagEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}},_renderAnnotation:function(j){var k=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:j.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("annotationEditorShown",true);k.toggleHDAAnnotationEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("annotationEditorShown",false);k.toggleHDAAnnotationEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history Annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}},_renderSearchButton:function(j){return faIconButton({title:_l("Search datasets"),classes:"history-search-btn",faIcon:"fa-search"})},_renderSelectButton:function(j){return faIconButton({title:_l("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(j){j=j||this.$el;j.find("[title]").tooltip({placement:"bottom"});if((!this.model)||(!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var k=this;j.find(".history-name").attr("title",_l("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(l){j.find(".history-name").text(l);k.model.save({name:l}).fail(function(){j.find(".history-name").text(k.model.previous("name"))})}});this._setUpDatasetActionsPopup(j)},_setUpDatasetActionsPopup:function(j){var k=this;(new PopupMenu(j.find(".history-dataset-action-popup-btn"),[{html:_l("Hide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.hide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Unhide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.unhide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Delete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype["delete"];k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Undelete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.undelete;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Permanently delete datasets"),func:function(){if(confirm(_l("This will permanently remove the data in your datasets. Are you sure?"))){var l=f.HistoryDatasetAssociation.prototype.purge;k.getSelectedHdaCollection().ajaxQueue(l)}}}]))},refreshHdas:function(k,j){if(this.model){return this.model.refresh(k,j)}return $.when()},addHdaView:function(m){this.log("add."+this,m);var k=this;if(!m.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return}$({}).queue([function l(o){var n=k.$el.find(k.emptyMsgSelector);if(n.is(":visible")){n.fadeOut(k.fxSpeed,o)}else{o()}},function j(o){k.scrollToTop();var n=k.$el.find(k.datasetsSelector);k.createHdaView(m).$el.hide().prependTo(n).slideDown(k.fxSpeed)}])},createHdaView:function(l){var k=l.get("id"),j=this.storage.get("expandedHdas")[k],m=new this.HDAViewClass({model:l,linkTarget:this.linkTarget,expanded:j,tagsEditorShown:this.preferences.get("tagsEditorShown"),annotationEditorShown:this.preferences.get("annotationEditorShown"),selectable:this.selecting,hasUser:this.model.ownedByCurrUser(),logger:this.logger});this._setUpHdaListeners(m);this.hdaViews[k]=m;return m.render()},_setUpHdaListeners:function(k){var j=this;k.on("body-expanded",function(l){j.storage.addExpandedHda(l)});k.on("body-collapsed",function(l){j.storage.removeExpandedHda(l)});k.on("error",function(m,o,l,n){j.errorHandler(m,o,l,n)})},handleHdaDeletionChange:function(j){if(j.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[j.id])}},handleHdaVisibleChange:function(j){if(j.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[j.id])}},removeHdaView:function(k){if(!k){return}var j=this;k.$el.fadeOut(j.fxSpeed,function(){k.off();k.remove();delete j.hdaViews[k.model.id];if(_.isEmpty(j.hdaViews)){j.$el.find(j.emptyMsgSelector).fadeIn(j.fxSpeed,function(){j.trigger("empty-history",j)})}})},renderHdas:function(l){l=l||this.$el;this.hdaViews={};var k=this,j=l.find(this.datasetsSelector),m=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);j.empty();if(m.length){m.each(function(n){j.prepend(k.createHdaView(n).$el)});l.find(this.emptyMsgSelector).hide()}else{l.find(this.emptyMsgSelector).show()}return this.hdaViews},toggleHDATagEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.tagsEditor){l.tagsEditor.toggle.apply(l.tagsEditor,k)}})},toggleHDAAnnotationEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.annotationEditor){l.annotationEditor.toggle.apply(l.annotationEditor,k)}})},events:{"click .message-container":"clearMessages","click .history-search-btn":"toggleSearchControls","click .history-select-btn":function(j){this.toggleSelectors(this.fxSpeed)},"click .history-select-all-datasets-btn":"selectAllDatasets"},updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(j){j.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},toggleShowDeleted:function(){this.storage.set("show_deleted",!this.storage.get("show_deleted"));this.renderHdas();return this.storage.get("show_deleted")},toggleShowHidden:function(){this.storage.set("show_hidden",!this.storage.get("show_hidden"));this.renderHdas();return this.storage.get("show_hidden")},renderSearchControls:function(k){var l=this;function n(o){l.searchFor=o;l.filters=[function(p){return p.matchesAll(l.searchFor)}];l.trigger("search:searching",o,l);l.renderHdas()}function j(o){if(l.model.hdas.haveDetails()){n(o);return}l.$el.find(".history-search-controls").searchInput("toggle-loading");l.model.hdas.fetchAllDetails({silent:true}).always(function(){l.$el.find(".history-search-controls").searchInput("toggle-loading")}).done(function(){n(o)})}function m(){l.searchFor="";l.filters=[];l.trigger("search:clear",l);l.renderHdas()}return k.searchInput({initialVal:l.searchFor,name:"history-search",placeholder:"search datasets",classes:"history-search",onfirstsearch:j,onsearch:n,onclear:m})},showSearchControls:function(l){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k=this.$el.find(".history-search-controls");if(!k.children().size()){k=this.renderSearchControls(k).hide()}k.show(l,function(){$(this).find("input").focus();j.preferences.set("searching",true)})},hideSearchControls:function(){speed=(speed===undefined)?(this.fxSpeed):(speed);var j=this;this.$el.find(".history-search-controls").hide(speed,function(){j.preferences.set("searching",false)})},toggleSearchControls:function(j){speed=(jQuery.type(j)==="number")?(j):(this.fxSpeed);if(this.$el.find(".history-search-controls").is(":visible")){this.hideSearchControls(speed)}else{this.showSearchControls(speed)}},showSelectors:function(j){this.selecting=true;this.$el.find(".history-dataset-actions").slideDown(j);_.each(this.hdaViews,function(k){k.showSelector(j)})},hideSelectors:function(j){this.selecting=false;this.$el.find(".history-dataset-actions").slideUp(j);_.each(this.hdaViews,function(k){k.hideSelector(j)})},toggleSelectors:function(j){if(!this.selecting){this.showSelectors(j)}else{this.hideSelectors(j)}},selectAllDatasets:function(k){var j=this.$el.find(".history-select-all-datasets-btn");currMode=j.data("mode");if(currMode==="select"){_.each(this.hdaViews,function(l){l.select(k)});j.data("mode","deselect");j.text(_l("De-select all"))}else{if(currMode==="deselect"){_.each(this.hdaViews,function(l){l.deselect(k)});j.data("mode","select");j.text(_l("Select all"))}}},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(j){return j.selected})},getSelectedHdaCollection:function(){return new f.HDACollection(_.map(this.getSelectedHdaViews(),function(j){return j.model}),{historyId:this.model.id})},showLoadingIndicator:function(k,j,l){j=(j!==undefined)?(j):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,l)}else{this.$el.fadeOut(j);this.indicator.show(k,j,l)}},hideLoadingIndicator:function(j,k){j=(j!==undefined)?(j):(this.fxSpeed);if(this.indicator){this.indicator.hide(j,k)}},displayMessage:function(o,p,n){var l=this;this.scrollToTop();var m=this.$el.find(this.msgsSelector),j=$("<div/>").addClass(o+"message").html(p);if(!_.isEmpty(n)){var k=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(l.messageToModalOptions(o,p,n));return false});j.append(" ",k)}return m.html(j)},messageToModalOptions:function(n,p,m){var j=this,o=$("<div/>"),l={title:"Details"};function k(q){q=_.omit(q,_.functions(q));return["<table>",_.map(q,function(s,r){s=(_.isObject(s))?(k(s)):(s);return'<tr><td style="vertical-align: top; color: grey">'+r+'</td><td style="padding-left: 8px">'+s+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(m)){l.body=o.append(k(m))}else{l.body=o.html(m)}l.buttons={Ok:function(){Galaxy.modal.hide();j.clearMessages()}};return l},clearMessages:function(){var j=this.$el.find(this.msgsSelector);j.empty()},scrollPosition:function(){return this.$el.parent().scrollTop()},scrollTo:function(j){this.$el.parent().scrollTop(j)},scrollToTop:function(){this.$el.parent().scrollTop(0);return this},scrollIntoView:function(k,l){if(!l){this.$el.parent().parent().scrollTop(k);return this}var j=window,m=this.$el.parent().parent(),o=$(j).innerHeight(),n=(o/2)-(l/2);$(m).scrollTop(k-n);return this},scrollToId:function(k){if((!k)||(!this.hdaViews[k])){return this}var j=this.hdaViews[k].$el;this.scrollIntoView(j.offset().top,j.outerHeight());return this},scrollToHid:function(j){var k=this.model.hdas.getByHid(j);if(!k){return this}return this.scrollToId(k.id)},connectToQuotaMeter:function(j){if(!j){return this}this.listenTo(j,"quota:over",this.showQuotaMessage);this.listenTo(j,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(j&&j.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var j=this.$el.find(".quota-message");if(j.is(":hidden")){j.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var j=this.$el.find(".quota-message");if(!j.is(":hidden")){j.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(j){if(!j){return this}this.on("new-storage",function(l,k){if(j&&l){j.findItemByHtml(_l("Include Deleted Datasets")).checked=l.get("show_deleted");j.findItemByHtml(_l("Include Hidden Datasets")).checked=l.get("show_hidden")}});return this},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});e.templates={historyPanel:Handlebars.templates["template-history-historyPanel"],anonHistoryPanel:Handlebars.templates["template-history-historyPanel-anon"]};return{HistoryPanel:e}});
\ No newline at end of file
diff -r a3e2c3464298ebea3fd2ab93a43aac479ba9665a -r 9fa754a203b7312d58211b5e1e7140bf36aec76a static/scripts/packed/mvc/user/user-model.js
--- a/static/scripts/packed/mvc/user/user-model.js
+++ b/static/scripts/packed/mvc/user/user-model.js
@@ -1,1 +1,1 @@
-var User=Backbone.Model.extend(LoggableMixin).extend({urlRoot:galaxy_config.root+"api/users",defaults:{id:null,username:"("+_l("anonymous user")+")",email:"",total_disk_usage:0,nice_total_disk_usage:"",quota_percent:null},initialize:function(a){this.log("User.initialize:",a);this.on("loaded",function(b,c){this.log(this+" has loaded:",b,c)});this.on("change",function(b,c){this.log(this+" has changed:",b,c.changes)})},isAnonymous:function(){return(!this.get("email"))},loadFromApi:function(d,b){d=d||User.CURRENT_ID_STR;b=b||{};var a=this,c=b.success;b.success=function(f,e){a.trigger("loaded",f,e);if(c){c(f,e)}};if(d===User.CURRENT_ID_STR){b.url=this.urlRoot+"/"+User.CURRENT_ID_STR}return BaseModel.prototype.fetch.call(this,b)},clearSessionStorage:function(){for(var a in sessionStorage){if(a.indexOf("HistoryView.")===0){sessionStorage.removeItem(a)}}},toString:function(){var a=[this.get("username")];if(this.get("id")){a.unshift(this.get("id"));a.push(this.get("email"))}return"User("+a.join(":")+")"}});User.CURRENT_ID_STR="current";User.getCurrentUserFromApi=function(b){var a=new User();a.loadFromApi(User.CURRENT_ID_STR,b);return a};var UserCollection=Backbone.Collection.extend(LoggableMixin).extend({model:User,urlRoot:galaxy_config.root+"api/users"});
\ No newline at end of file
+var User=Backbone.Model.extend(LoggableMixin).extend({urlRoot:galaxy_config.root+"api/users",defaults:{id:null,username:"("+_l("anonymous user")+")",email:"",total_disk_usage:0,nice_total_disk_usage:"",quota_percent:null},initialize:function(a){this.log("User.initialize:",a);this.on("loaded",function(b,c){this.log(this+" has loaded:",b,c)});this.on("change",function(b,c){this.log(this+" has changed:",b,c.changes)})},isAnonymous:function(){return(!this.get("email"))},loadFromApi:function(d,b){d=d||User.CURRENT_ID_STR;b=b||{};var a=this,c=b.success;b.success=function(f,e){a.trigger("loaded",f,e);if(c){c(f,e)}};if(d===User.CURRENT_ID_STR){b.url=this.urlRoot+"/"+User.CURRENT_ID_STR}return Backbone.Model.prototype.fetch.call(this,b)},clearSessionStorage:function(){for(var a in sessionStorage){if(a.indexOf("HistoryView.")===0){sessionStorage.removeItem(a)}}},toString:function(){var a=[this.get("username")];if(this.get("id")){a.unshift(this.get("id"));a.push(this.get("email"))}return"User("+a.join(":")+")"}});User.CURRENT_ID_STR="current";User.getCurrentUserFromApi=function(b){var a=new User();a.loadFromApi(User.CURRENT_ID_STR,b);return a};var UserCollection=Backbone.Collection.extend(LoggableMixin).extend({model:User,urlRoot:galaxy_config.root+"api/users"});
\ No newline at end of file
diff -r a3e2c3464298ebea3fd2ab93a43aac479ba9665a -r 9fa754a203b7312d58211b5e1e7140bf36aec76a templates/grid_base.mako
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -207,18 +207,11 @@
##
<%def name="center_panel()">
- ${self.grid_body()}
+ ${self.load()}
</%def>
## Render the grid's basic elements. Each of these elements can be subclassed.
<%def name="body()">
- ${self.grid_body()}
-</%def>
-
-## Because body() is special and always exists even if not explicitly defined,
-## it's not possible to override body() in the topmost template in the chain.
-## Because of this, override grid_body() instead.
-<%def name="grid_body()">
${self.load()}
</%def>
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: guerler: Grids: Push code into js, async refresh based on json
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a3e2c3464298/
Changeset: a3e2c3464298
User: guerler
Date: 2013-12-18 23:16:43
Summary: Grids: Push code into js, async refresh based on json
Affected #: 3 files
diff -r 350c72df26815c0483a268fd1a097f0af8360930 -r a3e2c3464298ebea3fd2ab93a43aac479ba9665a static/scripts/galaxy.grids.js
--- a/static/scripts/galaxy.grids.js
+++ b/static/scripts/galaxy.grids.js
@@ -130,7 +130,13 @@
});
return url_data;
+ },
+
+ // Return URL for obtaining a new grid
+ get_url: function (args) {
+ return this.get('url_base') + "?" + $.param(this.get_url_data()) + '&' + $.param(args);
}
+
});
// grid view
@@ -140,9 +146,10 @@
grid: null,
// Initialize
- initialize: function(grid)
+ initialize: function(grid_config)
{
- this.init_grid(grid);
+ // initialize controls
+ this.init_grid(grid_config);
this.init_grid_controls();
// Initialize text filters to select text on click and use normal font when user is typing.
@@ -153,9 +160,30 @@
},
// Initialize
- init_grid: function(grid)
+ init_grid: function(grid_config)
{
- this.grid = grid;
+ // link grid model
+ this.grid = new Grid(grid_config);
+
+ // get options
+ var options = this.grid.attributes;
+
+ // strip protocol and domain
+ var url = this.grid.get('url_base');
+ url = url.replace(/^.*\/\/[^\/]+/, '');
+ this.grid.set('url_base', url);
+
+ // update div contents
+ $('#grid-table-body').html(this.template_body(options));
+ $('#grid-table-footer').html(this.template_footer(options));
+
+ // update message
+ if (options.message) {
+ $('#grid-message').html(this.template_message(options));
+ setTimeout( function() { $('#grid-message').html(''); }, 5000);
+ }
+
+ // configure elements
this.init_grid_elements();
},
@@ -537,7 +565,7 @@
// check for operation details
if (operation && id) {
// show confirmation box
- if (confirmation_text && confirmation_text != '' && confirmation_text != 'None')
+ if (confirmation_text && confirmation_text != '' && confirmation_text != 'None' && confirmation_text != 'null')
if(!confirm(confirmation_text))
return false;
@@ -622,30 +650,16 @@
type: method,
url: self.grid.get('url_base'),
data: self.grid.get_url_data(),
- error: function(response) { alert( "Grid refresh failed" );},
+ error: function(response) { alert( 'Grid refresh failed' );},
success: function(response_text) {
- // HACK: use a simple string to separate the elements in the
- // response: (1) table body; (2) number of pages in table; and (3) message.
- var parsed_response_text = response_text.split("*****");
-
- // Update grid body and footer.
- $('#grid-table-body').html(parsed_response_text[0]);
- // FIXME: this does not work at all; what's needed is a function
- // that updates page links when number of pages changes.
- $('#grid-table-footer').html(parsed_response_text[1]);
-
+ // Initialize new grid config
+ self.init_grid($.parseJSON(response_text));
+
// Trigger custom event to indicate grid body has changed.
$('#grid-table-body').trigger('update');
// Hide loading overlay.
$('.loading-elt-overlay').hide();
-
- // Show message if there is one.
- var message = $.trim( parsed_response_text[2] );
- if (message !== "") {
- $('#grid-message').html( message ).show();
- setTimeout( function() { $('#grid-message').hide(); }, 5000);
- }
},
complete: function() {
// Clear grid of transient request attributes.
@@ -688,6 +702,253 @@
if (cur_page !== null && cur_page !== undefined && cur_page !== 'all') {
this.grid.set('cur_page', 1);
}
+ },
+
+ // template
+ template_body: function(options) {
+ // initialize
+ var tmpl = '';
+ var num_rows_rendered = 0;
+ var items_length = options.items.length;
+
+ // empty grid?
+ if (items_length == 0) {
+ // No results.
+ tmpl += '<tr><td colspan="100"><em>No Items</em></td></tr>';
+ num_rows_rendered = 1;
+ }
+
+ // create rows
+ for (i in options.items) {
+
+ // encode ids
+ var item = options.items[i];
+ var encoded_id = item.encode_id;
+ var popupmenu_id = 'grid-' + i + '-popup';
+
+ // Tag current
+ tmpl += '<tr ';
+ if (options.current_item_id == item.id) {
+ tmpl += 'class="current"';
+ }
+ tmpl += '>';
+
+ // Item selection column
+ if (options.show_item_checkboxes) {
+ tmpl += '<td style="width: 1.5em;">' +
+ '<input type="checkbox" name="id" value="' + encoded_id + '" id="' + encoded_id + '" class="grid-row-select-checkbox" />' +
+ '</td>';
+ }
+
+ // Data columns
+ for (j in options.columns) {
+ var column = options.columns[j];
+ if (column.visible) {
+ // Nowrap
+ var nowrap = '';
+ if (column.nowrap) {
+ nowrap = 'style="white-space:nowrap;"';
+ }
+
+ // get column settings
+ var column_settings = item.column_config[column.label];
+
+ // load attributes
+ var link = column_settings.link;
+ var value = column_settings.value;
+ var inbound = column_settings.inbound;
+
+ // unescape value
+ if (jQuery.type( value ) === 'string') {
+ value = value.replace(/\/\//g, '/');
+ }
+
+ // Attach popup menu?
+ var id = "";
+ var cls = "";
+ if (column.attach_popup) {
+ id = 'grid-' + i + '-popup';
+ cls = "menubutton"
+ if (link != '') {
+ cls += " split";
+ }
+ cls += " popup";
+ }
+
+ // Check for row wrapping
+ tmpl += '<td ' + nowrap + '>';
+
+ // Link
+ if (link) {
+ if (options.operations.length != 0) {
+ tmpl += '<div id="' + id + '" class="' + cls + '" style="float: left;">';
+ }
+
+ var label_class = "";
+ if (inbound) {
+ label_class = "use-inbound"
+ } else {
+ label_class = "use-outbound"
+ }
+ tmpl += '<a class="label ' + label_class + '" href="' + link + '" onclick="return false;">' + value + '</a>';
+ if (options.operations.length != 0) {
+ tmpl += '</div>';
+ }
+ } else {
+ tmpl += '<div id="' + id + '" class="' + cls + '"><label id="' + column.label_id_prefix + encoded_id + '" for="' + encoded_id + '">' + value + '</label></div>';
+ }
+ tmpl += '</td>';
+ }
+ }
+ tmpl += '</tr>';
+ num_rows_rendered++;
+ }
+ return tmpl;
+ },
+
+ // template
+ template_footer: function(options) {
+
+ // create template string
+ var tmpl = '';
+
+ // paging
+ if (options.use_paging && options.num_pages > 1) {
+ // get configuration
+ var num_page_links = options.num_page_links;
+ var cur_page_num = options.cur_page_num;
+ var num_pages = options.num_pages;
+
+ // First pass on min page.
+ var page_link_range = num_page_links / 2;
+ var min_page = cur_page_num - page_link_range
+ var min_offset = 0;
+ if (min_page == 0) {
+ // Min page is too low.
+ min_page = 1;
+ min_offset = page_link_range - ( cur_page_num - min_page );
+ }
+
+ // Set max page.
+ var max_range = page_link_range + min_offset;
+ var max_page = cur_page_num + max_range;
+ if (max_page <= num_pages) {
+ // Max page is fine.
+ max_offset = 0;
+ } else {
+ // Max page is too high.
+ max_page = num_pages;
+ // +1 to account for the +1 in the loop below.
+ max_offset = max_range - ( max_page + 1 - cur_page_num );
+ }
+
+ // Second and final pass on min page to add any unused
+ // offset from max to min.
+ if (max_offset != 0) {
+ min_page -= max_offset
+ if (min_page < 1) {
+ min_page = 1
+ }
+ }
+
+ // template header
+ tmpl += '<tr id="page-links-row">';
+ if (options.show_item_checkboxes) {
+ tmpl += '<td></td>';
+ }
+ tmpl += '<td colspan="100">' +
+ '<span id="page-link-container">' +
+ 'Page:';
+
+ if (min_page > 1) {
+ tmpl += '<span class="page-link" id="page-link-1"><a href="' + this.grid.get_url({page : page_index}) + '" page_num="1" onclick="return false;">1</a></span> ...';
+ }
+
+ // create page urls
+ for (var page_index = min_page; page_index < max_page + 1; page_index++) {
+
+ if (page_index == options.cur_page_num) {
+ tmpl += '<span class="page-link inactive-link" id="page-link-' + page_index + '">' + page_index + '</span>';
+ } else {
+ tmpl += '<span class="page-link" id="page-link-' + page_index + '"><a href="' + this.grid.get_url({page : page_index}) + '" onclick="return false;" page_num="' + page_index + '">' + page_index + '</a></span>';
+ }
+ }
+
+ // show last page
+ if (max_page < num_pages) {
+ tmpl += '...' +
+ '<span class="page-link" id="page-link-' + num_pages + '"><a href="' + this.grid.get_url({page : num_pages}) + '" onclick="return false;" page_num="' + num_pages + '">' + num_pages + '</a></span>';
+ }
+ tmpl += '</span>';
+
+ // Show all link
+ tmpl += '<span class="page-link" id="show-all-link-span"> | <a href="' + this.grid.get_url({page : 'all'}) + '" onclick="return false;" page_num="all">Show All</a></span>' +
+ '</td>' +
+ '</tr>';
+ }
+
+ // Grid operations for multiple items.
+ if (options.show_item_checkboxes) {
+ // start template
+ tmpl += '<tr>' +
+ '<input type="hidden" id="operation" name="operation" value="">' +
+ '<td></td>' +
+ '<td colspan="100">' +
+ 'For <span class="grid-selected-count"></span> selected ' + options.get_class_plural + ': ';
+
+ // configure buttons for operations
+ for (i in options.operations) {
+ var operation = options.operations[i];
+ if (operation.allow_multiple) {
+ tmpl += '<input type="button" value="' + operation.label + '" class="action-button" onclick="gridView.submit_operation(this, \'' + operation.confirm + '\')"> ';
+ }
+ }
+
+ // finalize template
+ tmpl += '</td>' +
+ '</tr>';
+ }
+
+ // count global operations
+ var found_global = false;
+ for (i in options.operations) {
+ if (options.operations[i].global_operation) {
+ found_global = true;
+ break;
+ }
+ }
+
+ // add global operations
+ if (found_global) {
+ tmpl += '<tr>' +
+ '<td colspan="100">';
+ for (i in options.operations) {
+ var operation = options.operations[i];
+ if (operation.global_operation) {
+ tmpl += '<a class="action-button" href="' + operation.global_operation + '">' + operation.label + '</a>';
+ }
+ }
+ tmpl += '</td>' +
+ '</tr>';
+ }
+
+ // add legend
+ if (options.legend) {
+ tmpl += '<tr>' +
+ '<td colspan="100">' + options.legend + '</td>' +
+ '</tr>';
+ }
+
+ // return
+ return tmpl;
+ },
+
+ // template
+ template_message: function(options) {
+ return '<p>' +
+ '<div class="' + options.status + 'message transient-message">' + options.message + '</div>' +
+ '<div style="clear: both"></div>' +
+ '</p>';
}
});
diff -r 350c72df26815c0483a268fd1a097f0af8360930 -r a3e2c3464298ebea3fd2ab93a43aac479ba9665a templates/grid_base.mako
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -1,6 +1,7 @@
<%!
from galaxy.web.framework.helpers.grids import TextColumn, StateColumn, GridColumnFilter
from galaxy.web.framework.helpers import iff
+ import urllib
import galaxy.util
def inherit(context):
@@ -24,7 +25,7 @@
self.grid_javascripts()
if embedded:
self.render_grid_header( grid, False )
- self.render_grid_table( grid, show_item_checkboxes=show_item_checkboxes )
+ self.render_grid_table( grid )
else:
self.make_grid( grid )
endif
@@ -50,6 +51,7 @@
'show_item_checkboxes' : context.get('show_item_checkboxes', False),
'cur_page_num' : cur_page_num,
'num_pages' : num_pages,
+ 'num_page_links' : num_page_links,
'history_tag_autocomplete_url' : url( controller='tag', action='tag_autocomplete_data', item_class='History' ),
'history_name_autocomplete_url' : url( controller='history', action='name_autocomplete_data' ),
'status' : status,
@@ -58,7 +60,6 @@
'operations' : [],
'items' : [],
'columns' : [],
- 'multiple_item_ops_exist' : None,
'get_class_plural' : get_class_plural( grid.model_class ).lower(),
'use_paging' : grid.use_paging,
'legend' : grid.legend,
@@ -68,7 +69,7 @@
'default_filter_dict' : default_filter_dict,
'advanced_search' : advanced_search
}
-
+
## add current item if exists
if current_item:
self.grid_config['current_item_id'] = current_item.id
@@ -119,7 +120,7 @@
'global_operation' : False
})
if operation.allow_multiple:
- self.grid_config['multiple_item_ops_exist'] = True
+ self.grid_config['show_item_checkboxes'] = True
if operation.global_operation:
self.grid_config['global_operation'] = url( ** (operation.global_operation()) )
@@ -176,8 +177,6 @@
# Handle non-ascii chars.
if isinstance(value, str):
value = unicode(value, 'utf-8')
-
- # escape string
value = value.replace('/', '//')
endif
@@ -242,7 +241,16 @@
}
gridView.add_filter_condition("tags", tag);
};
+
+ ## load grid viewer
+ $(function() {
+ require(['galaxy.grids'], function(mod_grids) {
+ gridView = new mod_grids.GridView(${h.to_json_string(self.grid_config)});
+ });
+ });
</script>
+
+ ${handle_refresh_frames()}
</%def><%def name="stylesheets()">
@@ -268,20 +276,13 @@
<td></td></tr><tr>
- <td width="100%" id="grid-message" valign="top">
- %if message:
- <p>
- <div class="${self.grid_config['status']}message transient-message">${self.grid_config['message']}</div>
- <div style="clear: both"></div>
- </p>
- %endif
- </td>
+ <td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>
- ${self.render_grid_table( grid, show_item_checkboxes )}
+ ${self.render_grid_table( grid )}
</%def><%def name="grid_title()">
@@ -328,16 +329,11 @@
</%def>
## Render grid.
-<%def name="render_grid_table( grid, show_item_checkboxes=False)">
+<%def name="render_grid_table( grid )"><%
# get configuration
show_item_checkboxes = self.grid_config['show_item_checkboxes']
- multiple_item_ops_exist = self.grid_config['multiple_item_ops_exist']
sort_key = self.grid_config['sort_key']
-
- # Show checkboxes if flag is set or if multiple item ops exist.
- if show_item_checkboxes or multiple_item_ops_exist:
- show_item_checkboxes = True
%><form method="post" onsubmit="return false;"><table id="grid-table" class="grid">
@@ -364,238 +360,14 @@
</th>
%endif
%endfor
- <th></th></tr></thead>
- <tbody id="grid-table-body">
- ${render_grid_table_body_contents( grid, show_item_checkboxes )}
- </tbody>
- <tfoot id="grid-table-footer">
- ${render_grid_table_footer_contents( grid, show_item_checkboxes )}
- </tfoot>
+ <tbody id="grid-table-body"></tbody>
+ <tfoot id="grid-table-footer"></tfoot></table></form></%def>
-## Render grid table body contents.
-<%def name="render_grid_table_body_contents(grid, show_item_checkboxes=False)">
- <% num_rows_rendered = 0 %>
- %if len(self.grid_config['items']) == 0:
- ## No results.
- <tr><td colspan="100"><em>No Items</em></td></tr>
- <% num_rows_rendered = 1 %>
- %endif
- %for i, item in enumerate( self.grid_config['items'] ):
- <% encoded_id = item['encode_id'] %>
- <% popupmenu_id = "grid-" + str(i) + "-popup" %>
- <tr \
- %if self.grid_config['current_item_id'] == item['id']:
- class="current" \
- %endif
- >
- ## Item selection column
- %if show_item_checkboxes:
- <td style="width: 1.5em;">
- <input type="checkbox" name="id" value="${encoded_id}" id="${encoded_id}" class="grid-row-select-checkbox" />
- </td>
- %endif
- ## Data columns
- %for column in self.grid_config['columns']:
- %if column['visible']:
- <%
- ## Nowrap
- nowrap = ""
- if column['nowrap']:
- nowrap = 'style="white-space:nowrap;"'
-
- # get column settings
- column_settings = item['column_config'][column['label']]
-
- # load attributes
- link = column_settings['link']
- value = column_settings['value']
- inbound = column_settings['inbound']
-
- # unescape value
- if isinstance(value, unicode):
- value = value.replace('//', '/')
-
- # Attach popup menu?
- id = ""
- cls = ""
- if column['attach_popup']:
- id = 'grid-%d-popup' % i
- cls = "menubutton"
- if link:
- cls += " split"
- endif
- cls += " popup"
- endif
- %>
- <td ${nowrap}>
- %if link:
- %if len(self.grid_config['operations']) != 0:
- <div id="${id}" class="${cls}" style="float: left;">
- %endif
-
- <%
- label_class = ""
- if inbound:
- label_class = "use-inbound"
- else:
- label_class = "use-outbound"
- endif
- %>
- <a class="label ${label_class}" href="${link}" onclick="return false;">${value}</a>
- %if len(self.grid_config['operations']) != 0:
- </div>
- %endif
- %else:
- <div id="${id}" class="${cls}"><label id="${column['label_id_prefix']}${encoded_id}" for="${encoded_id}">${value}</label></div>
- %endif
- </td>
- %endif
- %endfor
- </tr>
- <% num_rows_rendered += 1 %>
- %endfor
-
- ## update configuration
- <script type="text/javascript">
- $(function() {
- require(['galaxy.grids'], function(mod_grids) {
- ## get configuration
- var grid_config = ${ h.to_json_string( self.grid_config ) };
-
- // Create grid.
- var grid = new mod_grids.Grid(grid_config);
-
- // strip protocol and domain
- var url = grid.get('url_base');
- url = url.replace(/^.*\/\/[^\/]+/, '');
- grid.set('url_base', url);
-
- // Create view.
- if (!gridView)
- gridView = new mod_grids.GridView(grid);
- else
- gridView.init_grid(grid);
- });
- });
- </script>
-
- ${handle_refresh_frames()}
-</%def>
-
-## Render grid table footer contents.
-<%def name="render_grid_table_footer_contents(grid, show_item_checkboxes=False)">
- <%
- items_plural = self.grid_config['get_class_plural']
- num_pages = self.grid_config['num_pages']
- cur_page_num = self.grid_config['cur_page_num']
- %>
- %if self.grid_config['use_paging'] and num_pages > 1:
- <tr id="page-links-row">
- %if show_item_checkboxes:
- <td></td>
- %endif
- <td colspan="100">
- <span id='page-link-container'>
- ## Page links. Show 10 pages around current page.
- <%
- #
- # Set minimum & maximum page.
- #
- page_link_range = num_page_links/2
-
- # First pass on min page.
- min_page = cur_page_num - page_link_range
- if min_page >= 1:
- # Min page is fine.
- min_offset = 0
- else:
- # Min page is too low.
- min_page = 1
- min_offset = page_link_range - ( cur_page_num - min_page )
-
- # Set max page.
- max_range = page_link_range + min_offset
- max_page = cur_page_num + max_range
- if max_page <= num_pages:
- # Max page is fine.
- max_offset = 0
- else:
- # Max page is too high.
- max_page = num_pages
- # +1 to account for the +1 in the loop below.
- max_offset = max_range - ( max_page + 1 - cur_page_num )
-
- # Second and final pass on min page to add any unused
- # offset from max to min.
- if max_offset != 0:
- min_page -= max_offset
- if min_page < 1:
- min_page = 1
- %>
- Page:
- % if min_page > 1:
- <span class='page-link' id="page-link-1"><a href="${url( page=1 )}" page_num="1" onclick="return false;">1</a></span> ...
- % endif
- %for page_index in range(min_page, max_page + 1):
- %if page_index == cur_page_num:
- <span class='page-link inactive-link' id="page-link-${page_index}">${page_index}</span>
- %else:
- <% args = { 'page' : page_index } %>
- <span class='page-link' id="page-link-${page_index}"><a href="${url( args )}" onclick="return false;" page_num='${page_index}'>${page_index}</a></span>
- %endif
- %endfor
- %if max_page < num_pages:
- ...
- <span class='page-link' id="page-link-${num_pages}"><a href="${url( page=num_pages )}" onclick="return false;" page_num="${num_pages}">${num_pages}</a></span>
- %endif
- </span>
-
- ## Show all link
- <span class='page-link' id='show-all-link-span'> | <a href="${url( page='all' )}" onclick="return false;" page_num="all">Show All</a></span>
- </td>
- </tr>
- %endif
- ## Grid operations for multiple items.
- %if show_item_checkboxes:
- <tr>
- ## place holder for multiple operation commands
- <input type="hidden" id="operation" name="operation" value="">
- <td></td>
- <td colspan="100">
- For <span class="grid-selected-count"></span> selected ${items_plural}:
- %for operation in self.grid_config['operations']:
- %if operation['allow_multiple']:
- <input type="button" value="${operation['label']}" class="action-button" onclick="gridView.submit_operation(this, '${operation['confirm']}')">
- %endif
- %endfor
- </td>
- </tr>
- %endif
- %if len([o for o in self.grid_config['operations'] if o['global_operation']]) > 0:
- <tr>
- <td colspan="100">
- %for operation in self.grid_config['operations']:
- %if operation['global_operation']:
- <a class="action-button" href="${operation['global_operation']}">${operation['label']}</a>
- %endif
- %endfor
- </td>
- </tr>
- %endif
- %if self.grid_config['legend']:
- <tr>
- <td colspan="100">
- ${self.grid_config['legend']}
- </td>
- </tr>
- %endif
-</%def>
-
## Print grid search/filtering UI.
<%def name="render_grid_filters( grid, render_advanced_search=True )"><%
diff -r 350c72df26815c0483a268fd1a097f0af8360930 -r a3e2c3464298ebea3fd2ab93a43aac479ba9665a templates/grid_base_async.mako
--- a/templates/grid_base_async.mako
+++ b/templates/grid_base_async.mako
@@ -1,18 +1,4 @@
-<%namespace file="./grid_base.mako" import="*" />
-<%namespace file="/display_common.mako" import="render_message" />
+<%namespace name="grid_base" file="./grid_base.mako" import="*" />
${init()}
-
-<%
- # Set flag to indicate whether grid has operations that operate on multiple items.
- multiple_item_ops_exist = False
- for operation in grid.operations:
- if operation.allow_multiple:
- multiple_item_ops_exist = True
-%>
-
-${render_grid_table_body_contents( grid, show_item_checkboxes=( show_item_checkboxes or multiple_item_ops_exist ) )}
-*****
-${render_grid_table_footer_contents( grid, show_item_checkboxes=( show_item_checkboxes or multiple_item_ops_exist ) )}
-*****
-${render_message( message, status )}
\ No newline at end of file
+${h.to_json_string( grid_base.grid_config )}
\ 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
commit/galaxy-central: simleo: tools/fastq/fastq_paired_end_joiner: added support for recent Illumina headers
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e232573b7c62/
Changeset: e232573b7c62
User: simleo
Date: 2013-06-06 17:33:46
Summary: tools/fastq/fastq_paired_end_joiner: added support for recent Illumina headers
Affected #: 2 files
diff -r af91c734dbb76b8c0e3166c7471370f9ba8260b5 -r e232573b7c629a94ab60e6b615ecedf156913308 tools/fastq/fastq_paired_end_joiner.py
--- a/tools/fastq/fastq_paired_end_joiner.py
+++ b/tools/fastq/fastq_paired_end_joiner.py
@@ -1,38 +1,162 @@
-#Dan Blankenberg
-import sys, os, shutil
-from galaxy_utils.sequence.fastq import fastqReader, fastqNamedReader, fastqWriter, fastqJoiner
+"""
+Extended version of Dan Blankenberg's fastq joiner (adds support for
+recent Illumina headers).
+"""
+
+import sys, re
+import galaxy_utils.sequence.fastq as fq
+
+
+class IDManager(object):
+
+ def __init__(self, sep="\t"):
+ """
+ Recent Illumina FASTQ header format::
+
+ @<COORDS><FLAGS>
+ COORDS = <Instrument>:<Run #>:<Flowcell ID>:<Lane>:<Tile>:<X>:<Y>
+ FLAGS = <Read>:<Is Filtered>:<Control Number>:<Index Sequence>
+
+ where the whitespace character between <COORDS> and <FLAGS> can be
+ either a space or a tab.
+ """
+ self.sep = sep
+
+ def parse_id(self, identifier):
+ try:
+ coords, flags = identifier.strip()[1:].split(self.sep, 1)
+ except ValueError:
+ raise RuntimeError("bad identifier: %r" % (identifier,))
+ return coords.split(":"), flags.split(":")
+
+ def join_id(self, parsed_id):
+ coords, flags = parsed_id
+ return "@%s%s%s" % (":".join(coords), self.sep, ":".join(flags))
+
+ def get_read_number(self, parsed_id):
+ return int(parsed_id[1][0])
+
+ def set_read_number(self, parsed_id, n):
+ parsed_id[1][0] = "%d" % n
+
+ def get_paired_identifier(self, read):
+ t = self.parse_id(read.identifier)
+ n = self.get_read_number(t)
+ if n == 1:
+ pn = 2
+ elif n == 2:
+ pn = 1
+ else:
+ raise RuntimeError("Unknown read number '%d'" % n)
+ self.set_read_number(t, pn)
+ return self.join_id(t)
+
+
+class FastqJoiner(fq.fastqJoiner):
+
+ def __init__(self, format, force_quality_encoding=None, sep="\t"):
+ super(FastqJoiner, self).__init__(format, force_quality_encoding)
+ self.id_manager = IDManager(sep)
+
+ def join(self, read1, read2):
+ force_quality_encoding = self.force_quality_encoding
+ if not force_quality_encoding:
+ if read1.is_ascii_encoded():
+ force_quality_encoding = 'ascii'
+ else:
+ force_quality_encoding = 'decimal'
+ read1 = read1.convert_read_to_format(
+ self.format, force_quality_encoding=force_quality_encoding
+ )
+ read2 = read2.convert_read_to_format(
+ self.format, force_quality_encoding=force_quality_encoding
+ )
+ #--
+ t1, t2 = [
+ self.id_manager.parse_id(r.identifier) for r in (read1, read2)
+ ]
+ if self.id_manager.get_read_number(t1) == 2:
+ if not self.id_manager.get_read_number(t2) == 1:
+ raise RuntimeError("input files are not from mated pairs")
+ read1, read2 = read2, read1
+ t1, t2 = t2, t1
+ #--
+ rval = fq.FASTQ_FORMATS[self.format]()
+ rval.identifier = read1.identifier
+ rval.description = "+"
+ if len(read1.description) > 1:
+ rval.description += rval.identifier[1:]
+ if rval.sequence_space == 'color':
+ # convert to nuc space, join, then convert back
+ rval.sequence = rval.convert_base_to_color_space(
+ read1.convert_color_to_base_space(read1.sequence) +
+ read2.convert_color_to_base_space(read2.sequence)
+ )
+ else:
+ rval.sequence = read1.sequence + read2.sequence
+ if force_quality_encoding == 'ascii':
+ rval.quality = read1.quality + read2.quality
+ else:
+ rval.quality = "%s %s" % (
+ read1.quality.strip(), read2.quality.strip()
+ )
+ return rval
+
+ def get_paired_identifier(self, read):
+ return self.id_manager.get_paired_identifier(read)
+
+
+def sniff_sep(fastq_fn):
+ header = ""
+ with open(fastq_fn) as f:
+ while header == "":
+ try:
+ header = f.next().strip()
+ except StopIteration:
+ raise RuntimeError("%r: empty file" % (fastq_fn,))
+ return re.search(r"\s", header).group()
+
def main():
- #Read command line arguments
input1_filename = sys.argv[1]
input1_type = sys.argv[2] or 'sanger'
input2_filename = sys.argv[3]
input2_type = sys.argv[4] or 'sanger'
output_filename = sys.argv[5]
-
+ fastq_style = sys.argv[6] or 'old'
+ #--
if input1_type != input2_type:
- print "WARNING: You are trying to join files of two different types: %s and %s." % ( input1_type, input2_type )
-
- input2 = fastqNamedReader( open( input2_filename, 'rb' ), input2_type )
- joiner = fastqJoiner( input1_type )
- out = fastqWriter( open( output_filename, 'wb' ), format = input1_type )
-
+ print "WARNING: trying to join files of different types: %s and %s" % (
+ input1_type, input2_type
+ )
+ if fastq_style == 'new':
+ sep = sniff_sep(input1_filename)
+ joiner = FastqJoiner(input1_type, sep=sep)
+ else:
+ joiner = fq.fastqJoiner(input1_type)
+ #--
+ input2 = fq.fastqNamedReader(open(input2_filename, 'rb'), input2_type)
+ out = fq.fastqWriter(open(output_filename, 'wb'), format=input1_type)
i = None
skip_count = 0
- for i, fastq_read in enumerate( fastqReader( open( input1_filename, 'rb' ), format = input1_type ) ):
- identifier = joiner.get_paired_identifier( fastq_read )
- fastq_paired = input2.get( identifier )
+ for i, fastq_read in enumerate(fq.fastqReader(
+ open(input1_filename, 'rb' ), format=input1_type
+ )):
+ identifier = joiner.get_paired_identifier(fastq_read)
+ fastq_paired = input2.get(identifier)
if fastq_paired is None:
skip_count += 1
else:
- out.write( joiner.join( fastq_read, fastq_paired ) )
+ out.write(joiner.join(fastq_read, fastq_paired))
out.close()
-
if i is None:
- print "Your file contains no valid FASTQ reads."
+ print "Your file contains no valid FASTQ reads"
else:
print input2.has_data()
- print 'Joined %s of %s read pairs (%.2f%%).' % ( i - skip_count + 1, i + 1, float( i - skip_count + 1 ) / float( i + 1 ) * 100.0 )
+ print 'Joined %s of %s read pairs (%.2f%%)' % (
+ i - skip_count + 1, i + 1, (i - skip_count + 1) / (i + 1) * 100.0
+ )
+
if __name__ == "__main__":
main()
diff -r af91c734dbb76b8c0e3166c7471370f9ba8260b5 -r e232573b7c629a94ab60e6b615ecedf156913308 tools/fastq/fastq_paired_end_joiner.xml
--- a/tools/fastq/fastq_paired_end_joiner.xml
+++ b/tools/fastq/fastq_paired_end_joiner.xml
@@ -1,9 +1,13 @@
-<tool id="fastq_paired_end_joiner" name="FASTQ joiner" version="1.0.0">
+<tool id="fastq_paired_end_joiner" name="FASTQ joiner" version="2.0.0"><description>on paired end reads</description>
- <command interpreter="python">fastq_paired_end_joiner.py '$input1_file' '${input1_file.extension[len( 'fastq' ):]}' '$input2_file' '${input2_file.extension[len( 'fastq' ):]}' '$output_file'</command>
+ <command interpreter="python">fastq_paired_end_joiner.py '$input1_file' '${input1_file.extension[len( 'fastq' ):]}' '$input2_file' '${input2_file.extension[len( 'fastq' ):]}' '$output_file' '$style'</command><inputs><param name="input1_file" type="data" format="fastqsanger,fastqcssanger" label="Left-hand Reads" /><param name="input2_file" type="data" format="fastqsanger,fastqcssanger" label="Right-hand Reads" />
+ <param name="style" type="select" label="FASTQ Header Style">
+ <option value="old" selected="true">old</option>
+ <option value="new">new</option>
+ </param></inputs><outputs><data name="output_file" format="input" />
@@ -18,14 +22,19 @@
<help>
**What it does**
-This tool joins paired end FASTQ reads from two separate files into a single read in one file. The join is performed using sequence identifiers, allowing the two files to contain differing ordering. If a sequence identifier does not appear in both files, it is excluded from the output.
-
-Sequence identifiers with /1 and /2 appended override the left-hand and right-hand designation; i.e. if the reads end with /1 and /2, the read containing /1 will be used as the left-hand read and the read containing /2 will be used as the right-hand read. Sequences without this designation will follow the left-hand and right-hand settings set by the user.
+This tool joins paired end FASTQ reads from two separate files into a
+single read in one file. The join is performed using sequence
+identifiers, allowing the two files to contain differing ordering. If
+a sequence identifier does not appear in both files, it is excluded
+from the output.
-----
**Input formats**
+Both old and new (from recent Illumina software) style FASTQ headers
+are supported. The following example uses the "old" style.
+
Left-hand Read::
@HWI-EAS91_1_30788AAXX:7:21:1542:1758/1
@@ -53,10 +62,26 @@
------
-**Citation**
+**The "new" style**
-If you use this tool, please cite `Blankenberg D, Gordon A, Von Kuster G, Coraor N, Taylor J, Nekrutenko A; Galaxy Team. Manipulation of FASTQ data with Galaxy. Bioinformatics. 2010 Jul 15;26(14):1783-5. <http://www.ncbi.nlm.nih.gov/pubmed/20562416>`_
+Recent Illumina FASTQ headers are structured as follows::
+ @COORDS FLAGS
+ COORDS = INSTRUMENT:RUN_#:FLOWCELL_ID:LANE:TILE:X:Y
+ FLAGS = READ:IS_FILTERED:CONTROL_NUMBER:INDEX_SEQUENCE
+where the whitespace character between COORDS and FLAGS can be either
+a space or a tab.
+
+------
+
+**Credits**
+
+This is an extended version (adds support for "new" style FASTQ headers)
+of D. Blankenberg's fastq joiner:
+
+`Blankenberg D, Gordon A, Von Kuster G, Coraor N, Taylor J, Nekrutenko A; Galaxy Team. Manipulation of FASTQ data with Galaxy. Bioinformatics. 2010 Jul 15;26(14):1783-5. <http://www.ncbi.nlm.nih.gov/pubmed/20562416>`_
+
+New style header support added by Simone Leo <simone.leo(a)crs4.it>
</help></tool>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Corrections to statistics for the tool shed's install and test runs and successful installation results for repositories of type tool_dependency_definition now reported to the tool shed.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a950019c26a9/
Changeset: a950019c26a9
User: greg
Date: 2013-12-18 22:20:36
Summary: Corrections to statistics for the tool shed's install and test runs and successful installation results for repositories of type tool_dependency_definition now reported to the tool shed.
Affected #: 5 files
diff -r f1cb7f07af2c923d3f3be77a0ae0ccb25c6453a7 -r a950019c26a9189f145d4da87c589f64babaa8e2 install_and_test_tool_shed_repositories.sh
--- a/install_and_test_tool_shed_repositories.sh
+++ b/install_and_test_tool_shed_repositories.sh
@@ -70,12 +70,16 @@
done
case $which in
- dependencies)
+ # Use "-w tool_dependency_definitions" when you want to test repositories of type tool_dependency_definition.
+ tool_dependency_definitions)
test_tool_dependency_definitions
;;
- tools)
+ # 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
diff -r f1cb7f07af2c923d3f3be77a0ae0ccb25c6453a7 -r a950019c26a9189f145d4da87c589f64babaa8e2 lib/tool_shed/scripts/check_tool_dependency_definition_repositories.py
--- a/lib/tool_shed/scripts/check_tool_dependency_definition_repositories.py
+++ b/lib/tool_shed/scripts/check_tool_dependency_definition_repositories.py
@@ -89,8 +89,9 @@
now = strftime( "%Y-%m-%d %H:%M:%S" )
print "#############################################################################"
- print "# %s - Validating repositories of type %s on %s..." % ( now, TOOL_DEPENDENCY_DEFINITION, config_parser.get( config_section, 'host' ) )
- print "# This tool shed is configured to listen on %s:%s" % ( config_parser.get( config_section, 'host' ), config_parser.get( config_section, 'port' ) )
+ print "# %s - Validating repositories of type %s" % ( now, TOOL_DEPENDENCY_DEFINITION )
+ print "# This tool shed is configured to listen on %s:%s" % ( config_parser.get( config_section, 'host' ),
+ config_parser.get( config_section, 'port' ) )
app = RepositoriesApplication( config )
diff -r f1cb7f07af2c923d3f3be77a0ae0ccb25c6453a7 -r a950019c26a9189f145d4da87c589f64babaa8e2 test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -225,7 +225,8 @@
return suc.INITIAL_CHANGELOG_HASH, error_message
def get_missing_tool_dependencies( repository ):
- log.debug( 'Checking %s repository %s for missing tool dependencies.' % ( repository.status, repository.name ) )
+ log.debug( 'Checking revision %s of repository %s owned by %s for missing tool dependencies.' % \
+ ( str( repository.changeset_revision ), str( repository.name ), str( repository.changeset_revision ) ) )
missing_tool_dependencies = repository.missing_tool_dependencies
for tool_dependency in repository.tool_dependencies:
log.debug( 'Tool dependency %s version %s has status %s.' % ( tool_dependency.name, tool_dependency.version, tool_dependency.status ) )
@@ -423,12 +424,13 @@
return global_conf
def handle_missing_dependencies( app, repository, missing_tool_dependencies, repository_dict, tool_test_results_dicts,
- tool_test_results_dict, results_dict, can_update_tool_shed ):
+ tool_test_results_dict, can_update_tool_shed ):
"""Handle missing repository or tool dependencies for an installed repository."""
# If a tool dependency fails to install correctly, this should be considered an installation error,
# and functional tests should be skipped, since the tool dependency needs to be correctly installed
# for the test to be considered reliable.
- log.debug( 'The following dependencies of this repository are missing, so skipping functional tests.' )
+ log.debug( 'The following dependencies of revision %s of repository %s owned by %s are missing.' % \
+ ( str( repository.changeset_revision ), str( repository.name ), str( repository.owner ) ) )
# In keeping with the standard display layout, add the error message to the dict for each tool individually.
for dependency in repository.missing_tool_dependencies:
name = str( dependency.name )
@@ -436,24 +438,25 @@
version = str( dependency.version )
error_message = unicodify( dependency.error_message )
log.debug( 'Missing tool dependency %s of type %s version %s: %s' % ( name, type, version, error_message ) )
- test_result = dict( type=dependency.type,
- name=dependency.name,
- version=dependency.version,
- error_message=dependency.error_message )
- tool_test_results_dict[ 'installation_errors' ][ 'tool_dependencies' ].append( test_result )
+ missing_tool_dependency_info_dict = dict( type=type,
+ name=name,
+ version=version,
+ error_message=error_message )
+ tool_test_results_dict[ 'installation_errors' ][ 'tool_dependencies' ].append( missing_tool_dependency_info_dict )
for dependency in repository.missing_repository_dependencies:
+ tool_shed = str( dependency.tool_shed )
name = str( dependency.name )
owner = str( dependency.owner )
changeset_revision = str( dependency.changeset_revision )
error_message = unicodify( dependency.error_message )
log.debug( 'Missing repository dependency %s changeset revision %s owned by %s: %s' % \
( name, changeset_revision, owner, error_message ) )
- test_result = dict( tool_shed=dependency.tool_shed,
- name=dependency.name,
- owner=dependency.owner,
- changeset_revision=dependency.changeset_revision,
- error_message=dependency.error_message )
- tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ].append( test_result )
+ missing_repository_dependency_info_dict = dict( tool_shed=tool_shed,
+ name=name,
+ owner=owner,
+ changeset_revision=changeset_revision,
+ error_message=error_message )
+ tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ].append( missing_repository_dependency_info_dict )
# Record the status of this repository in the tool shed.
params = dict( tools_functionally_correct=False,
do_not_test=False,
@@ -465,26 +468,25 @@
repository_dict,
params,
can_update_tool_shed )
- # Since this repository is missing components we do not want to test it, uninstall it.
+ # Uninstall this repository since it is missing dependencies.
uninstall_repository_and_repository_dependencies( app, repository_dict )
- results_dict[ 'repositories_failed_install' ].append( dict( name=str( repository.name ),
- owner=str( repository.owner ),
- changeset_revision=str( repository.changeset_revision ) ) )
- return results_dict
-def initialize_results_dict( test_framework ):
+def initialize_install_and_test_statistics_dict( test_framework ):
# Initialize a dictionary for the summary that will be printed to stdout.
- results_dict = {}
+ install_and_test_statistics_dict = {}
if test_framework == REPOSITORIES_WITH_TOOLS:
- results_dict[ 'total_repositories_tested' ] = 0
- results_dict[ 'all_tests_passed' ] = []
- results_dict[ 'at_least_one_test_failed' ] = []
- results_dict[ 'repositories_failed_install' ] = []
+ install_and_test_statistics_dict[ 'total_repositories_processed' ] = 0
+ install_and_test_statistics_dict[ 'all_tests_passed' ] = []
+ install_and_test_statistics_dict[ 'at_least_one_test_failed' ] = []
+ install_and_test_statistics_dict[ 'successful_installations' ] = []
+ install_and_test_statistics_dict[ 'repositories_with_installation_error' ] = []
+ install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ] = []
elif test_framework == TOOL_DEPENDENCY_DEFINITIONS:
- results_dict[ 'total_repositories_installed' ] = 0
- results_dict[ 'repositories_with_installation_error' ] = 0
- results_dict[ 'tool_dependencies_with_installation_error' ] = 0
- return results_dict
+ install_and_test_statistics_dict[ 'total_repositories_processed' ] = 0
+ install_and_test_statistics_dict[ 'successful_installations' ] = []
+ install_and_test_statistics_dict[ 'repositories_with_installation_error' ] = []
+ install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ] = []
+ return install_and_test_statistics_dict
def initialize_tool_tests_results_dict( app, tool_test_results_dict ):
test_environment_dict = tool_test_results_dict.get( 'test_environment', {} )
diff -r f1cb7f07af2c923d3f3be77a0ae0ccb25c6453a7 -r a950019c26a9189f145d4da87c589f64babaa8e2 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -134,10 +134,10 @@
return tool_id, tool_version
def install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file ):
- results_dict = install_and_test_base_util.initialize_results_dict( test_framework )
+ install_and_test_statistics_dict = install_and_test_base_util.initialize_install_and_test_statistics_dict( test_framework )
error_message = ''
# Initialize a dictionary for the summary that will be printed to stdout.
- total_repositories_tested = results_dict[ 'total_repositories_tested' ]
+ total_repositories_processed = install_and_test_statistics_dict[ 'total_repositories_processed' ]
repositories_to_install, error_message = \
install_and_test_base_util.get_repositories_to_install( install_and_test_base_util.galaxy_tool_shed_url, test_framework )
if error_message:
@@ -213,8 +213,7 @@
break
if this_repository_is_in_the_exclude_list:
tool_test_results_dict[ 'not_tested' ] = dict( reason=skip_reason )
- params = dict( tools_functionally_correct=False,
- do_not_test=False )
+ params = dict( do_not_test=False )
# TODO: do something useful with response_dict
response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
tool_test_results_dicts,
@@ -223,30 +222,29 @@
params,
can_update_tool_shed )
log.debug( "Not testing revision %s of repository %s owned by %s because it is in the exclude list for this test run." % \
- ( changeset_revision, name, owner ) )
+ ( changeset_revision, name, owner ) )
else:
tool_test_results_dict = install_and_test_base_util.initialize_tool_tests_results_dict( app, tool_test_results_dict )
# Explicitly clear tests from twill's test environment.
remove_generated_tests( app )
# Proceed with installing repositories and testing contained tools.
repository, error_message = install_and_test_base_util.install_repository( app, repository_dict )
+ install_and_test_statistics_dict[ 'total_repositories_processed' ] += 1
if error_message:
+ # The repository installation failed.
+ log.debug( 'Installation failed for revision %s of repository %s owned by %s.' % \
+ ( changeset_revision, name, owner ) )
+ install_and_test_statistics_dict[ 'repositories_with_installation_error' ].append( repository_identifier_dict )
tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = error_message
# Even if the repository failed to install, execute the uninstall method, in case a dependency did succeed.
- log.debug( 'Attempting to uninstall repository %s owned by %s.' % ( name, owner ) )
+ log.debug( 'Attempting to uninstall revision %s of repository %s owned by %s.' % ( changeset_revision, name, owner ) )
try:
repository = test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
except Exception, e:
- error_message = 'Unable to find installed repository %s owned by %s: %s.' % ( name, owner, str( e ) )
+ error_message = 'Unable to find revision %s of repository %s owned by %s: %s.' % \
+ ( changeset_revision, name, owner, str( e ) )
log.exception( error_message )
- test_result = dict( tool_shed=install_and_test_base_util.galaxy_tool_shed_url,
- name=name,
- owner=owner,
- changeset_revision=changeset_revision,
- error_message=error_message )
- tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ].append( test_result )
- params = dict( tools_functionally_correct=False,
- test_install_error=True,
+ params = dict( test_install_error=True,
do_not_test=False )
# TODO: do something useful with response_dict
response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
@@ -263,18 +261,23 @@
( changeset_revision, name, owner, str( e ) ) )
# Clean out any generated tests. This is necessary for Twill.
remove_generated_tests( app )
- results_dict[ 'repositories_failed_install' ].append( repository_identifier_dict )
- log.debug( 'Repository %s failed to install correctly.' % str( name ) )
+ install_and_test_statistics_dict[ 'repositories_with_installation_error' ].append( repository_identifier_dict )
+ log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
+ ( changeset_revision, name, owner ) )
else:
- # Configure and run functional tests for this repository. This is equivalent to sh run_functional_tests.sh -installed
- remove_install_tests()
- log.debug( 'Installation of %s succeeded, running all defined functional tests.' % str( repository.name ) )
- # Generate the shed_tools_dict that specifies the location of test data contained within this repository. If the repository
- # does not have a test-data directory, this will return has_test_data = False, and we will set the do_not_test flag to True,
- # and the tools_functionally_correct flag to False, as well as updating tool_test_results.
- file( galaxy_shed_tools_dict, 'w' ).write( to_json_string( {} ) )
- has_test_data, shed_tools_dict = parse_tool_panel_config( galaxy_shed_tool_conf_file,
- from_json_string( file( galaxy_shed_tools_dict, 'r' ).read() ) )
+ log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
+ ( changeset_revision, name, owner ) )
+ # Keep statistics for this repository's tool dependencies that resulted in installation errors.
+ for missing_tool_dependency in repository.missing_tool_dependencies:
+ name = str( missing_tool_dependency.name )
+ type = str( missing_tool_dependency.type )
+ version = str( missing_tool_dependency.version )
+ error_message = unicodify( missing_tool_dependency.error_message )
+ missing_tool_dependency_info_dict = dict( type=type,
+ name=name,
+ version=version,
+ error_message=error_message )
+ install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ].append( missing_tool_dependency_info_dict )
# Add an empty 'missing_test_results' entry if it is missing from the tool_test_results_dict. The
# ~/tool_shed/scripts/check_repositories_for_functional_tests.py will have entered information in the
# 'missing_test_components' entry of the tool_test_results_dict dictionary for repositories that are
@@ -283,28 +286,40 @@
tool_test_results_dict[ 'missing_test_components' ] = []
missing_tool_dependencies = install_and_test_base_util.get_missing_tool_dependencies( repository )
if missing_tool_dependencies or repository.missing_repository_dependencies:
- results_dict = install_and_test_base_util.handle_missing_dependencies( app,
- repository,
- missing_tool_dependencies,
- repository_dict,
- tool_test_results_dicts,
- tool_test_results_dict,
- results_dict,
- can_update_tool_shed )
+ install_and_test_statistics_dict = \
+ install_and_test_base_util.handle_missing_dependencies( app,
+ repository,
+ missing_tool_dependencies,
+ repository_dict,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ install_and_test_statistics_dict,
+ can_update_tool_shed )
# Set the test_toolbox.toolbox module-level variable to the new app.toolbox.
test_toolbox.toolbox = app.toolbox
else:
+ # This repository and all of its dependencies were successfully installed.
+ install_and_test_statistics_dict[ 'successful_installations' ].append( repository_identifier_dict )
+ # Configure and run functional tests for this repository. This is equivalent to sh run_functional_tests.sh -installed
+ remove_install_tests()
+ log.debug( 'Installation of %s succeeded, running all defined functional tests.' % str( repository.name ) )
+ # Generate the shed_tools_dict that specifies the location of test data contained within this repository. If the repository
+ # does not have a test-data directory, this will return has_test_data = False, and we will set the do_not_test flag to True,
+ # and the tools_functionally_correct flag to False, as well as updating tool_test_results.
+ file( galaxy_shed_tools_dict, 'w' ).write( to_json_string( {} ) )
+ has_test_data, shed_tools_dict = parse_tool_panel_config( galaxy_shed_tool_conf_file,
+ from_json_string( file( galaxy_shed_tools_dict, 'r' ).read() ) )
# If the repository has a test-data directory we write the generated shed_tools_dict to a file, so the functional
# test framework can find it.
file( galaxy_shed_tools_dict, 'w' ).write( to_json_string( shed_tools_dict ) )
log.debug( 'Saved generated shed_tools_dict to %s\nContents: %s' % ( str( galaxy_shed_tools_dict ), str( shed_tools_dict ) ) )
try:
- results_dict = test_repository_tools( app,
- repository,
- repository_dict,
- tool_test_results_dicts,
- tool_test_results_dict,
- results_dict )
+ install_and_test_statistics_dict = test_repository_tools( app,
+ repository,
+ repository_dict,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ install_and_test_statistics_dict )
except Exception, e:
exception_message = 'Error executing tests for repository %s: %s' % ( name, str( e ) )
log.exception( exception_message )
@@ -321,10 +336,8 @@
repository_dict,
params,
can_update_tool_shed )
- results_dict[ 'at_least_one_test_failed' ].append( repository_identifier_dict )
- total_repositories_tested += 1
- results_dict[ 'total_repositories_tested' ] = total_repositories_tested
- return results_dict, error_message
+ install_and_test_statistics_dict[ 'at_least_one_test_failed' ].append( repository_identifier_dict )
+ return install_and_test_statistics_dict, error_message
def main():
if install_and_test_base_util.tool_shed_api_key is None:
@@ -535,35 +548,45 @@
persist=False )
now = time.strftime( "%Y-%m-%d %H:%M:%S" )
print "####################################################################################"
- print "# %s - running repository installation and testing script." % now
+ print "# %s - installation script for repositories containing tools started." % now
+ if not can_update_tool_shed:
+ print "# This run will not update the Tool Shed database."
print "####################################################################################"
- results_dict, error_message = install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file )
+ install_and_test_statistics_dict, error_message = \
+ install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file )
if error_message:
log.debug( error_message )
else:
- total_repositories_tested = results_dict[ 'total_repositories_tested' ]
- all_tests_passed = results_dict[ 'all_tests_passed' ]
- at_least_one_test_failed = results_dict[ 'at_least_one_test_failed' ]
- repositories_failed_install = results_dict[ 'repositories_failed_install' ]
+ total_repositories_processed = install_and_test_statistics_dict[ 'total_repositories_processed' ]
+ all_tests_passed = install_and_test_statistics_dict[ 'all_tests_passed' ]
+ at_least_one_test_failed = install_and_test_statistics_dict[ 'at_least_one_test_failed' ]
+ successful_installations = install_and_test_statistics_dict[ 'successful_installations' ]
+ repositories_with_installation_error = install_and_test_statistics_dict[ 'repositories_with_installation_error' ]
+ tool_dependencies_with_installation_error = install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ]
now = time.strftime( "%Y-%m-%d %H:%M:%S" )
print "####################################################################################"
- print "# %s - repository installation and testing script completed." % now
- print "# Repository revisions tested: %s" % str( total_repositories_tested )
- if not can_update_tool_shed:
- print "# This run will not update the Tool Shed database."
- if total_repositories_tested > 0:
- if all_tests_passed:
- print '# ----------------------------------------------------------------------------------'
- print "# %d repositories successfully passed all functional tests:" % len( all_tests_passed )
- install_and_test_base_util.show_summary_output( all_tests_passed )
- if at_least_one_test_failed:
- print '# ----------------------------------------------------------------------------------'
- print "# %d repositories failed at least 1 functional test:" % len( at_least_one_test_failed )
- install_and_test_base_util.show_summary_output( at_least_one_test_failed )
- if repositories_failed_install:
- print '# ----------------------------------------------------------------------------------'
- print "# %d repositories have installation errors:" % len( repositories_failed_install )
- install_and_test_base_util.show_summary_output( repositories_failed_install )
+ print "# %s - installation script for repositories containing tools completed." % now
+ print "# Repository revisions processed: %s" % str( total_repositories_processed )
+ if successful_installations:
+ print "# ----------------------------------------------------------------------------------"
+ print "# The following %d revisions with all dependencies were successfully installed:" % len( successful_installations )
+ install_and_test_base_util.show_summary_output( successful_installations )
+ if all_tests_passed:
+ print '# ----------------------------------------------------------------------------------'
+ print "# %d repositories successfully passed all functional tests:" % len( all_tests_passed )
+ install_and_test_base_util.show_summary_output( all_tests_passed )
+ if at_least_one_test_failed:
+ print '# ----------------------------------------------------------------------------------'
+ print "# %d repositories failed at least 1 functional test:" % len( at_least_one_test_failed )
+ install_and_test_base_util.show_summary_output( at_least_one_test_failed )
+ if repositories_with_installation_error:
+ print '# ----------------------------------------------------------------------------------'
+ print "# %d repositories have installation errors:" % len( repositories_with_installation_error )
+ install_and_test_base_util.show_summary_output( repositories_with_installation_error )
+ if tool_dependencies_with_installation_error:
+ print "# ----------------------------------------------------------------------------------"
+ print "# The following %d tool dependencies have installation errors:" % len( tool_dependencies_with_installation_error )
+ install_and_test_base_util.show_summary_output( tool_dependencies_with_installation_error )
print "####################################################################################"
log.debug( "Shutting down..." )
# Gracefully shut down the embedded web server and UniverseApplication.
@@ -632,7 +655,8 @@
# Now delete the tests found in the previous loop.
del test_install_repositories.__dict__[ key ]
-def test_repository_tools( app, repository, repository_dict, tool_test_results_dicts, tool_test_results_dict, results_dict ):
+def test_repository_tools( app, repository, repository_dict, tool_test_results_dicts, tool_test_results_dict,
+ install_and_test_statistics_dict ):
"""Test tools contained in the received repository."""
name = str( repository.name )
owner = str( repository.owner )
@@ -685,7 +709,7 @@
failed_test_dicts = get_failed_test_dicts( result, from_tool_test=True )
tool_test_results_dict[ 'failed_tests' ] = failed_test_dicts
failed_repository_dict = repository_identifier_dict
- results_dict[ 'at_least_one_test_failed' ].append( failed_repository_dict )
+ install_and_test_statistics_dict[ 'at_least_one_test_failed' ].append( failed_repository_dict )
set_do_not_test = not is_latest_downloadable_revision( install_and_test_base_util.galaxy_tool_shed_url, repository_dict )
params = dict( tools_functionally_correct=False,
test_install_error=False,
@@ -708,7 +732,7 @@
remove_generated_tests( app )
# Set the test_toolbox.toolbox module-level variable to the new app.toolbox.
test_toolbox.toolbox = app.toolbox
- return results_dict
+ return install_and_test_statistics_dict
if __name__ == "__main__":
# The tool_test_results_dict should always have the following structure:
diff -r f1cb7f07af2c923d3f3be77a0ae0ccb25c6453a7 -r a950019c26a9189f145d4da87c589f64babaa8e2 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -73,10 +73,10 @@
test_framework = install_and_test_base_util.TOOL_DEPENDENCY_DEFINITIONS
def install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file ):
- results_dict = install_and_test_base_util.initialize_results_dict( test_framework )
+ install_and_test_statistics_dict = install_and_test_base_util.initialize_install_and_test_statistics_dict( test_framework )
error_message = ''
# Initialize a dictionary for the summary that will be printed to stdout.
- total_repositories_installed = results_dict[ 'total_repositories_installed' ]
+ total_repositories_processed = install_and_test_statistics_dict[ 'total_repositories_processed' ]
repositories_to_install, error_message = \
install_and_test_base_util.get_repositories_to_install( install_and_test_base_util.galaxy_tool_shed_url, test_framework )
if error_message:
@@ -161,27 +161,26 @@
params,
can_update_tool_shed )
log.debug( "Not testing revision %s of repository %s owned by %s because it is in the exclude list for this test run." % \
- ( changeset_revision, name, owner ) )
+ ( changeset_revision, name, owner ) )
else:
tool_test_results_dict = install_and_test_base_util.initialize_tool_tests_results_dict( app, tool_test_results_dict )
repository, error_message = install_and_test_base_util.install_repository( app, repository_dict )
+ install_and_test_statistics_dict[ 'total_repositories_processed' ] += 1
if error_message:
+ # The repository installation failed.
+ log.debug( 'Installation failed for revision %s of repository %s owned by %s.' % \
+ ( changeset_revision, name, owner ) )
+ install_and_test_statistics_dict[ 'repositories_with_installation_error' ].append( repository_identifier_dict )
tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = error_message
# Even if the repository failed to install, execute the uninstall method, in case a dependency did succeed.
- log.debug( 'Attempting to uninstall repository %s owned by %s.' % ( name, owner ) )
+ log.debug( 'Attempting to uninstall revision %s of repository %s owned by %s.' % ( changeset_revision, name, owner ) )
try:
repository = test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
except Exception, e:
- error_message = 'Unable to find installed repository %s owned by %s: %s.' % ( name, owner, str( e ) )
+ error_message = 'Unable to find revision %s of repository %s owned by %s: %s.' % \
+ ( changeset_revision, name, owner, str( e ) )
log.exception( error_message )
- test_result = dict( tool_shed=install_and_test_base_util.galaxy_tool_shed_url,
- name=name,
- owner=owner,
- changeset_revision=changeset_revision,
- error_message=error_message )
- tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ].append( test_result )
- params = dict( tools_functionally_correct=False,
- test_install_error=True,
+ params = dict( test_install_error=True,
do_not_test=False )
# TODO: do something useful with response_dict
response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
@@ -196,22 +195,50 @@
except Exception, e:
log.exception( 'Error attempting to uninstall revision %s of repository %s owned by %s: %s' % \
( changeset_revision, name, owner, str( e ) ) )
- results_dict[ 'repositories_with_installation_error' ].append( repository_identifier_dict )
- log.debug( 'Repository %s failed to install correctly.' % str( name ) )
+ log.debug( 'Installation failed for revision %s of repository %s owned by %s.' % \
+ ( changeset_revision, name, owner ) )
else:
- log.debug( 'Installation of %s succeeded.' % str( repository.name ) )
+ log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
+ ( changeset_revision, name, owner ) )
+ # Keep statistics for this repository's tool dependencies that resulted in installation errors.
+ for missing_tool_dependency in repository.missing_tool_dependencies:
+ name = str( missing_tool_dependency.name )
+ type = str( missing_tool_dependency.type )
+ version = str( missing_tool_dependency.version )
+ error_message = unicodify( missing_tool_dependency.error_message )
+ missing_tool_dependency_info_dict = dict( type=type,
+ name=name,
+ version=version,
+ error_message=error_message )
+ install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ].append( missing_tool_dependency_info_dict )
+ # Attempt to uninstall this repository and all of its dependencies if its repository dependencies or
+ # tool dependencies resulted in an installation error.
missing_tool_dependencies = install_and_test_base_util.get_missing_tool_dependencies( repository )
if missing_tool_dependencies or repository.missing_repository_dependencies:
- results_dict = install_and_test_base_util.handle_missing_dependencies( app,
- repository,
- missing_tool_dependencies,
- repository_dict,
- tool_test_results_dicts,
- tool_test_results_dict,
- results_dict,
- can_update_tool_shed )
- results_dict[ 'total_repositories_installed' ] = total_repositories_installed
- return results_dict, error_message
+ install_and_test_base_util.handle_missing_dependencies( app,
+ repository,
+ missing_tool_dependencies,
+ repository_dict,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ install_and_test_statistics_dict,
+ can_update_tool_shed )
+ else:
+ # This repository and all of its dependencies were successfully installed.
+ install_and_test_statistics_dict[ 'successful_installations' ].append( repository_identifier_dict )
+ tool_test_results_dict[ 'passed_tests' ].append( repository_identifier_dict )
+ params = dict( test_install_error=False,
+ do_not_test=False )
+ # TODO: do something useful with response_dict
+ response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
+
+ install_and_test_statistics_dict[ 'total_repositories_processed' ] = total_repositories_processed
+ return install_and_test_statistics_dict, error_message
def main():
if install_and_test_base_util.tool_shed_api_key is None:
@@ -419,28 +446,34 @@
persist=False )
now = time.strftime( "%Y-%m-%d %H:%M:%S" )
print "####################################################################################"
- print "# %s - running repository installation and testing script." % now
+ print "# %s - installation script for repositories of type tool_dependency_definition started." % now
+ if not can_update_tool_shed:
+ print "# This run will not update the Tool Shed database."
print "####################################################################################"
- results_dict, error_message = install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file )
+ install_and_test_statistics_dict, error_message = \
+ install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file )
if error_message:
log.debug( error_message )
else:
- total_repositories_installed = results_dict[ 'total_repositories_installed' ]
- repositories_with_installation_error = results_dict[ 'repositories_with_installation_error' ]
- tool_dependencies_with_installation_error = results_dict[ 'tool_dependencies_with_installation_error' ]
+ total_repositories_processed = install_and_test_statistics_dict[ 'total_repositories_processed' ]
+ successful_installations = install_and_test_statistics_dict[ 'successful_installations' ]
+ repositories_with_installation_error = install_and_test_statistics_dict[ 'repositories_with_installation_error' ]
+ tool_dependencies_with_installation_error = install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ]
now = time.strftime( "%Y-%m-%d %H:%M:%S" )
print "####################################################################################"
- print "# %s - repository installation and testing script completed." % now
- print "# Repository revisions tested: %s" % str( total_repositories_installed )
- if not can_update_tool_shed:
- print "# This run will not update the Tool Shed database."
+ print "# %s - installation script for repositories of type tool_dependency_definition completed." % now
+ print "# Repository revisions processed: %s" % str( total_repositories_processed )
+ if successful_installations:
+ print "# ----------------------------------------------------------------------------------"
+ print "# The following %d revisions with all dependencies were successfully installed:" % len( successful_installations )
+ install_and_test_base_util.show_summary_output( successful_installations )
if repositories_with_installation_error:
- print '# ----------------------------------------------------------------------------------'
- print "# %d repositories have installation errors:" % len( repositories_with_installation_error )
+ print "# ----------------------------------------------------------------------------------"
+ print "# The following %d revisions have installation errors:" % len( repositories_with_installation_error )
install_and_test_base_util.show_summary_output( repositories_with_installation_error )
if tool_dependencies_with_installation_error:
- print '# ----------------------------------------------------------------------------------'
- print "# %d tool dependencies have installation errors:" % len( tool_dependencies_with_installation_error )
+ print "# ----------------------------------------------------------------------------------"
+ print "# The following %d tool dependencies have installation errors:" % len( tool_dependencies_with_installation_error )
install_and_test_base_util.show_summary_output( tool_dependencies_with_installation_error )
print "####################################################################################"
log.debug( "Shutting down..." )
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/f1cb7f07af2c/
Changeset: f1cb7f07af2c
User: jmchilton
Date: 2013-12-18 21:59:11
Summary: Use get_history() in tools API controller.
Affected #: 1 file
diff -r 0b2676366a82d61b5e46f4cae6a235e643548f78 -r f1cb7f07af2c923d3f3be77a0ae0ccb25c6453a7 lib/galaxy/webapps/galaxy/api/tools.py
--- a/lib/galaxy/webapps/galaxy/api/tools.py
+++ b/lib/galaxy/webapps/galaxy/api/tools.py
@@ -1,7 +1,7 @@
import urllib
from galaxy import web, util
-from galaxy.web.base.controller import BaseAPIController, UsesHistoryDatasetAssociationMixin, UsesVisualizationMixin
+from galaxy.web.base.controller import BaseAPIController, UsesHistoryDatasetAssociationMixin, UsesVisualizationMixin, UsesHistoryMixin
from galaxy.visualization.genomes import GenomeRegion
from galaxy.util.json import to_json_string, from_json_string
from galaxy.visualization.data_providers.genome import *
@@ -10,7 +10,7 @@
log = logging.getLogger( __name__ )
-class ToolsController( BaseAPIController, UsesVisualizationMixin ):
+class ToolsController( BaseAPIController, UsesVisualizationMixin, UsesHistoryMixin ):
"""
RESTful controller for interactions with tools.
"""
@@ -86,8 +86,7 @@
# dataset upload.
history_id = payload.get("history_id", None)
if history_id:
- target_history = trans.sa_session.query(trans.app.model.History).get(
- trans.security.decode_id(history_id))
+ target_history = self.get_history( trans, history_id )
else:
target_history = None
https://bitbucket.org/galaxy/galaxy-central/commits/26da838b778c/
Changeset: 26da838b778c
Branch: stable
User: jmchilton
Date: 2013-12-18 21:59:11
Summary: Use get_history() in tools API controller.
Affected #: 1 file
diff -r e059c6607f34b603e92e85cf0113b7b9b9635571 -r 26da838b778c68a5577d4cca9836f6ef70b8ed1b lib/galaxy/webapps/galaxy/api/tools.py
--- a/lib/galaxy/webapps/galaxy/api/tools.py
+++ b/lib/galaxy/webapps/galaxy/api/tools.py
@@ -1,7 +1,7 @@
import urllib
from galaxy import web, util
-from galaxy.web.base.controller import BaseAPIController, UsesHistoryDatasetAssociationMixin, UsesVisualizationMixin
+from galaxy.web.base.controller import BaseAPIController, UsesHistoryDatasetAssociationMixin, UsesVisualizationMixin, UsesHistoryMixin
from galaxy.visualization.genomes import GenomeRegion
from galaxy.util.json import to_json_string, from_json_string
from galaxy.visualization.data_providers.genome import *
@@ -9,7 +9,7 @@
import logging
log = logging.getLogger( __name__ )
-class ToolsController( BaseAPIController, UsesVisualizationMixin ):
+class ToolsController( BaseAPIController, UsesVisualizationMixin, UsesHistoryMixin ):
"""
RESTful controller for interactions with tools.
"""
@@ -85,8 +85,7 @@
# dataset upload.
history_id = payload.get("history_id", None)
if history_id:
- target_history = trans.sa_session.query(trans.app.model.History).get(
- trans.security.decode_id(history_id))
+ target_history = self.get_history( trans, history_id )
else:
target_history = None
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/0bb5714cb801/
Changeset: 0bb5714cb801
User: lepsiobec
Date: 2013-12-18 19:59:20
Summary: aiding security flaw with downloading datasets in archives
Affected #: 1 file
diff -r cbd1387437ceba5be9b47dcbf9eb951086e410a1 -r 0bb5714cb8012515824fd3e435b683afd554cdd2 lib/galaxy/webapps/galaxy/controllers/library_common.py
--- a/lib/galaxy/webapps/galaxy/controllers/library_common.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library_common.py
@@ -18,7 +18,7 @@
from galaxy.util import inflector
from galaxy.util.json import to_json_string, from_json_string
from galaxy.util.streamball import StreamBall
-from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin, UsesExtendedMetadataMixin
+from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin, UsesExtendedMetadataMixin, UsesLibraryMixinItems
from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, build_select_field
from galaxy.model.orm import and_, eagerload_all
@@ -65,7 +65,7 @@
except:
pass
-class LibraryCommon( BaseUIController, UsesFormDefinitionsMixin, UsesExtendedMetadataMixin ):
+class LibraryCommon( BaseUIController, UsesFormDefinitionsMixin, UsesExtendedMetadataMixin, UsesLibraryMixinItems ):
@web.json
def library_item_updates( self, trans, ids=None, states=None ):
# Avoid caching
@@ -1750,7 +1750,8 @@
ldda_ids = util.listify( ldda_ids )
for ldda_id in ldda_ids:
try:
- ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( ldda_id ) )
+ # Load the ldda requested and check whether the user has access to them
+ ldda = self.get_library_dataset_dataset_association( trans, ldda_id )
assert not ldda.dataset.purged
lddas.append( ldda )
except:
https://bitbucket.org/galaxy/galaxy-central/commits/0b2676366a82/
Changeset: 0b2676366a82
User: lepsiobec
Date: 2013-12-18 20:08:19
Summary: Merge
Affected #: 1 file
diff -r 0bb5714cb8012515824fd3e435b683afd554cdd2 -r 0b2676366a82d61b5e46f4cae6a235e643548f78 install_and_test_tool_shed_repositories.sh
--- a/install_and_test_tool_shed_repositories.sh
+++ b/install_and_test_tool_shed_repositories.sh
@@ -13,6 +13,10 @@
# 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."
exit 1
@@ -39,14 +43,41 @@
fi
fi
-# 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_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 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
+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
+ dependencies)
+ test_tool_dependency_definitions
+ ;;
+ tools)
+ test_repositories_with_tools
+ ;;
+ *)
+ test_tool_dependency_definitions
+ test_repositories_with_tools
+ ;;
+esac
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: inithello: Add command line option to select which set of tests to run.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/9c17939bb282/
Changeset: 9c17939bb282
User: inithello
Date: 2013-12-18 19:53:18
Summary: Add command line option to select which set of tests to run.
Affected #: 1 file
diff -r cbd1387437ceba5be9b47dcbf9eb951086e410a1 -r 9c17939bb28242a1532daf2c24c2920786b3502f install_and_test_tool_shed_repositories.sh
--- a/install_and_test_tool_shed_repositories.sh
+++ b/install_and_test_tool_shed_repositories.sh
@@ -13,6 +13,10 @@
# 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."
exit 1
@@ -39,14 +43,41 @@
fi
fi
-# 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_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 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
+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
+ dependencies)
+ test_tool_dependency_definitions
+ ;;
+ tools)
+ test_repositories_with_tools
+ ;;
+ *)
+ test_tool_dependency_definitions
+ test_repositories_with_tools
+ ;;
+esac
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: inithello: Another bugfix to the tool dependency cleaning script.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/cbd1387437ce/
Changeset: cbd1387437ce
User: inithello
Date: 2013-12-18 19:14:24
Summary: Another bugfix to the tool dependency cleaning script.
Affected #: 1 file
diff -r d1b1558308dc1cbb8076d20a7b7d130ca4051e1d -r cbd1387437ceba5be9b47dcbf9eb951086e410a1 lib/tool_shed/scripts/clean_up_tool_dependency_directory.py
--- a/lib/tool_shed/scripts/clean_up_tool_dependency_directory.py
+++ b/lib/tool_shed/scripts/clean_up_tool_dependency_directory.py
@@ -11,10 +11,11 @@
else:
for content in os.listdir( args.tool_dependency_dir ):
print 'Deleting directory %s from %s.' % ( content, args.tool_dependency_dir )
- if os.path.isdir( content ):
- shutil.rmtree( os.path.join( args.tool_dependency_dir, content ) )
+ full_path = os.path.join( args.tool_dependency_dir, content )
+ if os.path.isdir( full_path ):
+ shutil.rmtree( full_path )
else:
- os.remove( content )
+ os.remove( full_path )
if __name__ == '__main__':
description = 'Clean out the configured tool dependency path, creating it if it does not exist.'
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: inithello: Fix error when attempting to remove a file.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/d1b1558308dc/
Changeset: d1b1558308dc
User: inithello
Date: 2013-12-18 19:11:37
Summary: Fix error when attempting to remove a file.
Affected #: 1 file
diff -r c4c6e8ea9837d4aa4fbbf08a681fd0c72b34f5b6 -r d1b1558308dc1cbb8076d20a7b7d130ca4051e1d lib/tool_shed/scripts/clean_up_tool_dependency_directory.py
--- a/lib/tool_shed/scripts/clean_up_tool_dependency_directory.py
+++ b/lib/tool_shed/scripts/clean_up_tool_dependency_directory.py
@@ -9,9 +9,12 @@
os.mkdir( args.tool_dependency_dir )
return 0
else:
- for subdirectory in os.listdir( args.tool_dependency_dir ):
- print 'Deleting directory %s from %s.' % ( subdirectory, args.tool_dependency_dir )
- shutil.rmtree( os.path.join( args.tool_dependency_dir, subdirectory ) )
+ for content in os.listdir( args.tool_dependency_dir ):
+ print 'Deleting directory %s from %s.' % ( content, args.tool_dependency_dir )
+ if os.path.isdir( content ):
+ shutil.rmtree( os.path.join( args.tool_dependency_dir, content ) )
+ else:
+ os.remove( content )
if __name__ == '__main__':
description = 'Clean out the configured tool dependency path, creating it if it does not exist.'
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: inithello: Clean up maintenance scripts for the EC2 environment.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c4c6e8ea9837/
Changeset: c4c6e8ea9837
User: inithello
Date: 2013-12-18 18:07:49
Summary: Clean up maintenance scripts for the EC2 environment.
Affected #: 2 files
diff -r 356e0b061fb4c0500db7faf11de8bf5edb2f9c41 -r c4c6e8ea9837d4aa4fbbf08a681fd0c72b34f5b6 lib/tool_shed/scripts/clean_up_tool_dependency_directory.py
--- a/lib/tool_shed/scripts/clean_up_tool_dependency_directory.py
+++ b/lib/tool_shed/scripts/clean_up_tool_dependency_directory.py
@@ -4,55 +4,23 @@
import shutil
def main( args ):
- if not os.path.exists( args.basepath ):
- print 'Tool dependency path %s does not exist.' % str( args.basepath )
- return 1
- if args.delete:
- print 'Deleting contents of tool dependency path %s.' % args.basepath
- for node in os.listdir( args.basepath ):
- path = os.path.join( args.basepath, node )
- if os.path.isdir( path ):
- try:
- shutil.rmtree( path )
- print 'Deleted directory %s and all its contents.' % path
- except Exception, e:
- print 'Error deleting directory %s: %s' % ( path, str( e ) )
- pass
- elif os.path.isfile( path ):
- try:
- os.remove( path )
- print 'Deleted file %s.' % path
- except Exception, e:
- print 'Error deleting file %s: %s' % ( path, str( e ) )
- pass
- elif os.path.islink( path ):
- print 'Deleting symlink %s with target %s.' % ( path, os.path.realpath( path ) )
- try:
- os.remove( path )
- except Exception, e:
- print 'Error deleting symlink %s: %s' % ( path, str( e ) )
- pass
+ if not os.path.exists( args.tool_dependency_dir ):
+ print 'Tool dependency base path %s does not exist, creating.' % str( args.tool_dependency_dir )
+ os.mkdir( args.tool_dependency_dir )
+ return 0
else:
- print 'Tool dependency path %s contains the following files and directories:' % args.basepath
- for element in os.listdir( args.basepath ):
- print element
- return 0
+ for subdirectory in os.listdir( args.tool_dependency_dir ):
+ print 'Deleting directory %s from %s.' % ( subdirectory, args.tool_dependency_dir )
+ shutil.rmtree( os.path.join( args.tool_dependency_dir, subdirectory ) )
if __name__ == '__main__':
- description = 'Clean out or list the contents of the provided tool dependency path. Remove if '
- description += 'the --delete command line argument is provided.'
+ description = 'Clean out the configured tool dependency path, creating it if it does not exist.'
parser = argparse.ArgumentParser( description=description )
- parser.add_argument( '--delete',
- dest='delete',
- required=False,
- action='store_true',
- default=False,
- help='Whether to delete all folders and files or list them on exit.' )
- parser.add_argument( '--basepath',
- dest='basepath',
+ parser.add_argument( '--tool_dependency_dir',
+ dest='tool_dependency_dir',
required=True,
action='store',
metavar='name',
- help='The base path where tool dependencies are installed.' )
+ help='The base path where tool dependencies will be installed.' )
args = parser.parse_args()
sys.exit( main( args ) )
diff -r 356e0b061fb4c0500db7faf11de8bf5edb2f9c41 -r c4c6e8ea9837d4aa4fbbf08a681fd0c72b34f5b6 lib/tool_shed/scripts/show_tool_dependency_installation_dir_contents.py
--- /dev/null
+++ b/lib/tool_shed/scripts/show_tool_dependency_installation_dir_contents.py
@@ -0,0 +1,75 @@
+import argparse
+import os
+import sys
+
+new_path = [ os.path.join( os.getcwd(), "lib" ) ]
+new_path.extend( sys.path[ 1: ] )
+sys.path = new_path
+
+from galaxy import eggs
+eggs.require( "SQLAlchemy >= 0.4" )
+
+import galaxy.model
+import galaxy.model.tool_shed_install.mapping as install_mapper
+import galaxy.config as galaxy_config
+
+
+class CleanUpDependencyApplication( object ):
+ """Application that enables querying the database using the tool_shed_install model."""
+
+ def __init__( self, config ):
+ self.config = config
+ # Setup the database engine and ORM
+ self.model = install_mapper.init( self.config.database_connection, engine_options={}, create_tables=False )
+
+ @property
+ def sa_session( self ):
+ """Returns a SQLAlchemy session."""
+ return self.model.context.current
+
+ def shutdown( self ):
+ pass
+
+def main( args, app ):
+ if not os.path.exists( args.basepath ):
+ print 'Tool dependency base path %s does not exist.' % str( args.basepath )
+ return
+ print 'Checking tool dependency path %s' % args.basepath
+ tool_dependency_dirs = get_tool_dependency_dirs( app )
+ for tool_dependency_dir in tool_dependency_dirs:
+ path = os.path.join( args.basepath, tool_dependency_dir )
+ if os.path.exists( path ):
+ path_contents = os.listdir( path )
+ if len( path_contents ) > 0:
+ print 'Found non-empty tool dependency installation directory %s.' % path
+ print 'Directory has the following contents: \n %s' % '\n '.join( path_contents )
+
+def get_tool_dependency_dirs( app ):
+ dependency_paths = []
+ for tool_dependency in app.sa_session.query( galaxy.model.tool_shed_install.ToolDependency ).all():
+ dependency_paths.append( tool_dependency.installation_directory( app ) )
+ return dependency_paths
+
+if __name__ == '__main__':
+ description = 'Clean out or list the contents any tool dependency directory under the provided'
+ description += 'tool dependency path. Remove any non-empty directories found if the '
+ description += '--delete command line argument is provided.'
+ parser = argparse.ArgumentParser( description=description )
+ parser.add_argument( '--basepath',
+ dest='basepath',
+ required=True,
+ action='store',
+ metavar='name',
+ help='The base path where tool dependencies are installed.' )
+ parser.add_argument( '--dburi',
+ dest='dburi',
+ required=True,
+ action='store',
+ metavar='dburi',
+ help='The database URI to connect to.' )
+ args = parser.parse_args()
+ database_connection = args.dburi
+ config_dict = dict( database_connection=database_connection, tool_dependency_dir=args.basepath )
+ config = galaxy_config.Configuration( **config_dict )
+ app = CleanUpDependencyApplication( config )
+ sys.exit( main( args, app ) )
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: greg: Enhance the tool shed's install and test framework to handle repositories of type tool_dependency_definition.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/356e0b061fb4/
Changeset: 356e0b061fb4
User: greg
Date: 2013-12-18 18:29:01
Summary: Enhance the tool shed's install and test framework to handle repositories of type tool_dependency_definition.
Affected #: 4 files
diff -r 0a6f8a07b0c3ca8d58302cb3714ed01d132e52ec -r 356e0b061fb4c0500db7faf11de8bf5edb2f9c41 install_and_test_tool_shed_repositories.sh
--- a/install_and_test_tool_shed_repositories.sh
+++ b/install_and_test_tool_shed_repositories.sh
@@ -40,10 +40,13 @@
fi
# Test installation of repositories of type tool_dependency_definition.
-# ...coming soon!
+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 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
+#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
diff -r 0a6f8a07b0c3ca8d58302cb3714ed01d132e52ec -r 356e0b061fb4c0500db7faf11de8bf5edb2f9c41 test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -20,6 +20,7 @@
import nose
import platform
import time
+import tool_shed.repository_types.util as rt_util
import tool_shed.util.shed_util_common as suc
import urllib
@@ -142,6 +143,9 @@
else:
testing_single_repository_dict[ 'changeset_revision' ] = None
+# Test frameworks that use this utility module.
+REPOSITORIES_WITH_TOOLS = 'repositories_with_tools'
+TOOL_DEPENDENCY_DEFINITIONS = 'tool_dependency_definitions'
class ReportResults( Plugin ):
'''Simple Nose plugin to record the IDs of all tests run, regardless of success.'''
@@ -230,7 +234,7 @@
missing_tool_dependencies.extend( get_missing_tool_dependencies( repository_dependency ) )
return missing_tool_dependencies
-def get_repositories_to_install( tool_shed_url ):
+def get_repositories_to_install( tool_shed_url, test_framework ):
"""
Get a list of repository info dicts to install. This method expects a json list of dicts with the following structure:
[{ "changeset_revision": <revision>,
@@ -247,12 +251,18 @@
parts = [ 'repository_revisions' ]
# We'll filter out deprecated repositories from testing since testing them is necessary only if reproducibility
# is guaranteed and we currently do not guarantee reproducibility.
- params = dict( do_not_test='false',
- downloadable='true',
- includes_tools='true',
- malicious='false',
- missing_test_components='false',
- skip_tool_test='false' )
+ if test_framework == REPOSITORIES_WITH_TOOLS:
+ params = dict( do_not_test='false',
+ downloadable='true',
+ includes_tools='true',
+ malicious='false',
+ missing_test_components='false',
+ skip_tool_test='false' )
+ elif test_framework == TOOL_DEPENDENCY_DEFINITIONS:
+ params = dict( do_not_test='false',
+ downloadable='true',
+ malicious='false',
+ skip_tool_test='false' )
api_url = get_api_url( base=tool_shed_url, parts=parts, params=params )
baseline_repository_dicts, error_message = json_from_url( api_url )
if error_message:
@@ -272,6 +282,9 @@
# Don't test empty repositories.
changeset_revision = baseline_repository_dict[ 'changeset_revision' ]
if changeset_revision != suc.INITIAL_CHANGELOG_HASH:
+ # If testing repositories of type tool_dependency_definition, filter accordingly.
+ if test_framework == TOOL_DEPENDENCY_DEFINITIONS and repository_dict[ 'type' ] != rt_util.TOOL_DEPENDENCY_DEFINITION:
+ continue
# Merge the dictionary returned from /api/repository_revisions with the detailed repository_dict and
# append it to the list of repository_dicts to install and test.
if latest_revision_only:
@@ -299,7 +312,7 @@
# Get a list of repositories to test from the tool shed specified in the GALAXY_INSTALL_TEST_TOOL_SHED_URL
# environment variable.
log.debug( "The Tool Shed's API url...\n%s" % str( api_url ) )
- log.debug( "...retrieved %d repository revisions for testing." % len( repository_dicts ) )
+ log.debug( "...retrieved %d repository revisions for installation and possible testing." % len( repository_dicts ) )
log.debug( "Repository revisions for testing:" )
for repository_dict in repository_dicts:
name = str( repository_dict.get( 'name', None ) )
@@ -309,15 +322,13 @@
return repository_dicts, error_message
def get_repository_current_revision( repo_path ):
- '''
- This method uses the python mercurial API to get the current working directory's mercurial changeset hash. Note that if the author of mercurial
- changes the API, this method will have to be updated or replaced.
- '''
+ """This method uses the python mercurial API to get the current working directory's mercurial changeset hash."""
# Initialize a mercurial repo object from the provided path.
repo = hg.repository( ui.ui(), repo_path )
# Get the working directory's change context.
ctx = repo[ None ]
- # Extract the changeset hash of the first parent of that change context (the most recent changeset to which the working directory was updated).
+ # Extract the changeset hash of the first parent of that change context (the most recent changeset to which the
+ # working directory was updated).
changectx = ctx.parents()[ 0 ]
# Also get the numeric revision, so we can return the customary id:hash changeset identifiers.
ctx_rev = changectx.rev()
@@ -461,13 +472,18 @@
changeset_revision=str( repository.changeset_revision ) ) )
return results_dict
-def initialize_results_dict():
+def initialize_results_dict( test_framework ):
# Initialize a dictionary for the summary that will be printed to stdout.
results_dict = {}
- results_dict[ 'total_repositories_tested' ] = 0
- results_dict[ 'all_tests_passed' ] = []
- results_dict[ 'at_least_one_test_failed' ] = []
- results_dict[ 'repositories_failed_install' ] = []
+ if test_framework == REPOSITORIES_WITH_TOOLS:
+ results_dict[ 'total_repositories_tested' ] = 0
+ results_dict[ 'all_tests_passed' ] = []
+ results_dict[ 'at_least_one_test_failed' ] = []
+ results_dict[ 'repositories_failed_install' ] = []
+ elif test_framework == TOOL_DEPENDENCY_DEFINITIONS:
+ results_dict[ 'total_repositories_installed' ] = 0
+ results_dict[ 'repositories_with_installation_error' ] = 0
+ results_dict[ 'tool_dependencies_with_installation_error' ] = 0
return results_dict
def initialize_tool_tests_results_dict( app, tool_test_results_dict ):
diff -r 0a6f8a07b0c3ca8d58302cb3714ed01d132e52ec -r 356e0b061fb4c0500db7faf11de8bf5edb2f9c41 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -52,7 +52,7 @@
from functional import database_contexts
-log = logging.getLogger( 'install_and_test_repositories' )
+log = logging.getLogger( 'install_and_test_repositories_with_tools' )
assert sys.version_info[ :2 ] >= ( 2, 6 )
test_home_directory = os.path.join( cwd, 'test', 'install_and_test_tool_shed_repositories', 'repositories_with_tools' )
@@ -72,6 +72,8 @@
else:
can_update_tool_shed = True
+test_framework = install_and_test_base_util.REPOSITORIES_WITH_TOOLS
+
def get_failed_test_dicts( test_result, from_tool_test=True ):
"""Extract any useful data from the test_result.failures and test_result.errors attributes."""
failed_test_dicts = []
@@ -132,12 +134,12 @@
return tool_id, tool_version
def install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file ):
- results_dict = install_and_test_base_util.initialize_results_dict()
+ results_dict = install_and_test_base_util.initialize_results_dict( test_framework )
error_message = ''
# Initialize a dictionary for the summary that will be printed to stdout.
total_repositories_tested = results_dict[ 'total_repositories_tested' ]
repositories_to_install, error_message = \
- install_and_test_base_util.get_repositories_to_install( install_and_test_base_util.galaxy_tool_shed_url )
+ install_and_test_base_util.get_repositories_to_install( install_and_test_base_util.galaxy_tool_shed_url, test_framework )
if error_message:
return None, error_message
# Handle repositories not to be tested.
@@ -153,7 +155,8 @@
# someone has previously installed. Deleted repositories have never been installed, so should not be tested. If they are
# undeleted, this script will then test them the next time it runs. We don't need to check if a repository has been deleted
# here because our call to the Tool Shed API filters by downloadable='true', in which case deleted will always be False.
- log.debug( 'Loading the list of repositories excluded from testing from the file %s...', install_and_test_base_util.exclude_list_file )
+ log.debug( 'Loading the list of repositories excluded from testing from the file %s...' % \
+ str( install_and_test_base_util.exclude_list_file ) )
exclude_list = install_and_test_base_util.parse_exclude_list( install_and_test_base_util.exclude_list_file )
else:
exclude_list = []
diff -r 0a6f8a07b0c3ca8d58302cb3714ed01d132e52ec -r 356e0b061fb4c0500db7faf11de8bf5edb2f9c41 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- /dev/null
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -0,0 +1,525 @@
+#!/usr/bin/env python
+"""
+This script cannot be run directly, because it needs to have test/functional/test_toolbox.py in sys.argv in
+order to run functional tests on repository tools after installation. The install_and_test_tool_shed_repositories.sh
+will execute this script with the appropriate parameters.
+"""
+import os
+import sys
+# Assume we are run from the galaxy root directory, add lib to the python path
+cwd = os.getcwd()
+sys.path.append( cwd )
+new_path = [ os.path.join( cwd, "scripts" ),
+ os.path.join( cwd, "lib" ),
+ os.path.join( cwd, 'test' ),
+ os.path.join( cwd, 'scripts', 'api' ) ]
+new_path.extend( sys.path )
+sys.path = new_path
+
+from galaxy import eggs
+eggs.require( "nose" )
+eggs.require( "Paste" )
+eggs.require( 'mercurial' )
+# This should not be required, but it is under certain conditions thanks to this bug:
+# http://code.google.com/p/python-nose/issues/detail?id=284
+eggs.require( "pysqlite" )
+
+import httplib
+import install_and_test_tool_shed_repositories.base.test_db_util as test_db_util
+import install_and_test_tool_shed_repositories.functional.test_install_repositories as test_install_repositories
+import logging
+import nose
+import random
+import re
+import shutil
+import socket
+import string
+import tempfile
+import time
+import threading
+
+import install_and_test_tool_shed_repositories.base.util as install_and_test_base_util
+
+from base.tool_shed_util import parse_tool_panel_config
+
+from galaxy.app import UniverseApplication
+from galaxy.util.json import from_json_string
+from galaxy.util.json import to_json_string
+from galaxy.web import buildapp
+from functional_tests import generate_config_file
+from nose.plugins import Plugin
+from paste import httpserver
+
+from functional import database_contexts
+
+log = logging.getLogger( 'install_and_test_tool_dependency_definitions' )
+
+assert sys.version_info[ :2 ] >= ( 2, 6 )
+test_home_directory = os.path.join( cwd, 'test', 'install_and_test_tool_shed_repositories', 'tool_dependency_definitions' )
+
+# Here's the directory where everything happens. Temporary directories are created within this directory to contain
+# the database, new repositories, etc.
+galaxy_test_tmp_dir = os.path.join( test_home_directory, 'tmp' )
+default_galaxy_locales = 'en'
+default_galaxy_test_file_dir = "test-data"
+os.environ[ 'GALAXY_INSTALL_TEST_TMP_DIR' ] = galaxy_test_tmp_dir
+
+# This script can be run in such a way that no Tool Shed database records should be changed.
+if '-info_only' in sys.argv or 'GALAXY_INSTALL_TEST_INFO_ONLY' in os.environ:
+ can_update_tool_shed = False
+else:
+ can_update_tool_shed = True
+
+test_framework = install_and_test_base_util.TOOL_DEPENDENCY_DEFINITIONS
+
+def install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file ):
+ results_dict = install_and_test_base_util.initialize_results_dict( test_framework )
+ error_message = ''
+ # Initialize a dictionary for the summary that will be printed to stdout.
+ total_repositories_installed = results_dict[ 'total_repositories_installed' ]
+ repositories_to_install, error_message = \
+ install_and_test_base_util.get_repositories_to_install( install_and_test_base_util.galaxy_tool_shed_url, test_framework )
+ if error_message:
+ return None, error_message
+ # Handle repositories not to be tested.
+ if os.path.exists( install_and_test_base_util.exclude_list_file ):
+ # Entries in the exclude_list look something like this.
+ # { 'reason': The default reason or the reason specified in this section,
+ # 'repositories':
+ # [( name, owner, changeset revision if changeset revision else None ),
+ # ( name, owner, changeset revision if changeset revision else None )] }
+ # If changeset revision is None, that means the entire repository is excluded from testing, otherwise only the specified
+ # revision should be skipped.
+ # We are testing deprecated repositories because it is possible that a deprecated repository contains valid tools that
+ # someone has previously installed. Deleted repositories have never been installed, so should not be tested. If they are
+ # undeleted, this script will then test them the next time it runs. We don't need to check if a repository has been deleted
+ # here because our call to the Tool Shed API filters by downloadable='true', in which case deleted will always be False.
+ log.debug( 'Loading the list of repositories excluded from testing from the file %s...' % \
+ str( install_and_test_base_util.exclude_list_file ) )
+ exclude_list = install_and_test_base_util.parse_exclude_list( install_and_test_base_util.exclude_list_file )
+ else:
+ exclude_list = []
+ # Generate a test method that will use Twill to install each repository into the embedded Galaxy application that was
+ # started up, installing repository and tool dependencies. Upon successful installation, generate a test case for each
+ # functional test defined for each tool in the repository and execute the test cases. Record the result of the tests.
+ # The traceback and captured output of the tool that was run will be recored for test failures. After all tests have
+ # completed, the repository is uninstalled, so test cases don't interfere with the next repository's functional tests.
+ for repository_dict in repositories_to_install:
+ # Each repository_dict looks something like:
+ # { "changeset_revision": "13fa22a258b5",
+ # "contents_url": "/api/repositories/529fd61ab1c6cc36/contents",
+ # "deleted": false,
+ # "deprecated": false,
+ # "description": "Convert column case.",
+ # "downloadable": true,
+ # "id": "529fd61ab1c6cc36",
+ # "long_description": "This tool takes the specified columns and converts them to uppercase or lowercase.",
+ # "malicious": false,
+ # "name": "change_case",
+ # "owner": "test",
+ # "private": false,
+ # "repository_id": "529fd61ab1c6cc36",
+ # "times_downloaded": 0,
+ # "tool_shed_url": "http://toolshed.local:10001",
+ # "url": "/api/repository_revisions/529fd61ab1c6cc36",
+ # "user_id": "529fd61ab1c6cc36" }
+ encoded_repository_metadata_id = repository_dict.get( 'id', None )
+ # Add the URL for the tool shed we're installing from, so the automated installation methods go to the right place.
+ repository_dict[ 'tool_shed_url' ] = install_and_test_base_util.galaxy_tool_shed_url
+ # Get the name and owner out of the repository info dict.
+ name = str( repository_dict[ 'name' ] )
+ owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ log.debug( "Processing revision %s of repository %s owned by %s..." % ( changeset_revision, name, owner ) )
+ repository_identifier_dict = dict( name=name, owner=owner, changeset_revision=changeset_revision )
+ # Retrieve the stored list of tool_test_results_dicts.
+ tool_test_results_dicts, error_message = \
+ install_and_test_base_util.get_tool_test_results_dicts( install_and_test_base_util.galaxy_tool_shed_url,
+ encoded_repository_metadata_id )
+ if error_message:
+ log.debug( error_message )
+ else:
+ tool_test_results_dict = install_and_test_base_util.get_tool_test_results_dict( tool_test_results_dicts )
+ # See if this repository should be skipped for any reason.
+ this_repository_is_in_the_exclude_list = False
+ skip_reason = None
+ for exclude_dict in exclude_list:
+ reason = exclude_dict[ 'reason' ]
+ exclude_repositories = exclude_dict[ 'repositories' ]
+ if ( name, owner, changeset_revision ) in exclude_repositories or ( name, owner, None ) in exclude_repositories:
+ this_repository_is_in_the_exclude_list = True
+ skip_reason = reason
+ break
+ if this_repository_is_in_the_exclude_list:
+ tool_test_results_dict[ 'not_tested' ] = dict( reason=skip_reason )
+ params = dict( do_not_test=False )
+ # TODO: do something useful with response_dict
+ response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
+ log.debug( "Not testing revision %s of repository %s owned by %s because it is in the exclude list for this test run." % \
+ ( changeset_revision, name, owner ) )
+ else:
+ tool_test_results_dict = install_and_test_base_util.initialize_tool_tests_results_dict( app, tool_test_results_dict )
+ repository, error_message = install_and_test_base_util.install_repository( app, repository_dict )
+ if error_message:
+ tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = error_message
+ # Even if the repository failed to install, execute the uninstall method, in case a dependency did succeed.
+ log.debug( 'Attempting to uninstall repository %s owned by %s.' % ( name, owner ) )
+ try:
+ repository = test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
+ except Exception, e:
+ error_message = 'Unable to find installed repository %s owned by %s: %s.' % ( name, owner, str( e ) )
+ log.exception( error_message )
+ test_result = dict( tool_shed=install_and_test_base_util.galaxy_tool_shed_url,
+ name=name,
+ owner=owner,
+ changeset_revision=changeset_revision,
+ error_message=error_message )
+ tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ].append( test_result )
+ params = dict( tools_functionally_correct=False,
+ test_install_error=True,
+ do_not_test=False )
+ # TODO: do something useful with response_dict
+ response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
+ try:
+ # We are uninstalling this repository and all of its repository dependencies.
+ install_and_test_base_util.uninstall_repository_and_repository_dependencies( app, repository_dict )
+ except Exception, e:
+ log.exception( 'Error attempting to uninstall revision %s of repository %s owned by %s: %s' % \
+ ( changeset_revision, name, owner, str( e ) ) )
+ results_dict[ 'repositories_with_installation_error' ].append( repository_identifier_dict )
+ log.debug( 'Repository %s failed to install correctly.' % str( name ) )
+ else:
+ log.debug( 'Installation of %s succeeded.' % str( repository.name ) )
+ missing_tool_dependencies = install_and_test_base_util.get_missing_tool_dependencies( repository )
+ if missing_tool_dependencies or repository.missing_repository_dependencies:
+ results_dict = install_and_test_base_util.handle_missing_dependencies( app,
+ repository,
+ missing_tool_dependencies,
+ repository_dict,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ results_dict,
+ can_update_tool_shed )
+ results_dict[ 'total_repositories_installed' ] = total_repositories_installed
+ return results_dict, error_message
+
+def main():
+ if install_and_test_base_util.tool_shed_api_key is None:
+ # If the tool shed URL specified in any dict is not present in the tool_sheds_conf.xml, the installation will fail.
+ log.debug( 'Cannot proceed without a valid tool shed API key set in the enviroment variable GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY.' )
+ return 1
+ if install_and_test_base_util.galaxy_tool_shed_url is None:
+ log.debug( 'Cannot proceed without a valid Tool Shed base URL set in the environment variable GALAXY_INSTALL_TEST_TOOL_SHED_URL.' )
+ return 1
+ # ---- Configuration ------------------------------------------------------
+ galaxy_test_host = os.environ.get( 'GALAXY_INSTALL_TEST_HOST', install_and_test_base_util.default_galaxy_test_host )
+ # Set the GALAXY_INSTALL_TEST_HOST variable so that Twill will have the Galaxy url to which to
+ # install repositories.
+ os.environ[ 'GALAXY_INSTALL_TEST_HOST' ] = galaxy_test_host
+ # Set the GALAXY_TEST_HOST environment variable so that the toolbox tests will have the Galaxy url
+ # on which to to run tool functional tests.
+ os.environ[ 'GALAXY_TEST_HOST' ] = galaxy_test_host
+ galaxy_test_port = os.environ.get( 'GALAXY_INSTALL_TEST_PORT', str( install_and_test_base_util.default_galaxy_test_port_max ) )
+ os.environ[ 'GALAXY_TEST_PORT' ] = galaxy_test_port
+ tool_path = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_PATH', 'tools' )
+ if 'HTTP_ACCEPT_LANGUAGE' not in os.environ:
+ os.environ[ 'HTTP_ACCEPT_LANGUAGE' ] = default_galaxy_locales
+ galaxy_test_file_dir = os.environ.get( 'GALAXY_INSTALL_TEST_FILE_DIR', default_galaxy_test_file_dir )
+ if not os.path.isabs( galaxy_test_file_dir ):
+ galaxy_test_file_dir = os.path.abspath( galaxy_test_file_dir )
+ use_distributed_object_store = os.environ.get( 'GALAXY_INSTALL_TEST_USE_DISTRIBUTED_OBJECT_STORE', False )
+ if not os.path.isdir( galaxy_test_tmp_dir ):
+ os.mkdir( galaxy_test_tmp_dir )
+ # Set up the configuration files for the Galaxy instance.
+ shed_tool_data_table_conf_file = os.environ.get( 'GALAXY_INSTALL_TEST_SHED_TOOL_DATA_TABLE_CONF',
+ os.path.join( galaxy_test_tmp_dir, 'test_shed_tool_data_table_conf.xml' ) )
+ galaxy_tool_data_table_conf_file = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_DATA_TABLE_CONF',
+ install_and_test_base_util.tool_data_table_conf )
+ galaxy_tool_conf_file = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_CONF',
+ os.path.join( galaxy_test_tmp_dir, 'test_tool_conf.xml' ) )
+ galaxy_job_conf_file = os.environ.get( 'GALAXY_INSTALL_TEST_JOB_CONF',
+ os.path.join( galaxy_test_tmp_dir, 'test_job_conf.xml' ) )
+ galaxy_shed_tool_conf_file = os.environ.get( 'GALAXY_INSTALL_TEST_SHED_TOOL_CONF',
+ os.path.join( galaxy_test_tmp_dir, 'test_shed_tool_conf.xml' ) )
+ galaxy_migrated_tool_conf_file = os.environ.get( 'GALAXY_INSTALL_TEST_MIGRATED_TOOL_CONF',
+ os.path.join( galaxy_test_tmp_dir, 'test_migrated_tool_conf.xml' ) )
+ galaxy_tool_sheds_conf_file = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_SHEDS_CONF',
+ os.path.join( galaxy_test_tmp_dir, 'test_tool_sheds_conf.xml' ) )
+ galaxy_shed_tools_dict = os.environ.get( 'GALAXY_INSTALL_TEST_SHED_TOOL_DICT_FILE',
+ os.path.join( galaxy_test_tmp_dir, 'shed_tool_dict' ) )
+ file( galaxy_shed_tools_dict, 'w' ).write( to_json_string( {} ) )
+ # Set the GALAXY_TOOL_SHED_TEST_FILE environment variable to the path of the shed_tools_dict file so that
+ # test.base.twilltestcase.setUp will find and parse it properly.
+ os.environ[ 'GALAXY_TOOL_SHED_TEST_FILE' ] = galaxy_shed_tools_dict
+ if 'GALAXY_INSTALL_TEST_TOOL_DATA_PATH' in os.environ:
+ tool_data_path = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_DATA_PATH' )
+ else:
+ tool_data_path = tempfile.mkdtemp( dir=galaxy_test_tmp_dir )
+ os.environ[ 'GALAXY_INSTALL_TEST_TOOL_DATA_PATH' ] = tool_data_path
+ # Configure the database connection and path.
+ if 'GALAXY_INSTALL_TEST_DBPATH' in os.environ:
+ galaxy_db_path = os.environ[ 'GALAXY_INSTALL_TEST_DBPATH' ]
+ else:
+ tempdir = tempfile.mkdtemp( dir=galaxy_test_tmp_dir )
+ galaxy_db_path = os.path.join( tempdir, 'database' )
+ # Configure the paths Galaxy needs to install and test tools.
+ galaxy_file_path = os.path.join( galaxy_db_path, 'files' )
+ new_repos_path = tempfile.mkdtemp( dir=galaxy_test_tmp_dir )
+ galaxy_tempfiles = tempfile.mkdtemp( dir=galaxy_test_tmp_dir )
+ galaxy_shed_tool_path = tempfile.mkdtemp( dir=galaxy_test_tmp_dir, prefix='shed_tools' )
+ galaxy_migrated_tool_path = tempfile.mkdtemp( dir=galaxy_test_tmp_dir )
+ # Set up the tool dependency path for the Galaxy instance.
+ tool_dependency_dir = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_DEPENDENCY_DIR', None )
+ if tool_dependency_dir is None:
+ tool_dependency_dir = tempfile.mkdtemp( dir=galaxy_test_tmp_dir )
+ os.environ[ 'GALAXY_INSTALL_TEST_TOOL_DEPENDENCY_DIR' ] = tool_dependency_dir
+ os.environ[ 'GALAXY_TOOL_DEPENDENCY_DIR' ] = tool_dependency_dir
+ if 'GALAXY_INSTALL_TEST_DBURI' in os.environ:
+ database_connection = os.environ[ 'GALAXY_INSTALL_TEST_DBURI' ]
+ else:
+ database_connection = 'sqlite:///' + os.path.join( galaxy_db_path, 'install_and_test_repositories.sqlite' )
+ kwargs = {}
+ for dir in [ galaxy_test_tmp_dir ]:
+ try:
+ os.makedirs( dir )
+ except OSError:
+ pass
+ print "Database connection: ", database_connection
+ # Generate the shed_tool_data_table_conf.xml file.
+ file( shed_tool_data_table_conf_file, 'w' ).write( install_and_test_base_util.tool_data_table_conf_xml_template )
+ os.environ[ 'GALAXY_INSTALL_TEST_SHED_TOOL_DATA_TABLE_CONF' ] = shed_tool_data_table_conf_file
+ # ---- Start up a Galaxy instance ------------------------------------------------------
+ # Generate the tool_conf.xml file.
+ file( galaxy_tool_conf_file, 'w' ).write( install_and_test_base_util.tool_conf_xml )
+ # Generate the job_conf.xml file.
+ file( galaxy_job_conf_file, 'w' ).write( install_and_test_base_util.job_conf_xml )
+ # Generate the tool_sheds_conf.xml file, but only if a the user has not specified an existing one in the environment.
+ if 'GALAXY_INSTALL_TEST_TOOL_SHEDS_CONF' not in os.environ:
+ file( galaxy_tool_sheds_conf_file, 'w' ).write( install_and_test_base_util.tool_sheds_conf_xml )
+ # Generate the shed_tool_conf.xml file.
+ tool_conf_template_parser = string.Template( install_and_test_base_util.shed_tool_conf_xml_template )
+ shed_tool_conf_xml = tool_conf_template_parser.safe_substitute( shed_tool_path=galaxy_shed_tool_path )
+ file( galaxy_shed_tool_conf_file, 'w' ).write( shed_tool_conf_xml )
+ os.environ[ 'GALAXY_INSTALL_TEST_SHED_TOOL_CONF' ] = galaxy_shed_tool_conf_file
+ # Generate the migrated_tool_conf.xml file.
+ migrated_tool_conf_xml = tool_conf_template_parser.safe_substitute( shed_tool_path=galaxy_migrated_tool_path )
+ file( galaxy_migrated_tool_conf_file, 'w' ).write( migrated_tool_conf_xml )
+ # Write the embedded web application's specific configuration to a temporary file. This is necessary in order for
+ # the external metadata script to find the right datasets.
+ kwargs = dict( admin_users = 'test(a)bx.psu.edu',
+ master_api_key = install_and_test_base_util.default_galaxy_master_api_key,
+ allow_user_creation = True,
+ allow_user_deletion = True,
+ allow_library_path_paste = True,
+ database_connection = database_connection,
+ datatype_converters_config_file = "datatype_converters_conf.xml.sample",
+ file_path = galaxy_file_path,
+ id_secret = install_and_test_base_util.galaxy_encode_secret,
+ job_config_file = galaxy_job_conf_file,
+ job_queue_workers = 5,
+ log_destination = "stdout",
+ migrated_tools_config = galaxy_migrated_tool_conf_file,
+ new_file_path = galaxy_tempfiles,
+ running_functional_tests = True,
+ shed_tool_data_table_config = shed_tool_data_table_conf_file,
+ shed_tool_path = galaxy_shed_tool_path,
+ template_path = "templates",
+ tool_config_file = ','.join( [ galaxy_tool_conf_file, galaxy_shed_tool_conf_file ] ),
+ tool_data_path = tool_data_path,
+ tool_data_table_config_path = galaxy_tool_data_table_conf_file,
+ tool_dependency_dir = tool_dependency_dir,
+ tool_path = tool_path,
+ tool_parse_help = False,
+ tool_sheds_config_file = galaxy_tool_sheds_conf_file,
+ update_integrated_tool_panel = False,
+ use_heartbeat = False )
+ galaxy_config_file = os.environ.get( 'GALAXY_INSTALL_TEST_INI_FILE', None )
+ # If the user has passed in a path for the .ini file, do not overwrite it.
+ if not galaxy_config_file:
+ galaxy_config_file = os.path.join( galaxy_test_tmp_dir, 'install_test_tool_shed_repositories_wsgi.ini' )
+ config_items = []
+ for label in kwargs:
+ config_tuple = label, kwargs[ label ]
+ config_items.append( config_tuple )
+ # Write a temporary file, based on universe_wsgi.ini.sample, using the configuration options defined above.
+ generate_config_file( 'universe_wsgi.ini.sample', galaxy_config_file, config_items )
+ kwargs[ 'tool_config_file' ] = [ galaxy_tool_conf_file, galaxy_shed_tool_conf_file ]
+ # Set the global_conf[ '__file__' ] option to the location of the temporary .ini file, which gets passed to set_metadata.sh.
+ kwargs[ 'global_conf' ] = install_and_test_base_util.get_webapp_global_conf()
+ kwargs[ 'global_conf' ][ '__file__' ] = galaxy_config_file
+ # ---- Build Galaxy Application --------------------------------------------------
+ if not database_connection.startswith( 'sqlite://' ):
+ kwargs[ 'database_engine_option_max_overflow' ] = '20'
+ kwargs[ 'database_engine_option_pool_size' ] = '10'
+ kwargs[ 'config_file' ] = galaxy_config_file
+ app = UniverseApplication( **kwargs )
+ database_contexts.galaxy_context = app.model.context
+ database_contexts.install_context = app.install_model.context
+
+ log.debug( "Embedded Galaxy application started..." )
+ # ---- Run galaxy webserver ------------------------------------------------------
+ server = None
+ global_conf = install_and_test_base_util.get_webapp_global_conf()
+ global_conf[ 'database_file' ] = database_connection
+ webapp = buildapp.app_factory( global_conf,
+ use_translogger=False,
+ static_enabled=install_and_test_base_util.STATIC_ENABLED,
+ app=app )
+ # Serve the app on a specified or random port.
+ if galaxy_test_port is not None:
+ server = httpserver.serve( webapp, host=galaxy_test_host, port=galaxy_test_port, start_loop=False )
+ else:
+ random.seed()
+ for i in range( 0, 9 ):
+ try:
+ galaxy_test_port = str( random.randint( install_and_test_base_util.default_galaxy_test_port_min,
+ install_and_test_base_util.default_galaxy_test_port_max ) )
+ log.debug( "Attempting to serve app on randomly chosen port: %s", galaxy_test_port )
+ server = httpserver.serve( webapp, host=galaxy_test_host, port=galaxy_test_port, start_loop=False )
+ break
+ except socket.error, e:
+ if e[0] == 98:
+ continue
+ raise
+ else:
+ raise Exception( "Unable to open a port between %s and %s to start Galaxy server" % \
+ ( install_and_test_base_util.default_galaxy_test_port_min, install_and_test_base_util.default_galaxy_test_port_max ) )
+ os.environ[ 'GALAXY_INSTALL_TEST_PORT' ] = galaxy_test_port
+ # Start the server.
+ t = threading.Thread( target=server.serve_forever )
+ t.start()
+ # Test if the server is up.
+ for i in range( 10 ):
+ # Directly test the app, not the proxy.
+ conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_port )
+ conn.request( "GET", "/" )
+ if conn.getresponse().status == 200:
+ break
+ time.sleep( 0.1 )
+ else:
+ raise Exception( "Test HTTP server did not return '200 OK' after 10 tries" )
+ log.debug( "Embedded galaxy web server started..." )
+ log.debug( "The embedded Galaxy application is running on %s:%s" % ( str( galaxy_test_host ), str( galaxy_test_port ) ) )
+ log.debug( "Repositories will be installed from the tool shed at %s" % str( install_and_test_base_util.galaxy_tool_shed_url ) )
+ # If a tool_data_table_conf.test.xml file was found, add the entries from it into the app's tool data tables.
+ if install_and_test_base_util.additional_tool_data_tables:
+ app.tool_data_tables.add_new_entries_from_config_file( config_filename=install_and_test_base_util.additional_tool_data_tables,
+ tool_data_path=install_and_test_base_util.additional_tool_data_path,
+ shed_tool_data_table_config=None,
+ persist=False )
+ now = time.strftime( "%Y-%m-%d %H:%M:%S" )
+ print "####################################################################################"
+ print "# %s - running repository installation and testing script." % now
+ print "####################################################################################"
+ results_dict, error_message = install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file )
+ if error_message:
+ log.debug( error_message )
+ else:
+ total_repositories_installed = results_dict[ 'total_repositories_installed' ]
+ repositories_with_installation_error = results_dict[ 'repositories_with_installation_error' ]
+ tool_dependencies_with_installation_error = results_dict[ 'tool_dependencies_with_installation_error' ]
+ now = time.strftime( "%Y-%m-%d %H:%M:%S" )
+ print "####################################################################################"
+ print "# %s - repository installation and testing script completed." % now
+ print "# Repository revisions tested: %s" % str( total_repositories_installed )
+ if not can_update_tool_shed:
+ print "# This run will not update the Tool Shed database."
+ if repositories_with_installation_error:
+ print '# ----------------------------------------------------------------------------------'
+ print "# %d repositories have installation errors:" % len( repositories_with_installation_error )
+ install_and_test_base_util.show_summary_output( repositories_with_installation_error )
+ if tool_dependencies_with_installation_error:
+ print '# ----------------------------------------------------------------------------------'
+ print "# %d tool dependencies have installation errors:" % len( tool_dependencies_with_installation_error )
+ install_and_test_base_util.show_summary_output( tool_dependencies_with_installation_error )
+ print "####################################################################################"
+ log.debug( "Shutting down..." )
+ # Gracefully shut down the embedded web server and UniverseApplication.
+ if server:
+ log.debug( "Shutting down embedded galaxy web server..." )
+ server.server_close()
+ server = None
+ log.debug( "Embedded galaxy server stopped..." )
+ if app:
+ log.debug( "Shutting down galaxy application..." )
+ app.shutdown()
+ app = None
+ log.debug( "Embedded galaxy application stopped..." )
+ # Clean up test files unless otherwise specified.
+ if 'GALAXY_INSTALL_TEST_NO_CLEANUP' not in os.environ:
+ for dir in [ galaxy_test_tmp_dir ]:
+ if os.path.exists( dir ):
+ try:
+ shutil.rmtree( dir )
+ log.debug( "Cleaned up temporary files in %s", str( dir ) )
+ except:
+ pass
+ else:
+ log.debug( 'GALAXY_INSTALL_TEST_NO_CLEANUP set, not cleaning up.' )
+ # Return a "successful" response to buildbot.
+ return 0
+
+if __name__ == "__main__":
+ # The tool_test_results_dict should always have the following structure:
+ # {
+ # "test_environment":
+ # {
+ # "galaxy_revision": "9001:abcd1234",
+ # "galaxy_database_version": "114",
+ # "tool_shed_revision": "9001:abcd1234",
+ # "tool_shed_mercurial_version": "2.3.1",
+ # "tool_shed_database_version": "17",
+ # "python_version": "2.7.2",
+ # "architecture": "x86_64",
+ # "system": "Darwin 12.2.0"
+ # },
+ # "installation_errors":
+ # {
+ # 'tool_dependencies':
+ # [
+ # {
+ # 'type': 'Type of tool dependency, e.g. package, set_environment, etc.',
+ # 'name': 'Name of the tool dependency.',
+ # 'version': 'Version if this is a package, otherwise blank.',
+ # 'error_message': 'The error message returned when installation was attempted.',
+ # },
+ # ],
+ # 'repository_dependencies':
+ # [
+ # {
+ # 'tool_shed': 'The tool shed that this repository was installed from.',
+ # 'name': 'The name of the repository that failed to install.',
+ # 'owner': 'Owner of the failed repository.',
+ # 'changeset_revision': 'Changeset revision of the failed repository.',
+ # 'error_message': 'The error message that was returned when the repository failed to install.',
+ # },
+ # ],
+ # 'current_repository':
+ # [
+ # {
+ # 'tool_shed': 'The tool shed that this repository was installed from.',
+ # 'name': 'The name of the repository that failed to install.',
+ # 'owner': 'Owner of the failed repository.',
+ # 'changeset_revision': 'Changeset revision of the failed repository.',
+ # 'error_message': 'The error message that was returned when the repository failed to install.',
+ # },
+ # ],
+ # {
+ # "name": "The name of the repository.",
+ # "owner": "The owner of the repository.",
+ # "changeset_revision": "The changeset revision of the repository.",
+ # "error_message": "The message stored in tool_dependency.error_message."
+ # },
+ # }
+ # }
+ sys.exit( main() )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Refactor the tool shed's install and test framework in preparation for enhancements to test repositories of type tool_dependency_definition.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/0a6f8a07b0c3/
Changeset: 0a6f8a07b0c3
User: greg
Date: 2013-12-18 17:16:09
Summary: Refactor the tool shed's install and test framework in preparation for enhancements to test repositories of type tool_dependency_definition.
Affected #: 2 files
diff -r ce186eb5fefcb7ff332c73bd0869b9e222293d0b -r 0a6f8a07b0c3ca8d58302cb3714ed01d132e52ec test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -1,25 +1,195 @@
-import logging
import os
import sys
cwd = os.getcwd()
-if cwd not in sys.path:
- sys.path.append( cwd )
-
-new_path = [ os.path.join( cwd, "lib" ) ]
-if new_path not in sys.path:
- new_path.extend( sys.path )
- sys.path = new_path
+sys.path.append( cwd )
+new_path = [ os.path.join( cwd, "scripts" ),
+ os.path.join( cwd, "lib" ),
+ os.path.join( cwd, 'test' ),
+ os.path.join( cwd, 'scripts', 'api' ) ]
+new_path.extend( sys.path )
+sys.path = new_path
from galaxy import eggs
+eggs.require( 'mercurial' )
+eggs.require( "nose" )
-eggs.require( 'mercurial' )
+import logging
+import install_and_test_tool_shed_repositories.base.test_db_util as test_db_util
+import install_and_test_tool_shed_repositories.functional.test_install_repositories as test_install_repositories
+import nose
+import platform
+import time
+import tool_shed.util.shed_util_common as suc
+import urllib
+
+from base.tool_shed_util import log_reason_repository_cannot_be_uninstalled
+
+from common import update
+
+from galaxy.util import asbool
+from galaxy.util import listify
+from galaxy.util import unicodify
+from galaxy.util.json import from_json_string
+from nose.plugins import Plugin
+from tool_shed.util import tool_dependency_util
+
+from tool_shed.util.xml_util import parse_xml
from mercurial import hg
from mercurial import ui
log = logging.getLogger(__name__)
+# Set up a job_conf.xml that explicitly limits jobs to 10 minutes.
+job_conf_xml = '''<?xml version="1.0"?>
+<!-- A test job config that explicitly configures job running the way it is configured by default (if there is no explicit config). -->
+<job_conf>
+ <plugins>
+ <plugin id="local" type="runner" load="galaxy.jobs.runners.local:LocalJobRunner" workers="4"/>
+ </plugins>
+ <handlers>
+ <handler id="main"/>
+ </handlers>
+ <destinations>
+ <destination id="local" runner="local"/>
+ </destinations>
+ <limits>
+ <limit type="walltime">00:10:00</limit>
+ </limits>
+</job_conf>
+'''
+
+# Create a blank shed_tool_conf.xml to define the installed repositories.
+shed_tool_conf_xml_template = '''<?xml version="1.0"?>
+<toolbox tool_path="${shed_tool_path}">
+</toolbox>
+'''
+
+# Since we will be running functional tests we'll need the upload tool, but the rest can be omitted.
+tool_conf_xml = '''<?xml version="1.0"?>
+<toolbox>
+ <section name="Get Data" id="getext">
+ <tool file="data_source/upload.xml"/>
+ </section>
+</toolbox>
+'''
+
+# Set up an empty shed_tool_data_table_conf.xml.
+tool_data_table_conf_xml_template = '''<?xml version="1.0"?>
+<tables>
+</tables>
+'''
+
+# Optionally set the environment variable GALAXY_INSTALL_TEST_TOOL_SHEDS_CONF to the location of a
+# tool shed's configuration file that includes the tool shed from which repositories will be installed.
+tool_sheds_conf_xml = '''<?xml version="1.0"?>
+<tool_sheds>
+ <tool_shed name="Galaxy main tool shed" url="http://toolshed.g2.bx.psu.edu/"/>
+ <tool_shed name="Galaxy test tool shed" url="http://testtoolshed.g2.bx.psu.edu/"/>
+</tool_sheds>
+'''
+
+# Should this serve static resources (scripts, images, styles, etc.)?
+STATIC_ENABLED = True
+
+# If we have a tool_data_table_conf.test.xml, set it up to be loaded when the UniverseApplication is started.
+# This allows one to specify a set of tool data that is used exclusively for testing, and not loaded into any
+# Galaxy instance. By default, this will be in the test-data-repo/location directory generated by buildbot_setup.sh.
+if os.path.exists( 'tool_data_table_conf.test.xml' ):
+ additional_tool_data_tables = 'tool_data_table_conf.test.xml'
+ additional_tool_data_path = os.environ.get( 'GALAXY_INSTALL_TEST_EXTRA_TOOL_DATA_PATH',
+ os.path.join( 'test-data-repo', 'location' ) )
+else:
+ additional_tool_data_tables = None
+ additional_tool_data_path = None
+
+# Set up default tool data tables.
+if os.path.exists( 'tool_data_table_conf.xml' ):
+ tool_data_table_conf = 'tool_data_table_conf.xml'
+elif os.path.exists( 'tool_data_table_conf.xml.sample' ):
+ tool_data_table_conf = 'tool_data_table_conf.xml.sample'
+else:
+ tool_data_table_conf = None
+
+# The GALAXY_INSTALL_TEST_TOOL_SHED_URL and GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY environment variables must be
+# set for this script to work correctly. If the value of GALAXY_INSTALL_TEST_TOOL_SHED_URL does not refer to one
+# of the defaults, the GALAXY_INSTALL_TEST_TOOL_SHEDS_CONF must refer to a tool shed configuration file that contains
+# a definition for that tool shed.
+galaxy_tool_shed_url = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_SHED_URL', None )
+tool_shed_api_key = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY', None )
+exclude_list_file = os.environ.get( 'GALAXY_INSTALL_TEST_EXCLUDE_REPOSITORIES', 'install_test_exclude.xml' )
+
+if 'GALAXY_INSTALL_TEST_SECRET' not in os.environ:
+ galaxy_encode_secret = 'changethisinproductiontoo'
+ os.environ[ 'GALAXY_INSTALL_TEST_SECRET' ] = galaxy_encode_secret
+else:
+ galaxy_encode_secret = os.environ[ 'GALAXY_INSTALL_TEST_SECRET' ]
+
+default_galaxy_test_port_min = 10000
+default_galaxy_test_port_max = 10999
+default_galaxy_test_host = '127.0.0.1'
+# The following should be an actual value (not None). If developers manually specify their
+# tests to use the API it will not work unless a master API key is specified.
+default_galaxy_master_api_key = 123456
+
+testing_single_repository_dict = {}
+if 'repository_name' in os.environ and 'repository_owner' in os.environ:
+ testing_single_repository_dict[ 'name' ] = str( os.environ[ 'repository_name' ] )
+ testing_single_repository_dict[ 'owner' ] = str( os.environ[ 'repository_owner' ] )
+ if 'repository_revision' in os.environ:
+ testing_single_repository_dict[ 'changeset_revision' ] = str( os.environ[ 'repository_revision' ] )
+ else:
+ testing_single_repository_dict[ 'changeset_revision' ] = None
+
+
+class ReportResults( Plugin ):
+ '''Simple Nose plugin to record the IDs of all tests run, regardless of success.'''
+ name = "reportresults"
+ passed = {}
+
+ def options( self, parser, env=os.environ ):
+ super( ReportResults, self ).options( parser, env=env )
+
+ def configure(self, options, conf):
+ super( ReportResults, self ).configure( options, conf )
+ if not self.enabled:
+ return
+
+ def addSuccess( self, test ):
+ '''Only record test IDs that correspond to tool functional tests.'''
+ if 'TestForTool' in test.id():
+ test_id = test.id()
+ # Rearrange the test ID to match the format that is produced in test_results.failures
+ test_id_parts = test_id.split( '.' )
+ fixed_test_id = '%s (%s)' % ( test_id_parts[ -1 ], '.'.join( test_id_parts[ :-1 ] ) )
+ test_parts = fixed_test_id.split( '/' )
+ owner = test_parts[ -4 ]
+ name = test_parts[ -3 ]
+ test_identifier = '%s/%s' % ( owner, name )
+ if test_identifier not in self.passed:
+ self.passed[ test_identifier ] = []
+ self.passed[ test_identifier ].append( fixed_test_id )
+
+ def getTestStatus( self, test_identifier ):
+ if test_identifier in self.passed:
+ passed_tests = self.passed[ test_identifier ]
+ del self.passed[ test_identifier ]
+ return passed_tests
+ return []
+
+def get_api_url( base, parts=[], params=None ):
+ if 'api' in parts and parts.index( 'api' ) != 0:
+ parts.pop( parts.index( 'api' ) )
+ parts.insert( 0, 'api' )
+ elif 'api' not in parts:
+ parts.insert( 0, 'api' )
+ url = suc.url_join( base, *parts )
+ if params is not None:
+ query_string = urllib.urlencode( params )
+ url += '?%s' % query_string
+ return url
+
def get_database_version( app ):
'''
This method returns the value of the version column from the migrate_version table, using the provided app's SQLAlchemy session to determine
@@ -37,6 +207,107 @@
break
return version
+def get_latest_downloadable_changeset_revision( url, name, owner ):
+ error_message = ''
+ parts = [ 'api', 'repositories', 'get_ordered_installable_revisions' ]
+ params = dict( name=name, owner=owner )
+ api_url = get_api_url( base=url, parts=parts, params=params )
+ changeset_revisions, error_message = json_from_url( api_url )
+ if error_message:
+ return None, error_message
+ if changeset_revisions:
+ return changeset_revisions[ -1 ], error_message
+ else:
+ return suc.INITIAL_CHANGELOG_HASH, error_message
+
+def get_missing_tool_dependencies( repository ):
+ log.debug( 'Checking %s repository %s for missing tool dependencies.' % ( repository.status, repository.name ) )
+ missing_tool_dependencies = repository.missing_tool_dependencies
+ for tool_dependency in repository.tool_dependencies:
+ log.debug( 'Tool dependency %s version %s has status %s.' % ( tool_dependency.name, tool_dependency.version, tool_dependency.status ) )
+ for repository_dependency in repository.repository_dependencies:
+ if repository_dependency.includes_tool_dependencies:
+ missing_tool_dependencies.extend( get_missing_tool_dependencies( repository_dependency ) )
+ return missing_tool_dependencies
+
+def get_repositories_to_install( tool_shed_url ):
+ """
+ Get a list of repository info dicts to install. This method expects a json list of dicts with the following structure:
+ [{ "changeset_revision": <revision>,
+ "encoded_repository_id": <encoded repository id from the tool shed>,
+ "name": <name>,
+ "owner": <owner>,
+ "tool_shed_url": <url> }]
+ """
+ error_message = ''
+ latest_revision_only = '-check_all_revisions' not in sys.argv
+ if latest_revision_only:
+ log.debug( 'Testing is restricted to the latest downloadable revision in this test run.' )
+ repository_dicts = []
+ parts = [ 'repository_revisions' ]
+ # We'll filter out deprecated repositories from testing since testing them is necessary only if reproducibility
+ # is guaranteed and we currently do not guarantee reproducibility.
+ params = dict( do_not_test='false',
+ downloadable='true',
+ includes_tools='true',
+ malicious='false',
+ missing_test_components='false',
+ skip_tool_test='false' )
+ api_url = get_api_url( base=tool_shed_url, parts=parts, params=params )
+ baseline_repository_dicts, error_message = json_from_url( api_url )
+ if error_message:
+ return None, error_message
+ for baseline_repository_dict in baseline_repository_dicts:
+ # We need to get some details from the tool shed API, such as repository name and owner, to pass on to the
+ # module that will generate the install methods.
+ repository_dict, error_message = \
+ get_repository_dict( galaxy_tool_shed_url, baseline_repository_dict )
+ if error_message:
+ log.debug( 'Error getting additional details from the API: %s' % str( error_message ) )
+ else:
+ # Don't test deprecated repositories since testing them is necessary only if reproducibility is guaranteed
+ # and we are not currently guaranteeing reproducibility.
+ deprecated = asbool( repository_dict.get( 'deprecated', False ) )
+ if not deprecated:
+ # Don't test empty repositories.
+ changeset_revision = baseline_repository_dict[ 'changeset_revision' ]
+ if changeset_revision != suc.INITIAL_CHANGELOG_HASH:
+ # Merge the dictionary returned from /api/repository_revisions with the detailed repository_dict and
+ # append it to the list of repository_dicts to install and test.
+ if latest_revision_only:
+ latest_revision = repository_dict[ 'latest_revision' ]
+ if changeset_revision == latest_revision:
+ repository_dicts.append( dict( repository_dict.items() + baseline_repository_dict.items() ) )
+ else:
+ repository_dicts.append( dict( repository_dict.items() + baseline_repository_dict.items() ) )
+ if testing_single_repository_dict:
+ tsr_name = testing_single_repository_dict[ 'name' ]
+ tsr_owner = testing_single_repository_dict[ 'owner' ]
+ tsr_changeset_revision = testing_single_repository_dict[ 'changeset_revision' ]
+ log.debug( 'Testing single repository with name %s and owner %s.' % ( str( tsr_name ), str( tsr_owner ) ) )
+ for repository_to_install in repository_dicts:
+ rti_name = repository_to_install[ 'name' ]
+ rti_owner = repository_to_install[ 'owner' ]
+ rti_changeset_revision = repository_to_install[ 'changeset_revision' ]
+ if rti_name == tsr_name and rti_owner == tsr_owner:
+ if tsr_changeset_revision is None:
+ return [ repository_to_install ], error_message
+ else:
+ if tsr_changeset_revision == rti_changeset_revision:
+ return repository_dicts, error_message
+ return repository_dicts, error_message
+ # Get a list of repositories to test from the tool shed specified in the GALAXY_INSTALL_TEST_TOOL_SHED_URL
+ # environment variable.
+ log.debug( "The Tool Shed's API url...\n%s" % str( api_url ) )
+ log.debug( "...retrieved %d repository revisions for testing." % len( repository_dicts ) )
+ log.debug( "Repository revisions for testing:" )
+ for repository_dict in repository_dicts:
+ name = str( repository_dict.get( 'name', None ) )
+ owner = str( repository_dict.get( 'owner', None ) )
+ changeset_revision = str( repository_dict.get( 'changeset_revision', None ) )
+ log.debug( "Revision %s of repository %s owned by %s" % ( changeset_revision, name, owner ) )
+ return repository_dicts, error_message
+
def get_repository_current_revision( repo_path ):
'''
This method uses the python mercurial API to get the current working directory's mercurial changeset hash. Note that if the author of mercurial
@@ -52,3 +323,415 @@
ctx_rev = changectx.rev()
hg_id = '%d:%s' % ( ctx_rev, str( changectx ) )
return hg_id
+
+def get_repository_dict( url, repository_dict ):
+ error_message = ''
+ parts = [ 'api', 'repositories', repository_dict[ 'repository_id' ] ]
+ api_url = get_api_url( base=url, parts=parts )
+ extended_dict, error_message = json_from_url( api_url )
+ if error_message:
+ return None, error_message
+ name = str( extended_dict[ 'name' ] )
+ owner = str( extended_dict[ 'owner' ] )
+ latest_changeset_revision, error_message = get_latest_downloadable_changeset_revision( url, name, owner )
+ if error_message:
+ return None, error_message
+ extended_dict[ 'latest_revision' ] = str( latest_changeset_revision )
+ return extended_dict, error_message
+
+def get_repository_tuple_from_elem( elem ):
+ attributes = elem.attrib
+ name = attributes.get( 'name', None )
+ owner = attributes.get( 'owner', None )
+ changeset_revision = attributes.get( 'changeset_revision', None )
+ return ( name, owner, changeset_revision )
+
+def get_static_settings():
+ """
+ Return a dictionary of the settings necessary for a Galaxy application to be wrapped in the static
+ middleware. This mainly consists of the file system locations of url-mapped static resources.
+ """
+ cwd = os.getcwd()
+ static_dir = os.path.join( cwd, 'static' )
+ #TODO: these should be copied from universe_wsgi.ini
+ #TODO: static_enabled needed here?
+ return dict( static_enabled = True,
+ static_cache_time = 360,
+ static_dir = static_dir,
+ static_images_dir = os.path.join( static_dir, 'images', '' ),
+ static_favicon_dir = os.path.join( static_dir, 'favicon.ico' ),
+ static_scripts_dir = os.path.join( static_dir, 'scripts', '' ),
+ static_style_dir = os.path.join( static_dir, 'june_2007_style', 'blue' ),
+ static_robots_txt = os.path.join( static_dir, 'robots.txt' ) )
+
+def get_tool_test_results_dict( tool_test_results_dicts ):
+ if tool_test_results_dicts:
+ # Inspect the tool_test_results_dict for the last test run to make sure it contains only a test_environment
+ # entry. If it contains more entries, then the script ~/tool_shed/api/check_repositories_for_functional_tests.py
+ # was not executed in preparation for this script's execution, so we'll just create an empty dictionary.
+ tool_test_results_dict = tool_test_results_dicts[ 0 ]
+ if len( tool_test_results_dict ) <= 1:
+ # We can re-use the mostly empty tool_test_results_dict for this run because it is either empty or it contains only
+ # a test_environment entry. If we use it we need to temporarily eliminate it from the list of tool_test_results_dicts
+ # since it will be re-inserted later.
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
+ elif len( tool_test_results_dict ) == 2 and \
+ 'test_environment' in tool_test_results_dict and 'missing_test_components' in tool_test_results_dict:
+ # We can re-use tool_test_results_dict if its only entries are "test_environment" and "missing_test_components".
+ # In this case, some tools are missing tests components while others are not.
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
+ else:
+ # The latest tool_test_results_dict has been populated with the results of a test run, so it cannot be used.
+ tool_test_results_dict = {}
+ else:
+ # Create a new dictionary for this test test run,
+ tool_test_results_dict = {}
+ return tool_test_results_dict
+
+def get_tool_test_results_dicts( tool_shed_url, encoded_repository_metadata_id ):
+ """
+ Return the list of dictionaries contained in the Tool Shed's repository_metadata.tool_test_results
+ column via the Tool Shed API.
+ """
+ error_message = ''
+ parts = [ 'api', 'repository_revisions', encoded_repository_metadata_id ]
+ api_url = get_api_url( base=tool_shed_url, parts=parts )
+ repository_metadata, error_message = json_from_url( api_url )
+ if error_message:
+ return None, error_message
+ # The tool_test_results used to be stored as a single dictionary rather than a list, but we currently
+ # return a list.
+ tool_test_results = listify( repository_metadata.get( 'tool_test_results', [] ) )
+ return tool_test_results, error_message
+
+def get_webapp_global_conf():
+ """Return the global_conf dictionary sent as the first argument to app_factory."""
+ global_conf = {}
+ if STATIC_ENABLED:
+ global_conf.update( get_static_settings() )
+ return global_conf
+
+def handle_missing_dependencies( app, repository, missing_tool_dependencies, repository_dict, tool_test_results_dicts,
+ tool_test_results_dict, results_dict, can_update_tool_shed ):
+ """Handle missing repository or tool dependencies for an installed repository."""
+ # If a tool dependency fails to install correctly, this should be considered an installation error,
+ # and functional tests should be skipped, since the tool dependency needs to be correctly installed
+ # for the test to be considered reliable.
+ log.debug( 'The following dependencies of this repository are missing, so skipping functional tests.' )
+ # In keeping with the standard display layout, add the error message to the dict for each tool individually.
+ for dependency in repository.missing_tool_dependencies:
+ name = str( dependency.name )
+ type = str( dependency.type )
+ version = str( dependency.version )
+ error_message = unicodify( dependency.error_message )
+ log.debug( 'Missing tool dependency %s of type %s version %s: %s' % ( name, type, version, error_message ) )
+ test_result = dict( type=dependency.type,
+ name=dependency.name,
+ version=dependency.version,
+ error_message=dependency.error_message )
+ tool_test_results_dict[ 'installation_errors' ][ 'tool_dependencies' ].append( test_result )
+ for dependency in repository.missing_repository_dependencies:
+ name = str( dependency.name )
+ owner = str( dependency.owner )
+ changeset_revision = str( dependency.changeset_revision )
+ error_message = unicodify( dependency.error_message )
+ log.debug( 'Missing repository dependency %s changeset revision %s owned by %s: %s' % \
+ ( name, changeset_revision, owner, error_message ) )
+ test_result = dict( tool_shed=dependency.tool_shed,
+ name=dependency.name,
+ owner=dependency.owner,
+ changeset_revision=dependency.changeset_revision,
+ error_message=dependency.error_message )
+ tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ].append( test_result )
+ # Record the status of this repository in the tool shed.
+ params = dict( tools_functionally_correct=False,
+ do_not_test=False,
+ test_install_error=True )
+ # TODO: do something useful with response_dict
+ response_dict = register_test_result( galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
+ # Since this repository is missing components we do not want to test it, uninstall it.
+ uninstall_repository_and_repository_dependencies( app, repository_dict )
+ results_dict[ 'repositories_failed_install' ].append( dict( name=str( repository.name ),
+ owner=str( repository.owner ),
+ changeset_revision=str( repository.changeset_revision ) ) )
+ return results_dict
+
+def initialize_results_dict():
+ # Initialize a dictionary for the summary that will be printed to stdout.
+ results_dict = {}
+ results_dict[ 'total_repositories_tested' ] = 0
+ results_dict[ 'all_tests_passed' ] = []
+ results_dict[ 'at_least_one_test_failed' ] = []
+ results_dict[ 'repositories_failed_install' ] = []
+ return results_dict
+
+def initialize_tool_tests_results_dict( app, tool_test_results_dict ):
+ test_environment_dict = tool_test_results_dict.get( 'test_environment', {} )
+ if len( test_environment_dict ) == 0:
+ # Set information about the tool shed to nothing since we cannot currently determine it from here.
+ # We could eventually add an API method...
+ test_environment_dict = dict( tool_shed_database_version='',
+ tool_shed_mercurial_version='',
+ tool_shed_revision='' )
+ # Add the current time as the approximate time that this test run occurs. A similar value will also be
+ # set to the repository_metadata.time_last_tested column, but we also store it here because the Tool Shed
+ # may be configured to store multiple test run results, so each must be associated with a time stamp.
+ now = time.strftime( "%Y-%m-%d %H:%M:%S" )
+ # Add information about the current platform.
+ test_environment_dict[ 'time_tested' ] = now
+ test_environment_dict[ 'python_version' ] = platform.python_version()
+ test_environment_dict[ 'architecture' ] = platform.machine()
+ operating_system, hostname, operating_system_version, uname, arch, processor = platform.uname()
+ test_environment_dict[ 'system' ] = '%s %s' % ( operating_system, operating_system_version )
+ # Add information about the current Galaxy environment.
+ test_environment_dict[ 'galaxy_database_version' ] = get_database_version( app )
+ test_environment_dict[ 'galaxy_revision' ] = get_repository_current_revision( os.getcwd() )
+ # Initialize and populate the tool_test_results_dict.
+ tool_test_results_dict[ 'test_environment' ] = test_environment_dict
+ tool_test_results_dict[ 'passed_tests' ] = []
+ tool_test_results_dict[ 'failed_tests' ] = []
+ tool_test_results_dict[ 'installation_errors' ] = dict( current_repository=[],
+ repository_dependencies=[],
+ tool_dependencies=[] )
+ return tool_test_results_dict
+
+def install_repository( app, repository_dict ):
+ """Install a repository defined by the received repository_dict from the tool shed into Galaxy."""
+ name = str( repository_dict[ 'name' ] )
+ owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ error_message = ''
+ repository = None
+ log.debug( "Installing revision %s of repository %s owned by %s." % ( changeset_revision, name, owner ) )
+ # Use the repository information dictionary to generate an install method that will install the repository into the
+ # embedded Galaxy application, with tool dependencies and repository dependencies, if any.
+ test_install_repositories.generate_install_method( repository_dict )
+ # Configure nose to run the install method as a test.
+ test_config = nose.config.Config( env=os.environ, plugins=nose.plugins.manager.DefaultPluginManager() )
+ test_config.configure( sys.argv )
+ # Run the configured install method as a test. This method uses the embedded Galaxy application's web interface to
+ # install the specified repository with tool and repository dependencies also selected for installation.
+ result, _ = run_tests( test_config )
+ try:
+ repository = test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
+ except Exception, e:
+ error_message = 'Error getting revision %s of repository %s owned by %s: %s' % ( changeset_revision, name, owner, str( e ) )
+ log.exception( error_message )
+ return repository, error_message
+
+def is_latest_downloadable_revision( url, repository_dict ):
+ name = str( repository_dict[ 'name' ] )
+ owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ latest_revision = get_latest_downloadable_changeset_revision( url, name=name, owner=owner )
+ return changeset_revision == str( latest_revision )
+
+def json_from_url( url ):
+ error_message = ''
+ url_handle = urllib.urlopen( url )
+ url_contents = url_handle.read()
+ try:
+ parsed_json = from_json_string( url_contents )
+ except Exception, e:
+ error_message = str( url_contents )
+ log.exception( 'Error parsing JSON data in json_from_url(): %s.' % str( e ) )
+ return None, error_message
+ return parsed_json, error_message
+
+def parse_exclude_list( xml_filename ):
+ """Return a list of repositories to exclude from testing."""
+ # This method should return a list with the following structure:
+ # [{ 'reason': The default reason or the reason specified in this section,
+ # 'repositories': [( name, owner, changeset revision if changeset revision else None ),
+ # ( name, owner, changeset revision if changeset revision else None )]}]
+ exclude_list = []
+ exclude_verbose = []
+ xml_tree, error_message = parse_xml( xml_filename )
+ if error_message:
+ log.debug( 'The xml document %s defining the exclude list is invalid, so no repositories will be excluded from testing: %s' % \
+ ( str( xml_filename ), str( error_message ) ) )
+ return exclude_list
+ tool_sheds = xml_tree.findall( 'repositories' )
+ xml_element = []
+ exclude_count = 0
+ for tool_shed in tool_sheds:
+ if galaxy_tool_shed_url != tool_shed.attrib[ 'tool_shed' ]:
+ continue
+ else:
+ xml_element = tool_shed
+ for reason_section in xml_element:
+ reason_text = reason_section.find( 'text' ).text
+ repositories = reason_section.findall( 'repository' )
+ exclude_dict = dict( reason=reason_text, repositories=[] )
+ for repository in repositories:
+ repository_tuple = get_repository_tuple_from_elem( repository )
+ if repository_tuple not in exclude_dict[ 'repositories' ]:
+ exclude_verbose.append( repository_tuple )
+ exclude_count += 1
+ exclude_dict[ 'repositories' ].append( repository_tuple )
+ exclude_list.append( exclude_dict )
+ log.debug( 'The xml document %s containing the exclude list defines the following %s repositories to be excluded from testing...' % \
+ ( str( xml_filename ), str( exclude_count ) ) )
+ #if '-list_repositories' in sys.argv:
+ for name, owner, changeset_revision in exclude_verbose:
+ if changeset_revision:
+ log.debug( 'Repository %s owned by %s, changeset revision %s.' % ( str( name ), str( owner ), str( changeset_revision ) ) )
+ else:
+ log.debug( 'Repository %s owned by %s, all revisions.' % ( str( name ), str( owner ) ) )
+ return exclude_list
+
+def register_test_result( url, tool_test_results_dicts, tool_test_results_dict, repository_dict, params, can_update_tool_shed ):
+ """
+ Update the repository metadata tool_test_results and appropriate flags using the Tool SHed API. This method
+ updates tool_test_results with the relevant data, sets the do_not_test and tools_functionally correct flags
+ to the appropriate values and updates the time_last_tested field to the value of the received time_tested.
+ """
+ if can_update_tool_shed:
+ metadata_revision_id = repository_dict.get( 'id', None )
+ if metadata_revision_id is not None:
+ tool_test_results_dicts.insert( 0, tool_test_results_dict )
+ params[ 'tool_test_results' ] = tool_test_results_dicts
+ # Set the time_last_tested entry so that the repository_metadata.time_last_tested will be set in the tool shed.
+ params[ 'time_last_tested' ] = 'This entry will result in this value being set via the Tool Shed API.'
+ url = '%s' % ( suc.url_join( galaxy_tool_shed_url,'api', 'repository_revisions', str( metadata_revision_id ) ) )
+ try:
+ return update( tool_shed_api_key, url, params, return_formatted=False )
+ except Exception, e:
+ log.exception( 'Error attempting to register test results: %s' % str( e ) )
+ return {}
+ else:
+ return {}
+
+def run_tests( test_config ):
+ loader = nose.loader.TestLoader( config=test_config )
+ test_config.plugins.addPlugin( ReportResults() )
+ plug_loader = test_config.plugins.prepareTestLoader( loader )
+ if plug_loader is not None:
+ loader = plug_loader
+ tests = loader.loadTestsFromNames( test_config.testNames )
+ test_runner = nose.core.TextTestRunner( stream=test_config.stream,
+ verbosity=test_config.verbosity,
+ config=test_config )
+ plug_runner = test_config.plugins.prepareTestRunner( test_runner )
+ if plug_runner is not None:
+ test_runner = plug_runner
+ result = test_runner.run( tests )
+ return result, test_config.plugins._plugins
+
+def show_summary_output( repository_dicts ):
+ # Group summary display by repository owner.
+ repository_dicts_by_owner = {}
+ for repository_dict in repository_dicts:
+ name = str( repository_dict[ 'name' ] )
+ owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ if owner in repository_dicts_by_owner:
+ repository_dicts_by_owner[ owner ].append( repository_dict )
+ else:
+ repository_dicts_by_owner[ owner ] = [ repository_dict ]
+ # Display grouped summary.
+ for owner, grouped_repository_dicts in repository_dicts_by_owner.items():
+ print "# "
+ for repository_dict in grouped_repository_dicts:
+ name = str( repository_dict[ 'name' ] )
+ owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ print "# Revision %s of repository %s owned by %s" % ( changeset_revision, name, owner )
+
+def uninstall_repository_and_repository_dependencies( app, repository_dict ):
+ """Uninstall a repository and all of its repository dependencies."""
+ # This method assumes that the repositor defined by the received repository_dict is not a repository
+ # dependency of another repository.
+ sa_session = app.install_model.context
+ # The dict contains the only repository the app should have installed at this point.
+ name = str( repository_dict[ 'name' ] )
+ owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ # Since this install and test framework uninstalls repositories immediately after installing and testing
+ # them, the values of repository.installed_changeset_revision and repository.changeset_revision should be
+ # the same.
+ repository = test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
+ if repository.can_uninstall( app ):
+ # A repository can be uninstalled only if no dependent repositories are installed. So uninstallation order
+ # id critical. A repository is always uninstalled first, and the each of its dependencies is checked to see
+ # if it can be uninstalled.
+ uninstall_repository_dict = dict( name=name,
+ owner=owner,
+ changeset_revision=changeset_revision )
+ log.debug( 'Revision %s of repository %s owned by %s selected for uninstallation.' % ( changeset_revision, name, owner ) )
+ try:
+ test_install_repositories.generate_uninstall_method( uninstall_repository_dict )
+ # Set up nose to run the generated uninstall method as a functional test.
+ test_config = nose.config.Config( env=os.environ, plugins=nose.plugins.manager.DefaultPluginManager() )
+ test_config.configure( sys.argv )
+ # Run the uninstall method. This method uses the Galaxy web interface to uninstall the previously installed
+ # repository and all of its repository dependencies, deleting each of them from disk.
+ result, _ = run_tests( test_config )
+ repository_uninstall_successful = result.wasSuccessful()
+ except Exception, e:
+ repository_uninstall_successful = False
+ log.exception( 'Uninstallation of revision %s of repository %s owned by %s failed: %s.' % \
+ ( rd_changeset_revision, rd_name, rd_owner, str( e ) ) )
+ if repository_uninstall_successful:
+ # Now that the repository is uninstalled we can attempt to uninstall each of its repository dependencies.
+ # We have to do this through Twill in order to maintain app.toolbox and shed_tool_conf.xml in a state that
+ # is valid for future tests. Since some of the repository's repository dependencies may require other of
+ # the repository's repository dependencies, we'll keep track of the repositories we've been able to unistall.
+ processed_repository_dependency_ids = []
+ while len( processed_repository_dependency_ids ) < len( repository.repository_dependencies ):
+ for repository_dependency in repository.repository_dependencies:
+ if repository_dependency.id not in processed_repository_dependency_ids and repository_dependency.can_uninstall( app ):
+ processed_repository_dependency_ids.append( repository_dependency.id )
+ rd_name = str( repository_dependency.name )
+ rd_owner = str( repository_dependency.owner )
+ rd_changeset_revision = str( repository_dependency.changeset_revision )
+ uninstall_repository_dict = dict( name=rd_name,
+ owner=rd_owner,
+ changeset_revision=rd_changeset_revision )
+ log.debug( 'Revision %s of repository dependency %s owned by %s selected for uninstallation.' % \
+ ( rd_changeset_revision, rd_name, rd_owner ) )
+ # Generate a test method to uninstall the repository dependency through the embedded Galaxy application's
+ # web interface.
+ try:
+ test_install_repositories.generate_uninstall_method( uninstall_repository_dict )
+ # Set up nose to run the generated uninstall method as a functional test.
+ test_config = nose.config.Config( env=os.environ, plugins=nose.plugins.manager.DefaultPluginManager() )
+ test_config.configure( sys.argv )
+ # Run the uninstall method.
+ result, _ = run_tests( test_config )
+ if not result.wasSuccessful():
+ # We won't set ok here because we'll continue to uninstall whatever we can.
+ log.debug( 'Uninstallation of revision %s of repository %s owned by %s failed.' % \
+ ( rd_changeset_revision, rd_name, rd_owner ) )
+ except Exception, e:
+ log.exception( 'Uninstallation of revision %s of repository %s owned by %s failed: %s.' % \
+ ( rd_changeset_revision, rd_name, rd_owner, str( e ) ) )
+ else:
+ log.debug( 'Uninstallation of revision %s of repository %s owned by %s failed.' % ( changeset_revision, name, owner ) )
+ else:
+ log_reason_repository_cannot_be_uninstalled( app, repository )
+
+def uninstall_tool_dependency( app, tool_dependency ):
+ """Attempt to uninstall a tool dependency."""
+ sa_session = app.install_model.context
+ # Clean out any generated tests. This is necessary for Twill.
+ tool_dependency_install_path = tool_dependency.installation_directory( app )
+ uninstalled, error_message = tool_dependency_util.remove_tool_dependency( app, tool_dependency )
+ if error_message:
+ log.debug( 'There was an error attempting to remove directory: %s' % str( tool_dependency_install_path ) )
+ log.debug( error_message )
+ else:
+ log.debug( 'Successfully removed tool dependency installation directory: %s' % str( tool_dependency_install_path ) )
+ if not uninstalled or tool_dependency.status != app.model.ToolDependency.installation_status.UNINSTALLED:
+ tool_dependency.status = app.model.ToolDependency.installation_status.UNINSTALLED
+ sa_session.add( tool_dependency )
+ sa_session.flush()
+ if os.path.exists( tool_dependency_install_path ):
+ log.debug( 'Uninstallation of tool dependency succeeded, but the installation path still exists on the filesystem. It is now being explicitly deleted.')
+ suc.remove_dir( tool_dependency_install_path )
+
diff -r ce186eb5fefcb7ff332c73bd0869b9e222293d0b -r 0a6f8a07b0c3ca8d58302cb3714ed01d132e52ec test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -24,13 +24,11 @@
# http://code.google.com/p/python-nose/issues/detail?id=284
eggs.require( "pysqlite" )
-test_toolbox = None
import httplib
import install_and_test_tool_shed_repositories.base.test_db_util as test_db_util
import install_and_test_tool_shed_repositories.functional.test_install_repositories as test_install_repositories
import logging
import nose
-import platform
import random
import re
import shutil
@@ -39,37 +37,27 @@
import tempfile
import time
import threading
-import tool_shed.util.shed_util_common as suc
-import urllib
-from base.tool_shed_util import log_reason_repository_cannot_be_uninstalled
+import install_and_test_tool_shed_repositories.base.util as install_and_test_base_util
+
from base.tool_shed_util import parse_tool_panel_config
-from install_and_test_tool_shed_repositories.base.util import get_database_version
-from install_and_test_tool_shed_repositories.base.util import get_repository_current_revision
-from common import update
-from datetime import datetime
+
from galaxy.app import UniverseApplication
-from galaxy.util import asbool
-from galaxy.util import listify
-from galaxy.util import unicodify
from galaxy.util.json import from_json_string
from galaxy.util.json import to_json_string
from galaxy.web import buildapp
-from galaxy.web.framework.helpers import time_ago
from functional_tests import generate_config_file
-from mercurial import __version__
from nose.plugins import Plugin
from paste import httpserver
-from tool_shed.util import tool_dependency_util
-from tool_shed.util.xml_util import parse_xml
from functional import database_contexts
log = logging.getLogger( 'install_and_test_repositories' )
assert sys.version_info[ :2 ] >= ( 2, 6 )
-test_home_directory = os.path.join( cwd, 'test', 'install_and_test_tool_shed_repositories' )
-default_test_file_dir = os.path.join( test_home_directory, 'test_data' )
+test_home_directory = os.path.join( cwd, 'test', 'install_and_test_tool_shed_repositories', 'repositories_with_tools' )
+
+test_toolbox = None
# Here's the directory where everything happens. Temporary directories are created within this directory to contain
# the database, new repositories, etc.
@@ -78,184 +66,12 @@
default_galaxy_test_file_dir = "test-data"
os.environ[ 'GALAXY_INSTALL_TEST_TMP_DIR' ] = galaxy_test_tmp_dir
-default_galaxy_test_port_min = 10000
-default_galaxy_test_port_max = 10999
-default_galaxy_test_host = '127.0.0.1'
-# The following should be an actual value (not None). If developers manually specify their
-# tests to use the API it will not work unless a master API key is specified.
-default_galaxy_master_api_key = 123456
-
# This script can be run in such a way that no Tool Shed database records should be changed.
if '-info_only' in sys.argv or 'GALAXY_INSTALL_TEST_INFO_ONLY' in os.environ:
can_update_tool_shed = False
else:
can_update_tool_shed = True
-# Should this serve static resources (scripts, images, styles, etc.)?
-STATIC_ENABLED = True
-
-# Set up a job_conf.xml that explicitly limits jobs to 10 minutes.
-job_conf_xml = '''<?xml version="1.0"?>
-<!-- A test job config that explicitly configures job running the way it is configured by default (if there is no explicit config). -->
-<job_conf>
- <plugins>
- <plugin id="local" type="runner" load="galaxy.jobs.runners.local:LocalJobRunner" workers="4"/>
- </plugins>
- <handlers>
- <handler id="main"/>
- </handlers>
- <destinations>
- <destination id="local" runner="local"/>
- </destinations>
- <limits>
- <limit type="walltime">00:10:00</limit>
- </limits>
-</job_conf>
-'''
-
-# Create a blank shed_tool_conf.xml to define the installed repositories.
-shed_tool_conf_xml_template = '''<?xml version="1.0"?>
-<toolbox tool_path="${shed_tool_path}">
-</toolbox>
-'''
-
-# Since we will be running functional tests we'll need the upload tool, but the rest can be omitted.
-tool_conf_xml = '''<?xml version="1.0"?>
-<toolbox>
- <section name="Get Data" id="getext">
- <tool file="data_source/upload.xml"/>
- </section>
-</toolbox>
-'''
-
-# Set up an empty shed_tool_data_table_conf.xml.
-tool_data_table_conf_xml_template = '''<?xml version="1.0"?>
-<tables>
-</tables>
-'''
-
-# Optionally set the environment variable GALAXY_INSTALL_TEST_TOOL_SHEDS_CONF to the location of a
-# tool shed's configuration file that includes the tool shed from which repositories will be installed.
-tool_sheds_conf_xml = '''<?xml version="1.0"?>
-<tool_sheds>
- <tool_shed name="Galaxy main tool shed" url="http://toolshed.g2.bx.psu.edu/"/>
- <tool_shed name="Galaxy test tool shed" url="http://testtoolshed.g2.bx.psu.edu/"/>
-</tool_sheds>
-'''
-
-# If we have a tool_data_table_conf.test.xml, set it up to be loaded when the UniverseApplication is started.
-# This allows one to specify a set of tool data that is used exclusively for testing, and not loaded into any
-# Galaxy instance. By default, this will be in the test-data-repo/location directory generated by buildbot_setup.sh.
-if os.path.exists( 'tool_data_table_conf.test.xml' ):
- additional_tool_data_tables = 'tool_data_table_conf.test.xml'
- additional_tool_data_path = os.environ.get( 'GALAXY_INSTALL_TEST_EXTRA_TOOL_DATA_PATH',
- os.path.join( 'test-data-repo', 'location' ) )
-else:
- additional_tool_data_tables = None
- additional_tool_data_path = None
-
-# Set up default tool data tables.
-if os.path.exists( 'tool_data_table_conf.xml' ):
- tool_data_table_conf = 'tool_data_table_conf.xml'
-elif os.path.exists( 'tool_data_table_conf.xml.sample' ):
- tool_data_table_conf = 'tool_data_table_conf.xml.sample'
-else:
- tool_data_table_conf = None
-
-# The GALAXY_INSTALL_TEST_TOOL_SHED_URL and GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY environment variables must be
-# set for this script to work correctly. If the value of GALAXY_INSTALL_TEST_TOOL_SHED_URL does not refer to one
-# of the defaults, the GALAXY_INSTALL_TEST_TOOL_SHEDS_CONF must refer to a tool shed configuration file that contains
-# a definition for that tool shed.
-galaxy_tool_shed_url = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_SHED_URL', None )
-tool_shed_api_key = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY', None )
-exclude_list_file = os.environ.get( 'GALAXY_INSTALL_TEST_EXCLUDE_REPOSITORIES', 'install_test_exclude.xml' )
-
-if 'GALAXY_INSTALL_TEST_SECRET' not in os.environ:
- galaxy_encode_secret = 'changethisinproductiontoo'
- os.environ[ 'GALAXY_INSTALL_TEST_SECRET' ] = galaxy_encode_secret
-else:
- galaxy_encode_secret = os.environ[ 'GALAXY_INSTALL_TEST_SECRET' ]
-
-testing_single_repository_dict = {}
-if 'repository_name' in os.environ and 'repository_owner' in os.environ:
- testing_single_repository_dict[ 'name' ] = str( os.environ[ 'repository_name' ] )
- testing_single_repository_dict[ 'owner' ] = str( os.environ[ 'repository_owner' ] )
- if 'repository_revision' in os.environ:
- testing_single_repository_dict[ 'changeset_revision' ] = str( os.environ[ 'repository_revision' ] )
- else:
- testing_single_repository_dict[ 'changeset_revision' ] = None
-
-
-class ReportResults( Plugin ):
- '''Simple Nose plugin to record the IDs of all tests run, regardless of success.'''
- name = "reportresults"
- passed = {}
-
- def options( self, parser, env=os.environ ):
- super( ReportResults, self ).options( parser, env=env )
-
- def configure(self, options, conf):
- super( ReportResults, self ).configure( options, conf )
- if not self.enabled:
- return
-
- def addSuccess( self, test ):
- '''Only record test IDs that correspond to tool functional tests.'''
- if 'TestForTool' in test.id():
- test_id = test.id()
- # Rearrange the test ID to match the format that is produced in test_results.failures
- test_id_parts = test_id.split( '.' )
- fixed_test_id = '%s (%s)' % ( test_id_parts[ -1 ], '.'.join( test_id_parts[ :-1 ] ) )
- test_parts = fixed_test_id.split( '/' )
- owner = test_parts[ -4 ]
- name = test_parts[ -3 ]
- test_identifier = '%s/%s' % ( owner, name )
- if test_identifier not in self.passed:
- self.passed[ test_identifier ] = []
- self.passed[ test_identifier ].append( fixed_test_id )
-
- def getTestStatus( self, test_identifier ):
- if test_identifier in self.passed:
- passed_tests = self.passed[ test_identifier ]
- del self.passed[ test_identifier ]
- return passed_tests
- return []
-
-def deactivate_repository( app, repository_dict ):
- sa_session = app.install_model.context
- # Clean out any generated tests. This is necessary for Twill.
- remove_generated_tests( app )
- # The dict contains the only repository the app should have installed at this point.
- name = str( repository_dict[ 'name' ] )
- owner = str( repository_dict[ 'owner' ] )
- changeset_revision = str( repository_dict[ 'changeset_revision' ] )
- repository = test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
- repository_dict_for_deactivation = dict( name=name,
- owner=owner,
- changeset_revision=changeset_revision )
- log.debug( 'Changeset revision %s of repository %s owned by %s selected for deactivation.' % ( changeset_revision, name, owner ) )
- test_install_repositories.generate_deactivate_method( repository_dict_for_deactivation )
- # Set up nose to run the generated uninstall method as a functional test.
- test_config = nose.config.Config( env=os.environ, plugins=nose.plugins.manager.DefaultPluginManager() )
- test_config.configure( sys.argv )
- # Run the deactivate method which uses the Galaxy web interface to deactivate the repository.
- result, _ = run_tests( test_config )
- success = result.wasSuccessful()
- if not success:
- log.debug( 'Deactivation of revision %s repository %s owned by %s failed.' % ( changeset_revision, name, owner ) )
-
-def get_api_url( base, parts=[], params=None ):
- if 'api' in parts and parts.index( 'api' ) != 0:
- parts.pop( parts.index( 'api' ) )
- parts.insert( 0, 'api' )
- elif 'api' not in parts:
- parts.insert( 0, 'api' )
- url = suc.url_join( base, *parts )
- if params is not None:
- query_string = urllib.urlencode( params )
- url += '?%s' % query_string
- return url
-
def get_failed_test_dicts( test_result, from_tool_test=True ):
"""Extract any useful data from the test_result.failures and test_result.errors attributes."""
failed_test_dicts = []
@@ -304,146 +120,6 @@
failed_test_dicts.append( test_status_dict )
return failed_test_dicts
-def get_latest_downloadable_changeset_revision( url, name, owner ):
- error_message = ''
- parts = [ 'api', 'repositories', 'get_ordered_installable_revisions' ]
- params = dict( name=name, owner=owner )
- api_url = get_api_url( base=url, parts=parts, params=params )
- changeset_revisions, error_message = json_from_url( api_url )
- if error_message:
- return None, error_message
- if changeset_revisions:
- return changeset_revisions[ -1 ], error_message
- else:
- return suc.INITIAL_CHANGELOG_HASH, error_message
-
-def get_missing_tool_dependencies( repository ):
- log.debug( 'Checking %s repository %s for missing tool dependencies.' % ( repository.status, repository.name ) )
- missing_tool_dependencies = repository.missing_tool_dependencies
- for tool_dependency in repository.tool_dependencies:
- log.debug( 'Tool dependency %s version %s has status %s.' % ( tool_dependency.name, tool_dependency.version, tool_dependency.status ) )
- for repository_dependency in repository.repository_dependencies:
- if repository_dependency.includes_tool_dependencies:
- missing_tool_dependencies.extend( get_missing_tool_dependencies( repository_dependency ) )
- return missing_tool_dependencies
-
-def get_repository_dict( url, repository_dict ):
- error_message = ''
- parts = [ 'api', 'repositories', repository_dict[ 'repository_id' ] ]
- api_url = get_api_url( base=url, parts=parts )
- extended_dict, error_message = json_from_url( api_url )
- if error_message:
- return None, error_message
- name = str( extended_dict[ 'name' ] )
- owner = str( extended_dict[ 'owner' ] )
- latest_changeset_revision, error_message = get_latest_downloadable_changeset_revision( url, name, owner )
- if error_message:
- return None, error_message
- extended_dict[ 'latest_revision' ] = str( latest_changeset_revision )
- return extended_dict, error_message
-
-def get_repository_tuple_from_elem( elem ):
- attributes = elem.attrib
- name = attributes.get( 'name', None )
- owner = attributes.get( 'owner', None )
- changeset_revision = attributes.get( 'changeset_revision', None )
- return ( name, owner, changeset_revision )
-
-def get_repositories_to_install( tool_shed_url ):
- """
- Get a list of repository info dicts to install. This method expects a json list of dicts with the following structure:
- [{ "changeset_revision": <revision>,
- "encoded_repository_id": <encoded repository id from the tool shed>,
- "name": <name>,
- "owner": <owner>,
- "tool_shed_url": <url> }]
- """
- error_message = ''
- latest_revision_only = '-check_all_revisions' not in sys.argv
- if latest_revision_only:
- log.debug( 'Testing is restricted to the latest downloadable revision in this test run.' )
- repository_dicts = []
- parts = [ 'repository_revisions' ]
- # We'll filter out deprecated repositories from testing since testing them is necessary only if reproducibility
- # is guaranteed and we currently do not guarantee reproducibility.
- params = dict( do_not_test='false',
- downloadable='true',
- includes_tools='true',
- malicious='false',
- missing_test_components='false',
- skip_tool_test='false' )
- api_url = get_api_url( base=tool_shed_url, parts=parts, params=params )
- baseline_repository_dicts, error_message = json_from_url( api_url )
- if error_message:
- return None, error_message
- for baseline_repository_dict in baseline_repository_dicts:
- # We need to get some details from the tool shed API, such as repository name and owner, to pass on to the
- # module that will generate the install methods.
- repository_dict, error_message = get_repository_dict( galaxy_tool_shed_url, baseline_repository_dict )
- if error_message:
- log.debug( 'Error getting additional details from the API: %s' % str( error_message ) )
- else:
- # Don't test deprecated repositories since testing them is necessary only if reproducibility is guaranteed
- # and we are not currently guaranteeing reproducibility.
- deprecated = asbool( repository_dict.get( 'deprecated', False ) )
- if not deprecated:
- # Don't test empty repositories.
- changeset_revision = baseline_repository_dict[ 'changeset_revision' ]
- if changeset_revision != suc.INITIAL_CHANGELOG_HASH:
- # Merge the dictionary returned from /api/repository_revisions with the detailed repository_dict and
- # append it to the list of repository_dicts to install and test.
- if latest_revision_only:
- latest_revision = repository_dict[ 'latest_revision' ]
- if changeset_revision == latest_revision:
- repository_dicts.append( dict( repository_dict.items() + baseline_repository_dict.items() ) )
- else:
- repository_dicts.append( dict( repository_dict.items() + baseline_repository_dict.items() ) )
- if testing_single_repository_dict:
- tsr_name = testing_single_repository_dict[ 'name' ]
- tsr_owner = testing_single_repository_dict[ 'owner' ]
- tsr_changeset_revision = testing_single_repository_dict[ 'changeset_revision' ]
- log.debug( 'Testing single repository with name %s and owner %s.' % ( str( tsr_name ), str( tsr_owner ) ) )
- for repository_to_install in repository_dicts:
- rti_name = repository_to_install[ 'name' ]
- rti_owner = repository_to_install[ 'owner' ]
- rti_changeset_revision = repository_to_install[ 'changeset_revision' ]
- if rti_name == tsr_name and rti_owner == tsr_owner:
- if tsr_changeset_revision is None:
- return [ repository_to_install ], error_message
- else:
- if tsr_changeset_revision == rti_changeset_revision:
- return repository_dicts, error_message
- return repository_dicts, error_message
- # Get a list of repositories to test from the tool shed specified in the GALAXY_INSTALL_TEST_TOOL_SHED_URL
- # environment variable.
- log.debug( "The Tool Shed's API url...\n%s" % str( api_url ) )
- log.debug( "...retrieved %d repository revisions for testing." % len( repository_dicts ) )
- log.debug( "Repository revisions for testing:" )
- for repository_dict in repository_dicts:
- name = str( repository_dict.get( 'name', None ) )
- owner = str( repository_dict.get( 'owner', None ) )
- changeset_revision = str( repository_dict.get( 'changeset_revision', None ) )
- log.debug( "Revision %s of repository %s owned by %s" % ( changeset_revision, name, owner ) )
- return repository_dicts, error_message
-
-def get_static_settings():
- """
- Return a dictionary of the settings necessary for a Galaxy application to be wrapped in the static
- middleware. This mainly consists of the file system locations of url-mapped static resources.
- """
- cwd = os.getcwd()
- static_dir = os.path.join( cwd, 'static' )
- #TODO: these should be copied from universe_wsgi.ini
- #TODO: static_enabled needed here?
- return dict( static_enabled = True,
- static_cache_time = 360,
- static_dir = static_dir,
- static_images_dir = os.path.join( static_dir, 'images', '' ),
- static_favicon_dir = os.path.join( static_dir, 'favicon.ico' ),
- static_scripts_dir = os.path.join( static_dir, 'scripts', '' ),
- static_style_dir = os.path.join( static_dir, 'june_2007_style', 'blue' ),
- static_robots_txt = os.path.join( static_dir, 'robots.txt' ) )
-
def get_tool_info_from_test_id( test_id ):
"""
Test IDs come in the form test_tool_number
@@ -455,137 +131,17 @@
tool_id = parts[ -2 ]
return tool_id, tool_version
-def get_tool_test_results_dicts( tool_shed_url, encoded_repository_metadata_id ):
- """
- Return the list of dictionaries contained in the Tool Shed's repository_metadata.tool_test_results
- column via the Tool Shed API.
- """
+def install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file ):
+ results_dict = install_and_test_base_util.initialize_results_dict()
error_message = ''
- parts = [ 'api', 'repository_revisions', encoded_repository_metadata_id ]
- api_url = get_api_url( base=tool_shed_url, parts=parts )
- repository_metadata, error_message = json_from_url( api_url )
- if error_message:
- return None, error_message
- # The tool_test_results used to be stored as a single dictionary rather than a list, but we currently
- # return a list.
- tool_test_results = listify( repository_metadata.get( 'tool_test_results', [] ) )
- return tool_test_results, error_message
-
-def get_webapp_global_conf():
- """Return the global_conf dictionary sent as the first argument to app_factory."""
- global_conf = {}
- if STATIC_ENABLED:
- global_conf.update( get_static_settings() )
- return global_conf
-
-def handle_missing_dependencies( app, repository, missing_tool_dependencies, repository_dict,
- tool_test_results_dicts, tool_test_results_dict, results_dict ):
- """Handle missing repository or tool dependencies for an installed repository."""
- # If a tool dependency fails to install correctly, this should be considered an installation error,
- # and functional tests should be skipped, since the tool dependency needs to be correctly installed
- # for the test to be considered reliable.
- log.debug( 'The following dependencies of this repository are missing, so skipping functional tests.' )
- # In keeping with the standard display layout, add the error message to the dict for each tool individually.
- for dependency in repository.missing_tool_dependencies:
- name = str( dependency.name )
- type = str( dependency.type )
- version = str( dependency.version )
- error_message = unicodify( dependency.error_message )
- log.debug( 'Missing tool dependency %s of type %s version %s: %s' % ( name, type, version, error_message ) )
- test_result = dict( type=dependency.type,
- name=dependency.name,
- version=dependency.version,
- error_message=dependency.error_message )
- tool_test_results_dict[ 'installation_errors' ][ 'tool_dependencies' ].append( test_result )
- for dependency in repository.missing_repository_dependencies:
- name = str( dependency.name )
- owner = str( dependency.owner )
- changeset_revision = str( dependency.changeset_revision )
- error_message = unicodify( dependency.error_message )
- log.debug( 'Missing repository dependency %s changeset revision %s owned by %s: %s' % \
- ( name, changeset_revision, owner, error_message ) )
- test_result = dict( tool_shed=dependency.tool_shed,
- name=dependency.name,
- owner=dependency.owner,
- changeset_revision=dependency.changeset_revision,
- error_message=dependency.error_message )
- tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ].append( test_result )
- # Record the status of this repository in the tool shed.
- params = dict( tools_functionally_correct=False,
- do_not_test=False,
- test_install_error=True )
- # TODO: do something useful with response_dict
- response_dict = register_test_result( galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params )
- # Since this repository is missing components, we do not want to test it, so deactivate it or uninstall it.
- # The deactivate flag is set to True if the environment variable GALAXY_INSTALL_TEST_KEEP_TOOL_DEPENDENCIES
- # is set to 'true'.
- deactivate = asbool( os.environ.get( 'GALAXY_INSTALL_TEST_KEEP_TOOL_DEPENDENCIES', False ) )
- if deactivate:
- for missing_tool_dependency in missing_tool_dependencies:
- uninstall_tool_dependency( app, missing_tool_dependency )
- # We are deactivating this repository and all of its repository dependencies.
- deactivate_repository( app, repository_dict )
- else:
- # We are uninstalling this repository and all of its repository dependencies.
- uninstall_repository_and_repository_dependencies( app, repository_dict )
- results_dict[ 'repositories_failed_install' ].append( dict( name=str( repository.name ),
- owner=str( repository.owner ),
- changeset_revision=str( repository.changeset_revision ) ) )
- # Set the test_toolbox.toolbox module-level variable to the new app.toolbox.
- test_toolbox.toolbox = app.toolbox
- return results_dict
-
-def initialize_results_dict():
- # Initialize a dictionary for the summary that will be printed to stdout.
- results_dict = {}
- results_dict[ 'total_repositories_tested' ] = 0
- results_dict[ 'all_tests_passed' ] = []
- results_dict[ 'at_least_one_test_failed' ] = []
- results_dict[ 'repositories_failed_install' ] = []
- return results_dict
-
-def install_repository( app, repository_dict ):
- """Install a repository defined by the received repository_dict from the tool shed into Galaxy."""
- name = str( repository_dict[ 'name' ] )
- owner = str( repository_dict[ 'owner' ] )
- changeset_revision = str( repository_dict[ 'changeset_revision' ] )
- error_message = ''
- repository = None
- log.debug( "Installing revision %s of repository %s owned by %s." % ( changeset_revision, name, owner ) )
- # Explicitly clear tests from twill's test environment.
- remove_generated_tests( app )
- # Use the repository information dictionary to generate an install method that will install the repository into the
- # embedded Galaxy application, with tool dependencies and repository dependencies, if any.
- test_install_repositories.generate_install_method( repository_dict )
- # Configure nose to run the install method as a test.
- test_config = nose.config.Config( env=os.environ, plugins=nose.plugins.manager.DefaultPluginManager() )
- test_config.configure( sys.argv )
- # Run the configured install method as a test. This method uses the embedded Galaxy application's web interface to
- # install the specified repository with tool and repository dependencies also selected for installation.
- result, _ = run_tests( test_config )
- try:
- repository = test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
- except Exception, e:
- error_message = 'Error getting revision %s of repository %s owned by %s: %s' % ( changeset_revision, name, owner, str( e ) )
- log.exception( error_message )
- return repository, error_message
-
-def install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file ):
- results_dict = initialize_results_dict()
- error_message = ''
- # Allow the option to keep or delete tool dependencies when a repository has been tested.
- deactivate = asbool( os.environ.get( 'GALAXY_INSTALL_TEST_KEEP_TOOL_DEPENDENCIES', False ) )
# Initialize a dictionary for the summary that will be printed to stdout.
total_repositories_tested = results_dict[ 'total_repositories_tested' ]
- repositories_to_install, error_message = get_repositories_to_install( galaxy_tool_shed_url )
+ repositories_to_install, error_message = \
+ install_and_test_base_util.get_repositories_to_install( install_and_test_base_util.galaxy_tool_shed_url )
if error_message:
return None, error_message
# Handle repositories not to be tested.
- if os.path.exists( exclude_list_file ):
+ if os.path.exists( install_and_test_base_util.exclude_list_file ):
# Entries in the exclude_list look something like this.
# { 'reason': The default reason or the reason specified in this section,
# 'repositories':
@@ -597,8 +153,8 @@
# someone has previously installed. Deleted repositories have never been installed, so should not be tested. If they are
# undeleted, this script will then test them the next time it runs. We don't need to check if a repository has been deleted
# here because our call to the Tool Shed API filters by downloadable='true', in which case deleted will always be False.
- log.debug( 'Loading the list of repositories excluded from testing from the file %s...', exclude_list_file )
- exclude_list = parse_exclude_list( exclude_list_file )
+ log.debug( 'Loading the list of repositories excluded from testing from the file %s...', install_and_test_base_util.exclude_list_file )
+ exclude_list = install_and_test_base_util.parse_exclude_list( install_and_test_base_util.exclude_list_file )
else:
exclude_list = []
# Generate a test method that will use Twill to install each repository into the embedded Galaxy application that was
@@ -627,7 +183,7 @@
# "user_id": "529fd61ab1c6cc36" }
encoded_repository_metadata_id = repository_dict.get( 'id', None )
# Add the URL for the tool shed we're installing from, so the automated installation methods go to the right place.
- repository_dict[ 'tool_shed_url' ] = galaxy_tool_shed_url
+ repository_dict[ 'tool_shed_url' ] = install_and_test_base_util.galaxy_tool_shed_url
# Get the name and owner out of the repository info dict.
name = str( repository_dict[ 'name' ] )
owner = str( repository_dict[ 'owner' ] )
@@ -635,31 +191,13 @@
log.debug( "Processing revision %s of repository %s owned by %s..." % ( changeset_revision, name, owner ) )
repository_identifier_dict = dict( name=name, owner=owner, changeset_revision=changeset_revision )
# Retrieve the stored list of tool_test_results_dicts.
- tool_test_results_dicts, error_message = get_tool_test_results_dicts( galaxy_tool_shed_url, encoded_repository_metadata_id )
+ tool_test_results_dicts, error_message = \
+ install_and_test_base_util.get_tool_test_results_dicts( install_and_test_base_util.galaxy_tool_shed_url,
+ encoded_repository_metadata_id )
if error_message:
log.debug( error_message )
else:
- if tool_test_results_dicts:
- # Inspect the tool_test_results_dict for the last test run to make sure it contains only a test_environment
- # entry. If it contains more entries, then the script ~/tool_shed/api/check_repositories_for_functional_tests.py
- # was not executed in preparation for this script's execution, so we'll just create an empty dictionary.
- tool_test_results_dict = tool_test_results_dicts[ 0 ]
- if len( tool_test_results_dict ) <= 1:
- # We can re-use the mostly empty tool_test_results_dict for this run because it is either empty or it contains only
- # a test_environment entry. If we use it we need to temporarily eliminate it from the list of tool_test_results_dicts
- # since it will be re-inserted later.
- tool_test_results_dict = tool_test_results_dicts.pop( 0 )
- elif len( tool_test_results_dict ) == 2 and \
- 'test_environment' in tool_test_results_dict and 'missing_test_components' in tool_test_results_dict:
- # We can re-use tool_test_results_dict if its only entries are "test_environment" and "missing_test_components".
- # In this case, some tools are missing tests components while others are not.
- tool_test_results_dict = tool_test_results_dicts.pop( 0 )
- else:
- # The latest tool_test_results_dict has been populated with the results of a test run, so it cannot be used.
- tool_test_results_dict = {}
- else:
- # Create a new dictionary for this test test run,
- tool_test_results_dict = {}
+ tool_test_results_dict = install_and_test_base_util.get_tool_test_results_dict( tool_test_results_dicts )
# See if this repository should be skipped for any reason.
this_repository_is_in_the_exclude_list = False
skip_reason = None
@@ -675,37 +213,20 @@
params = dict( tools_functionally_correct=False,
do_not_test=False )
# TODO: do something useful with response_dict
- response_dict = register_test_result( galaxy_tool_shed_url, tool_test_results_dicts, tool_test_results_dict, repository_dict, params )
+ response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
log.debug( "Not testing revision %s of repository %s owned by %s because it is in the exclude list for this test run." % \
( changeset_revision, name, owner ) )
else:
- test_environment_dict = tool_test_results_dict.get( 'test_environment', {} )
- if len( test_environment_dict ) == 0:
- # Set information about the tool shed to nothing since we cannot currently determine it from here.
- # We could eventually add an API method...
- test_environment_dict = dict( tool_shed_database_version='',
- tool_shed_mercurial_version='',
- tool_shed_revision='' )
- # Add the current time as the approximate time that this test run occurs. A similar value will also be
- # set to the repository_metadata.time_last_tested column, but we also store it here because the Tool Shed
- # may be configured to store multiple test run results, so each must be associated with a time stamp.
- now = time.strftime( "%Y-%m-%d %H:%M:%S" )
- # Add information about the current platform.
- test_environment_dict[ 'time_tested' ] = now
- test_environment_dict[ 'python_version' ] = platform.python_version()
- test_environment_dict[ 'architecture' ] = platform.machine()
- operating_system, hostname, operating_system_version, uname, arch, processor = platform.uname()
- test_environment_dict[ 'system' ] = '%s %s' % ( operating_system, operating_system_version )
- # Add information about the current Galaxy environment.
- test_environment_dict[ 'galaxy_database_version' ] = get_database_version( app )
- test_environment_dict[ 'galaxy_revision' ] = get_repository_current_revision( os.getcwd() )
- # Initialize and populate the tool_test_results_dict.
- tool_test_results_dict[ 'test_environment' ] = test_environment_dict
- tool_test_results_dict[ 'passed_tests' ] = []
- tool_test_results_dict[ 'failed_tests' ] = []
- tool_test_results_dict[ 'installation_errors' ] = dict( current_repository=[], repository_dependencies=[], tool_dependencies=[] )
+ tool_test_results_dict = install_and_test_base_util.initialize_tool_tests_results_dict( app, tool_test_results_dict )
+ # Explicitly clear tests from twill's test environment.
+ remove_generated_tests( app )
# Proceed with installing repositories and testing contained tools.
- repository, error_message = install_repository( app, repository_dict )
+ repository, error_message = install_and_test_base_util.install_repository( app, repository_dict )
if error_message:
tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = error_message
# Even if the repository failed to install, execute the uninstall method, in case a dependency did succeed.
@@ -715,7 +236,7 @@
except Exception, e:
error_message = 'Unable to find installed repository %s owned by %s: %s.' % ( name, owner, str( e ) )
log.exception( error_message )
- test_result = dict( tool_shed=galaxy_tool_shed_url,
+ test_result = dict( tool_shed=install_and_test_base_util.galaxy_tool_shed_url,
name=name,
owner=owner,
changeset_revision=changeset_revision,
@@ -725,20 +246,20 @@
test_install_error=True,
do_not_test=False )
# TODO: do something useful with response_dict
- response_dict = register_test_result( galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params )
+ response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
try:
- if deactivate:
- # We are deactivating this repository and all of its repository dependencies.
- deactivate_repository( app, repository_dict )
- else:
- # We are uninstalling this repository and all of its repository dependencies.
- uninstall_repository_and_repository_dependencies( app, repository_dict )
- except:
- log.exception( 'Encountered error attempting to deactivate or uninstall %s.', str( repository_dict[ 'name' ] ) )
+ # We are uninstalling this repository and all of its repository dependencies.
+ install_and_test_base_util.uninstall_repository_and_repository_dependencies( app, repository_dict )
+ except Exception, e:
+ log.exception( 'Error attempting to uninstall revision %s of repository %s owned by %s: %s' % \
+ ( changeset_revision, name, owner, str( e ) ) )
+ # Clean out any generated tests. This is necessary for Twill.
+ remove_generated_tests( app )
results_dict[ 'repositories_failed_install' ].append( repository_identifier_dict )
log.debug( 'Repository %s failed to install correctly.' % str( name ) )
else:
@@ -757,15 +278,18 @@
# missing test components.
if 'missing_test_components' not in tool_test_results_dict:
tool_test_results_dict[ 'missing_test_components' ] = []
- missing_tool_dependencies = get_missing_tool_dependencies( repository )
+ missing_tool_dependencies = install_and_test_base_util.get_missing_tool_dependencies( repository )
if missing_tool_dependencies or repository.missing_repository_dependencies:
- results_dict = handle_missing_dependencies( app,
- repository,
- missing_tool_dependencies,
- repository_dict,
- tool_test_results_dicts,
- tool_test_results_dict,
- results_dict )
+ results_dict = install_and_test_base_util.handle_missing_dependencies( app,
+ repository,
+ missing_tool_dependencies,
+ repository_dict,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ results_dict,
+ can_update_tool_shed )
+ # Set the test_toolbox.toolbox module-level variable to the new app.toolbox.
+ test_toolbox.toolbox = app.toolbox
else:
# If the repository has a test-data directory we write the generated shed_tools_dict to a file, so the functional
# test framework can find it.
@@ -787,49 +311,35 @@
do_not_test=False,
test_install_error=False )
# TODO: do something useful with response_dict
- response_dict = register_test_result( galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params )
+ response_dict = \
+ install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
results_dict[ 'at_least_one_test_failed' ].append( repository_identifier_dict )
total_repositories_tested += 1
results_dict[ 'total_repositories_tested' ] = total_repositories_tested
return results_dict, error_message
-def is_latest_downloadable_revision( url, repository_dict ):
- latest_revision = get_latest_downloadable_changeset_revision( url, name=repository_dict[ 'name' ], owner=repository_dict[ 'owner' ] )
- return str( repository_dict[ 'changeset_revision' ] ) == str( latest_revision )
-
-def json_from_url( url ):
- error_message = ''
- url_handle = urllib.urlopen( url )
- url_contents = url_handle.read()
- try:
- parsed_json = from_json_string( url_contents )
- except Exception, e:
- error_message = str( url_contents )
- log.exception( 'Error parsing JSON data in json_from_url(): %s.' % str( e ) )
- return None, error_message
- return parsed_json, error_message
-
def main():
- if tool_shed_api_key is None:
+ if install_and_test_base_util.tool_shed_api_key is None:
# If the tool shed URL specified in any dict is not present in the tool_sheds_conf.xml, the installation will fail.
log.debug( 'Cannot proceed without a valid tool shed API key set in the enviroment variable GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY.' )
return 1
- if galaxy_tool_shed_url is None:
+ if install_and_test_base_util.galaxy_tool_shed_url is None:
log.debug( 'Cannot proceed without a valid Tool Shed base URL set in the environment variable GALAXY_INSTALL_TEST_TOOL_SHED_URL.' )
return 1
# ---- Configuration ------------------------------------------------------
- galaxy_test_host = os.environ.get( 'GALAXY_INSTALL_TEST_HOST', default_galaxy_test_host )
+ galaxy_test_host = os.environ.get( 'GALAXY_INSTALL_TEST_HOST', install_and_test_base_util.default_galaxy_test_host )
# Set the GALAXY_INSTALL_TEST_HOST variable so that Twill will have the Galaxy url to which to
# install repositories.
os.environ[ 'GALAXY_INSTALL_TEST_HOST' ] = galaxy_test_host
# Set the GALAXY_TEST_HOST environment variable so that the toolbox tests will have the Galaxy url
# on which to to run tool functional tests.
os.environ[ 'GALAXY_TEST_HOST' ] = galaxy_test_host
- galaxy_test_port = os.environ.get( 'GALAXY_INSTALL_TEST_PORT', str( default_galaxy_test_port_max ) )
+ galaxy_test_port = os.environ.get( 'GALAXY_INSTALL_TEST_PORT', str( install_and_test_base_util.default_galaxy_test_port_max ) )
os.environ[ 'GALAXY_TEST_PORT' ] = galaxy_test_port
tool_path = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_PATH', 'tools' )
if 'HTTP_ACCEPT_LANGUAGE' not in os.environ:
@@ -844,7 +354,7 @@
shed_tool_data_table_conf_file = os.environ.get( 'GALAXY_INSTALL_TEST_SHED_TOOL_DATA_TABLE_CONF',
os.path.join( galaxy_test_tmp_dir, 'test_shed_tool_data_table_conf.xml' ) )
galaxy_tool_data_table_conf_file = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_DATA_TABLE_CONF',
- tool_data_table_conf )
+ install_and_test_base_util.tool_data_table_conf )
galaxy_tool_conf_file = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_CONF',
os.path.join( galaxy_test_tmp_dir, 'test_tool_conf.xml' ) )
galaxy_job_conf_file = os.environ.get( 'GALAXY_INSTALL_TEST_JOB_CONF',
@@ -896,18 +406,18 @@
pass
print "Database connection: ", database_connection
# Generate the shed_tool_data_table_conf.xml file.
- file( shed_tool_data_table_conf_file, 'w' ).write( tool_data_table_conf_xml_template )
+ file( shed_tool_data_table_conf_file, 'w' ).write( install_and_test_base_util.tool_data_table_conf_xml_template )
os.environ[ 'GALAXY_INSTALL_TEST_SHED_TOOL_DATA_TABLE_CONF' ] = shed_tool_data_table_conf_file
# ---- Start up a Galaxy instance ------------------------------------------------------
# Generate the tool_conf.xml file.
- file( galaxy_tool_conf_file, 'w' ).write( tool_conf_xml )
+ file( galaxy_tool_conf_file, 'w' ).write( install_and_test_base_util.tool_conf_xml )
# Generate the job_conf.xml file.
- file( galaxy_job_conf_file, 'w' ).write( job_conf_xml )
+ file( galaxy_job_conf_file, 'w' ).write( install_and_test_base_util.job_conf_xml )
# Generate the tool_sheds_conf.xml file, but only if a the user has not specified an existing one in the environment.
if 'GALAXY_INSTALL_TEST_TOOL_SHEDS_CONF' not in os.environ:
- file( galaxy_tool_sheds_conf_file, 'w' ).write( tool_sheds_conf_xml )
+ file( galaxy_tool_sheds_conf_file, 'w' ).write( install_and_test_base_util.tool_sheds_conf_xml )
# Generate the shed_tool_conf.xml file.
- tool_conf_template_parser = string.Template( shed_tool_conf_xml_template )
+ tool_conf_template_parser = string.Template( install_and_test_base_util.shed_tool_conf_xml_template )
shed_tool_conf_xml = tool_conf_template_parser.safe_substitute( shed_tool_path=galaxy_shed_tool_path )
file( galaxy_shed_tool_conf_file, 'w' ).write( shed_tool_conf_xml )
os.environ[ 'GALAXY_INSTALL_TEST_SHED_TOOL_CONF' ] = galaxy_shed_tool_conf_file
@@ -917,14 +427,14 @@
# Write the embedded web application's specific configuration to a temporary file. This is necessary in order for
# the external metadata script to find the right datasets.
kwargs = dict( admin_users = 'test(a)bx.psu.edu',
- master_api_key = default_galaxy_master_api_key,
+ master_api_key = install_and_test_base_util.default_galaxy_master_api_key,
allow_user_creation = True,
allow_user_deletion = True,
allow_library_path_paste = True,
database_connection = database_connection,
datatype_converters_config_file = "datatype_converters_conf.xml.sample",
file_path = galaxy_file_path,
- id_secret = galaxy_encode_secret,
+ id_secret = install_and_test_base_util.galaxy_encode_secret,
job_config_file = galaxy_job_conf_file,
job_queue_workers = 5,
log_destination = "stdout",
@@ -955,7 +465,7 @@
generate_config_file( 'universe_wsgi.ini.sample', galaxy_config_file, config_items )
kwargs[ 'tool_config_file' ] = [ galaxy_tool_conf_file, galaxy_shed_tool_conf_file ]
# Set the global_conf[ '__file__' ] option to the location of the temporary .ini file, which gets passed to set_metadata.sh.
- kwargs[ 'global_conf' ] = get_webapp_global_conf()
+ kwargs[ 'global_conf' ] = install_and_test_base_util.get_webapp_global_conf()
kwargs[ 'global_conf' ][ '__file__' ] = galaxy_config_file
# ---- Build Galaxy Application --------------------------------------------------
if not database_connection.startswith( 'sqlite://' ):
@@ -972,11 +482,11 @@
log.debug( "Embedded Galaxy application started..." )
# ---- Run galaxy webserver ------------------------------------------------------
server = None
- global_conf = get_webapp_global_conf()
+ global_conf = install_and_test_base_util.get_webapp_global_conf()
global_conf[ 'database_file' ] = database_connection
webapp = buildapp.app_factory( global_conf,
use_translogger=False,
- static_enabled=STATIC_ENABLED,
+ static_enabled=install_and_test_base_util.STATIC_ENABLED,
app=app )
# Serve the app on a specified or random port.
if galaxy_test_port is not None:
@@ -985,7 +495,8 @@
random.seed()
for i in range( 0, 9 ):
try:
- galaxy_test_port = str( random.randint( default_galaxy_test_port_min, default_galaxy_test_port_max ) )
+ galaxy_test_port = str( random.randint( install_and_test_base_util.default_galaxy_test_port_min,
+ install_and_test_base_util.default_galaxy_test_port_max ) )
log.debug( "Attempting to serve app on randomly chosen port: %s", galaxy_test_port )
server = httpserver.serve( webapp, host=galaxy_test_host, port=galaxy_test_port, start_loop=False )
break
@@ -995,7 +506,7 @@
raise
else:
raise Exception( "Unable to open a port between %s and %s to start Galaxy server" % \
- ( default_galaxy_test_port_min, default_galaxy_test_port_max ) )
+ ( install_and_test_base_util.default_galaxy_test_port_min, install_and_test_base_util.default_galaxy_test_port_max ) )
os.environ[ 'GALAXY_INSTALL_TEST_PORT' ] = galaxy_test_port
# Start the server.
t = threading.Thread( target=server.serve_forever )
@@ -1012,12 +523,11 @@
raise Exception( "Test HTTP server did not return '200 OK' after 10 tries" )
log.debug( "Embedded galaxy web server started..." )
log.debug( "The embedded Galaxy application is running on %s:%s" % ( str( galaxy_test_host ), str( galaxy_test_port ) ) )
- log.debug( "Repositories will be installed from the tool shed at %s" % str( galaxy_tool_shed_url ) )
- success = False
+ log.debug( "Repositories will be installed from the tool shed at %s" % str( install_and_test_base_util.galaxy_tool_shed_url ) )
# If a tool_data_table_conf.test.xml file was found, add the entries from it into the app's tool data tables.
- if additional_tool_data_tables:
- app.tool_data_tables.add_new_entries_from_config_file( config_filename=additional_tool_data_tables,
- tool_data_path=additional_tool_data_path,
+ if install_and_test_base_util.additional_tool_data_tables:
+ app.tool_data_tables.add_new_entries_from_config_file( config_filename=install_and_test_base_util.additional_tool_data_tables,
+ tool_data_path=install_and_test_base_util.additional_tool_data_path,
shed_tool_data_table_config=None,
persist=False )
now = time.strftime( "%Y-%m-%d %H:%M:%S" )
@@ -1042,21 +552,15 @@
if all_tests_passed:
print '# ----------------------------------------------------------------------------------'
print "# %d repositories successfully passed all functional tests:" % len( all_tests_passed )
- show_summary_output( all_tests_passed )
+ install_and_test_base_util.show_summary_output( all_tests_passed )
if at_least_one_test_failed:
print '# ----------------------------------------------------------------------------------'
print "# %d repositories failed at least 1 functional test:" % len( at_least_one_test_failed )
- show_summary_output( at_least_one_test_failed )
+ install_and_test_base_util.show_summary_output( at_least_one_test_failed )
if repositories_failed_install:
- # Set success to False so that the return code will not be 0.
- success = False
print '# ----------------------------------------------------------------------------------'
print "# %d repositories have installation errors:" % len( repositories_failed_install )
- show_summary_output( repositories_failed_install )
- else:
- success = True
- else:
- success = True
+ install_and_test_base_util.show_summary_output( repositories_failed_install )
print "####################################################################################"
log.debug( "Shutting down..." )
# Gracefully shut down the embedded web server and UniverseApplication.
@@ -1081,78 +585,8 @@
pass
else:
log.debug( 'GALAXY_INSTALL_TEST_NO_CLEANUP set, not cleaning up.' )
- # Normally the value of 'success' would determine whether this test suite is marked as passed or failed
- # in the automated buildbot framework. However, due to the procedure used here we only want to report
- # failure if a repository fails to install correctly, so we have overriden the value of 'success' here
- # based on what actions the script has executed.
- if success:
- return 0
- else:
- return 1
-
-def parse_exclude_list( xml_filename ):
- """Return a list of repositories to exclude from testing."""
- # This method should return a list with the following structure:
- # [{ 'reason': The default reason or the reason specified in this section,
- # 'repositories': [( name, owner, changeset revision if changeset revision else None ),
- # ( name, owner, changeset revision if changeset revision else None )]}]
- exclude_list = []
- exclude_verbose = []
- xml_tree, error_message = parse_xml( xml_filename )
- if error_message:
- log.debug( 'The xml document %s defining the exclude list is invalid, so no repositories will be excluded from testing: %s' % \
- ( str( xml_filename ), str( error_message ) ) )
- return exclude_list
- tool_sheds = xml_tree.findall( 'repositories' )
- xml_element = []
- exclude_count = 0
- for tool_shed in tool_sheds:
- if galaxy_tool_shed_url != tool_shed.attrib[ 'tool_shed' ]:
- continue
- else:
- xml_element = tool_shed
- for reason_section in xml_element:
- reason_text = reason_section.find( 'text' ).text
- repositories = reason_section.findall( 'repository' )
- exclude_dict = dict( reason=reason_text, repositories=[] )
- for repository in repositories:
- repository_tuple = get_repository_tuple_from_elem( repository )
- if repository_tuple not in exclude_dict[ 'repositories' ]:
- exclude_verbose.append( repository_tuple )
- exclude_count += 1
- exclude_dict[ 'repositories' ].append( repository_tuple )
- exclude_list.append( exclude_dict )
- log.debug( 'The xml document %s containing the exclude list defines the following %s repositories to be excluded from testing...' % \
- ( str( xml_filename ), str( exclude_count ) ) )
- #if '-list_repositories' in sys.argv:
- for name, owner, changeset_revision in exclude_verbose:
- if changeset_revision:
- log.debug( 'Repository %s owned by %s, changeset revision %s.' % ( str( name ), str( owner ), str( changeset_revision ) ) )
- else:
- log.debug( 'Repository %s owned by %s, all revisions.' % ( str( name ), str( owner ) ) )
- return exclude_list
-
-def register_test_result( url, tool_test_results_dicts, tool_test_results_dict, repository_dict, params ):
- """
- Update the repository metadata tool_test_results and appropriate flags using the Tool SHed API. This method
- updates tool_test_results with the relevant data, sets the do_not_test and tools_functionally correct flags
- to the appropriate values and updates the time_last_tested field to the value of the received time_tested.
- """
- if can_update_tool_shed:
- metadata_revision_id = repository_dict.get( 'id', None )
- if metadata_revision_id is not None:
- tool_test_results_dicts.insert( 0, tool_test_results_dict )
- params[ 'tool_test_results' ] = tool_test_results_dicts
- # Set the time_last_tested entry so that the repository_metadata.time_last_tested will be set in the tool shed.
- params[ 'time_last_tested' ] = 'This entry will result in this value being set via the Tool Shed API.'
- url = '%s' % ( suc.url_join( galaxy_tool_shed_url,'api', 'repository_revisions', str( metadata_revision_id ) ) )
- try:
- return update( tool_shed_api_key, url, params, return_formatted=False )
- except Exception, e:
- log.exception( 'Error attempting to register test results: %s' % str( e ) )
- return {}
- else:
- return {}
+ # Return a "successful" response to buildbot.
+ return 0
def remove_generated_tests( app ):
"""
@@ -1195,42 +629,6 @@
# Now delete the tests found in the previous loop.
del test_install_repositories.__dict__[ key ]
-def run_tests( test_config ):
- loader = nose.loader.TestLoader( config=test_config )
- test_config.plugins.addPlugin( ReportResults() )
- plug_loader = test_config.plugins.prepareTestLoader( loader )
- if plug_loader is not None:
- loader = plug_loader
- tests = loader.loadTestsFromNames( test_config.testNames )
- test_runner = nose.core.TextTestRunner( stream=test_config.stream,
- verbosity=test_config.verbosity,
- config=test_config )
- plug_runner = test_config.plugins.prepareTestRunner( test_runner )
- if plug_runner is not None:
- test_runner = plug_runner
- result = test_runner.run( tests )
- return result, test_config.plugins._plugins
-
-def show_summary_output( repository_dicts ):
- # Group summary display by repository owner.
- repository_dicts_by_owner = {}
- for repository_dict in repository_dicts:
- name = str( repository_dict[ 'name' ] )
- owner = str( repository_dict[ 'owner' ] )
- changeset_revision = str( repository_dict[ 'changeset_revision' ] )
- if owner in repository_dicts_by_owner:
- repository_dicts_by_owner[ owner ].append( repository_dict )
- else:
- repository_dicts_by_owner[ owner ] = [ repository_dict ]
- # Display grouped summary.
- for owner, grouped_repository_dicts in repository_dicts_by_owner.items():
- print "# "
- for repository_dict in grouped_repository_dicts:
- name = str( repository_dict[ 'name' ] )
- owner = str( repository_dict[ 'owner' ] )
- changeset_revision = str( repository_dict[ 'changeset_revision' ] )
- print "# Revision %s of repository %s owned by %s" % ( changeset_revision, name, owner )
-
def test_repository_tools( app, repository, repository_dict, tool_test_results_dicts, tool_test_results_dict, results_dict ):
"""Test tools contained in the received repository."""
name = str( repository.name )
@@ -1241,12 +639,12 @@
test_toolbox.toolbox = app.toolbox
# Generate the test methods for this installed repository. We need to pass in True here, or it will look
# in $GALAXY_HOME/test-data for test data, which may result in missing or invalid test files.
- test_toolbox.build_tests( testing_shed_tools=True, master_api_key=default_galaxy_master_api_key )
+ test_toolbox.build_tests( testing_shed_tools=True, master_api_key=install_and_test_base_util.default_galaxy_master_api_key )
# Set up nose to run the generated functional tests.
test_config = nose.config.Config( env=os.environ, plugins=nose.plugins.manager.DefaultPluginManager() )
test_config.configure( sys.argv )
# Run the configured tests.
- result, test_plugins = run_tests( test_config )
+ result, test_plugins = install_and_test_base_util.run_tests( test_config )
if result.wasSuccessful():
# This repository's tools passed all functional tests. Use the ReportResults nose plugin to get a list
# of tests that passed.
@@ -1271,11 +669,12 @@
# controller with the status of the test. This also sets the do_not_test and tools_functionally
# correct flags and updates the time_last_tested field to today's date.
# TODO: do something useful with response_dict
- response_dict = register_test_result( galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params )
+ response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
log.debug( 'Revision %s of repository %s owned by %s installed and passed functional tests.' % \
( changeset_revision, name, owner ) )
else:
@@ -1284,127 +683,30 @@
tool_test_results_dict[ 'failed_tests' ] = failed_test_dicts
failed_repository_dict = repository_identifier_dict
results_dict[ 'at_least_one_test_failed' ].append( failed_repository_dict )
- set_do_not_test = not is_latest_downloadable_revision( galaxy_tool_shed_url, repository_dict )
+ set_do_not_test = not is_latest_downloadable_revision( install_and_test_base_util.galaxy_tool_shed_url, repository_dict )
params = dict( tools_functionally_correct=False,
test_install_error=False,
do_not_test=str( set_do_not_test ) )
# TODO: do something useful with response_dict
- response_dict = register_test_result( galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params )
+ response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
log.debug( 'Revision %s of repository %s owned by %s installed successfully but did not pass functional tests.' % \
( changeset_revision, name, owner ) )
# Run the uninstall method. This removes tool functional test methods from the test_toolbox module and uninstalls the
# repository using Twill.
- deactivate = asbool( os.environ.get( 'GALAXY_INSTALL_TEST_KEEP_TOOL_DEPENDENCIES', False ) )
- if deactivate:
- log.debug( 'Deactivating changeset revision %s of repository %s' % ( str( changeset_revision ), str( name ) ) )
- # We are deactivating this repository and all of its repository dependencies.
- deactivate_repository( app, repository_dict )
- else:
- log.debug( 'Uninstalling changeset revision %s of repository %s' % ( str( changeset_revision ), str( name ) ) )
- # We are uninstalling this repository and all of its repository dependencies.
- uninstall_repository_and_repository_dependencies( app, repository_dict )
+ log.debug( 'Uninstalling changeset revision %s of repository %s' % ( str( changeset_revision ), str( name ) ) )
+ # We are uninstalling this repository and all of its repository dependencies.
+ install_and_test_base_util.uninstall_repository_and_repository_dependencies( app, repository_dict )
+ # Clean out any generated tests. This is necessary for Twill.
+ remove_generated_tests( app )
# Set the test_toolbox.toolbox module-level variable to the new app.toolbox.
test_toolbox.toolbox = app.toolbox
return results_dict
-def uninstall_repository_and_repository_dependencies( app, repository_dict ):
- """Uninstall a repository and all of its repository dependencies."""
- # This method assumes that the repositor defined by the received repository_dict is not a repository
- # dependency of another repository.
- sa_session = app.install_model.context
- # Clean out any generated tests. This is necessary for Twill.
- remove_generated_tests( app )
- # The dict contains the only repository the app should have installed at this point.
- name = str( repository_dict[ 'name' ] )
- owner = str( repository_dict[ 'owner' ] )
- changeset_revision = str( repository_dict[ 'changeset_revision' ] )
- # Since this install and test framework uninstalls repositories immediately after installing and testing
- # them, the values of repository.installed_changeset_revision and repository.changeset_revision should be
- # the same.
- repository = test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
- if repository.can_uninstall( app ):
- # A repository can be uninstalled only if no dependent repositories are installed. So uninstallation order
- # id critical. A repository is always uninstalled first, and the each of its dependencies is checked to see
- # if it can be uninstalled.
- uninstall_repository_dict = dict( name=name,
- owner=owner,
- changeset_revision=changeset_revision )
- log.debug( 'Revision %s of repository %s owned by %s selected for uninstallation.' % ( changeset_revision, name, owner ) )
- try:
- test_install_repositories.generate_uninstall_method( uninstall_repository_dict )
- # Set up nose to run the generated uninstall method as a functional test.
- test_config = nose.config.Config( env=os.environ, plugins=nose.plugins.manager.DefaultPluginManager() )
- test_config.configure( sys.argv )
- # Run the uninstall method. This method uses the Galaxy web interface to uninstall the previously installed
- # repository and all of its repository dependencies, deleting each of them from disk.
- result, _ = run_tests( test_config )
- repository_uninstall_successful = result.wasSuccessful()
- except Exception, e:
- repository_uninstall_successful = False
- log.exception( 'Uninstallation of revision %s of repository %s owned by %s failed: %s.' % \
- ( rd_changeset_revision, rd_name, rd_owner, str( e ) ) )
- if repository_uninstall_successful:
- # Now that the repository is uninstalled we can attempt to uninstall each of its repository dependencies.
- # We have to do this through Twill in order to maintain app.toolbox and shed_tool_conf.xml in a state that
- # is valid for future tests. Since some of the repository's repository dependencies may require other of
- # the repository's repository dependencies, we'll keep track of the repositories we've been able to unistall.
- processed_repository_dependency_ids = []
- while len( processed_repository_dependency_ids ) < len( repository.repository_dependencies ):
- for repository_dependency in repository.repository_dependencies:
- if repository_dependency.id not in processed_repository_dependency_ids and repository_dependency.can_uninstall( app ):
- processed_repository_dependency_ids.append( repository_dependency.id )
- rd_name = str( repository_dependency.name )
- rd_owner = str( repository_dependency.owner )
- rd_changeset_revision = str( repository_dependency.changeset_revision )
- uninstall_repository_dict = dict( name=rd_name,
- owner=rd_owner,
- changeset_revision=rd_changeset_revision )
- log.debug( 'Revision %s of repository dependency %s owned by %s selected for uninstallation.' % \
- ( rd_changeset_revision, rd_name, rd_owner ) )
- # Generate a test method to uninstall the repository dependency through the embedded Galaxy application's
- # web interface.
- try:
- test_install_repositories.generate_uninstall_method( uninstall_repository_dict )
- # Set up nose to run the generated uninstall method as a functional test.
- test_config = nose.config.Config( env=os.environ, plugins=nose.plugins.manager.DefaultPluginManager() )
- test_config.configure( sys.argv )
- # Run the uninstall method.
- result, _ = run_tests( test_config )
- if not result.wasSuccessful():
- # We won't set ok here because we'll continue to uninstall whatever we can.
- log.debug( 'Uninstallation of revision %s of repository %s owned by %s failed.' % \
- ( rd_changeset_revision, rd_name, rd_owner ) )
- except Exception, e:
- log.exception( 'Uninstallation of revision %s of repository %s owned by %s failed: %s.' % \
- ( rd_changeset_revision, rd_name, rd_owner, str( e ) ) )
- else:
- log.debug( 'Uninstallation of revision %s of repository %s owned by %s failed.' % ( changeset_revision, name, owner ) )
- else:
- log_reason_repository_cannot_be_uninstalled( app, repository )
-
-def uninstall_tool_dependency( app, tool_dependency ):
- """Attempt to uninstall a tool dependency."""
- sa_session = app.install_model.context
- # Clean out any generated tests. This is necessary for Twill.
- tool_dependency_install_path = tool_dependency.installation_directory( app )
- uninstalled, error_message = tool_dependency_util.remove_tool_dependency( app, tool_dependency )
- if error_message:
- log.debug( 'There was an error attempting to remove directory: %s' % str( tool_dependency_install_path ) )
- log.debug( error_message )
- else:
- log.debug( 'Successfully removed tool dependency installation directory: %s' % str( tool_dependency_install_path ) )
- if not uninstalled or tool_dependency.status != app.model.ToolDependency.installation_status.UNINSTALLED:
- tool_dependency.status = app.model.ToolDependency.installation_status.UNINSTALLED
- sa_session.add( tool_dependency )
- sa_session.flush()
- if os.path.exists( tool_dependency_install_path ):
- log.debug( 'Uninstallation of tool dependency succeeded, but the installation path still exists on the filesystem. It is now being explicitly deleted.')
- suc.remove_dir( tool_dependency_install_path )
-
if __name__ == "__main__":
# The tool_test_results_dict should always have the following structure:
# {
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jmchilton: In upload tool sort FTP files by default.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/ce186eb5fefc/
Changeset: ce186eb5fefc
User: jmchilton
Date: 2013-12-18 15:19:58
Summary: In upload tool sort FTP files by default.
Not doing enough... but it is a step forward.
http://dev.list.galaxyproject.org/sort-list-of-ftp-uploaded-files-tp4662859…
Affected #: 1 file
diff -r c5c4deabb7316f2ef45ff8fb2718d167d91d09c0 -r ce186eb5fefcb7ff332c73bd0869b9e222293d0b lib/galaxy/web/form_builder.py
--- a/lib/galaxy/web/form_builder.py
+++ b/lib/galaxy/web/form_builder.py
@@ -3,6 +3,8 @@
"""
import logging, sys, os, time
+
+from operator import itemgetter
from cgi import escape
from galaxy.util import restore_text, relpath, nice_size, unicodify
from galaxy.web import url_for
@@ -212,6 +214,7 @@
ctime=time.strftime( "%m/%d/%Y %I:%M:%S %p", time.localtime( statinfo.st_ctime ) ) ) )
if not uploads:
rval += '<tr><td colspan="4"><em>Your FTP upload directory contains no files.</em></td></tr>'
+ uploads = sorted(uploads, key=itemgetter("path"))
for upload in uploads:
rval += FTPFileField.trow % ( prefix, self.name, upload['path'], upload['path'], upload['size'], upload['ctime'] )
rval += FTPFileField.tfoot
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jmchilton: Backout of 714f5b1 for now.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c5c4deabb731/
Changeset: c5c4deabb731
User: jmchilton
Date: 2013-12-18 08:23:27
Summary: Backout of 714f5b1 for now.
jgoecks_: jmchilto1: 714f5b1 seems to have broken HDA ordering in history panel
Affected #: 3 files
diff -r 41c6ee05c8acf2e98c69687bc894f1c596c930b0 -r c5c4deabb7316f2ef45ff8fb2718d167d91d09c0 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -19,7 +19,6 @@
import socket
import time
from string import Template
-from itertools import ifilter
import galaxy.datatypes
import galaxy.datatypes.registry
@@ -44,11 +43,6 @@
# 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):
@@ -899,32 +893,6 @@
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 41c6ee05c8acf2e98c69687bc894f1c596c930b0 -r c5c4deabb7316f2ef45ff8fb2718d167d91d09c0 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,28 +51,47 @@
else:
history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True )
- contents_kwds = {}
+ # if ids, return _FULL_ data (as show) for each id passed
if ids:
- 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'
+ 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
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 )
- 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 ) )
+ # 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 ) )
+
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 41c6ee05c8acf2e98c69687bc894f1c596c930b0 -r c5c4deabb7316f2ef45ff8fb2718d167d91d09c0 test/unit/test_galaxy_mapping.py
--- a/test/unit/test_galaxy_mapping.py
+++ b/test/unit/test_galaxy_mapping.py
@@ -180,39 +180,6 @@
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
@@ -224,16 +191,10 @@
return cls.model.session.query( type )
@classmethod
- def persist(cls, *args, **kwargs):
- session = cls.model.session
- flush = kwargs.get('flush', True)
+ def persist(cls, *args):
for arg in args:
- session.add( arg )
- if flush:
- session.flush()
- if kwargs.get('expunge', not flush):
- cls.expunge()
- return arg # Return last or only arg.
+ cls.model.session.add( arg )
+ cls.expunge()
@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: inithello: Fixes for test scripts 1010, 1080, 1400, and 1460.
by commits-noreply@bitbucket.org 17 Dec '13
by commits-noreply@bitbucket.org 17 Dec '13
17 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/41c6ee05c8ac/
Changeset: 41c6ee05c8ac
User: inithello
Date: 2013-12-17 21:54:55
Summary: Fixes for test scripts 1010, 1080, 1400, and 1460.
Affected #: 5 files
diff -r 43b23f5503997727debf8232239434974491e55e -r 41c6ee05c8acf2e98c69687bc894f1c596c930b0 test/tool_shed/base/twilltestcase.py
--- a/test/tool_shed/base/twilltestcase.py
+++ b/test/tool_shed/base/twilltestcase.py
@@ -1338,7 +1338,7 @@
def verify_installed_repository_data_table_entries( self, required_data_table_entries ):
# The value of the received required_data_table_entries will be something like: [ 'sam_fa_indexes' ]
- data_tables = xml_util.parse_xml( self.shed_tool_data_table_conf )
+ data_tables, error_message = xml_util.parse_xml( self.shed_tool_data_table_conf )
found = False
# With the tool shed, the "path" attribute that is hard-coded into the tool_data_tble_conf.xml
# file is ignored. This is because the tool shed requires the directory location to which this
diff -r 43b23f5503997727debf8232239434974491e55e -r 41c6ee05c8acf2e98c69687bc894f1c596c930b0 test/tool_shed/functional/test_1080_advanced_circular_dependency_installation.py
--- a/test/tool_shed/functional/test_1080_advanced_circular_dependency_installation.py
+++ b/test/tool_shed/functional/test_1080_advanced_circular_dependency_installation.py
@@ -285,6 +285,7 @@
'Uninstalled' ]
self.display_installed_repository_manage_page( installed_convert_repository,
strings_displayed=strings_displayed )
+ self.test_db_util.install_session.refresh( installed_column_repository )
self.check_galaxy_repository_tool_panel_section( installed_column_repository, 'column_maker' )
def test_0065_reinstall_column_repository( self ):
@@ -330,6 +331,7 @@
'Deactivated' ]
self.display_installed_repository_manage_page( installed_column_repository,
strings_displayed=strings_displayed )
+ self.test_db_util.install_session.refresh( installed_convert_repository )
self.check_galaxy_repository_tool_panel_section( installed_convert_repository, 'convert_chars' )
def test_0075_uninstall_column_repository( self ):
@@ -387,8 +389,8 @@
common.test_user_1_name )
self.uninstall_repository( installed_column_repository, remove_from_disk=False )
self.uninstall_repository( installed_convert_repository, remove_from_disk=False )
- self.test_db_util.ga_refresh( installed_column_repository )
- self.test_db_util.ga_refresh( installed_convert_repository )
+ self.test_db_util.install_session.refresh( installed_column_repository )
+ self.test_db_util.install_session.refresh( installed_convert_repository )
self.check_galaxy_repository_tool_panel_section( installed_column_repository, '' )
self.check_galaxy_repository_tool_panel_section( installed_convert_repository, 'convert_chars' )
diff -r 43b23f5503997727debf8232239434974491e55e -r 41c6ee05c8acf2e98c69687bc894f1c596c930b0 test/tool_shed/functional/test_1160_circular_prior_installation_required.py
--- a/test/tool_shed/functional/test_1160_circular_prior_installation_required.py
+++ b/test/tool_shed/functional/test_1160_circular_prior_installation_required.py
@@ -247,7 +247,7 @@
convert_repository = self.test_db_util.get_installed_repository_by_name_owner( convert_repository_name, common.test_user_1_name )
# Filtering was selected for reinstallation, so convert chars and column maker should have been installed first.
for repo in [ convert_repository, column_repository, filter_repository ]:
- self.test_db_util.ga_session.refresh( repo )
+ self.test_db_util.install_session.refresh( repo )
assert filter_repository.update_time > convert_repository.update_time, 'Prior installed convert_chars_0160 shows a later update time than filtering_0160'
assert filter_repository.update_time > column_repository.update_time, 'Prior installed column_maker_0160 shows a later update time than filtering_0160'
diff -r 43b23f5503997727debf8232239434974491e55e -r 41c6ee05c8acf2e98c69687bc894f1c596c930b0 test/tool_shed/functional/test_1400_review_migration_stages.py
--- a/test/tool_shed/functional/test_1400_review_migration_stages.py
+++ b/test/tool_shed/functional/test_1400_review_migration_stages.py
@@ -34,8 +34,8 @@
migrated_tool_dependencies = [ 'emboss', '5.0.0', 'freebayes', '0.9.4_9696d0ce8a962f7bb61c4791be5ce44312b81cf8',
'samtools', '0.1.18', 'blast+', '2.2.26+', 'bwa', '0.5.9', 'picard', '1.56.0',
'lastz', '1.02.00', 'bowtie', '0.12.7', 'FreeBayes requires g++', 'ncurses', 'zlib',
- 'zlib and libpthread', 'blast.ncbi.nlm.nih.gov', 'fastx_toolkit', '0.0.13',
- 'samtools', '0.1.16', 'cufflinks', '2.1.1', 'R', '2.11.0' ]
+ 'blast.ncbi.nlm.nih.gov', 'fastx_toolkit', '0.0.13', 'samtools', '0.1.16', 'cufflinks',
+ '2.1.1', 'R', '2.11.0' ]
migration_scripts = [ '0002_tools.sh', '0003_tools.sh', '0004_tools.sh', '0005_tools.sh', '0006_tools.sh',
'0007_tools.sh', '0008_tools.sh' ]
stages.extend( migration_scripts + migrated_tool_dependencies + migrated_repository_names )
diff -r 43b23f5503997727debf8232239434974491e55e -r 41c6ee05c8acf2e98c69687bc894f1c596c930b0 test/tool_shed/functional_tests.py
--- a/test/tool_shed/functional_tests.py
+++ b/test/tool_shed/functional_tests.py
@@ -391,7 +391,7 @@
use_heartbeat = False )
# ---- Build Galaxy Application --------------------------------------------------
- if not galaxy_database_connection.startswith( 'sqlite://' ):
+ if not galaxy_database_connection.startswith( 'sqlite://' ) and not install_galaxy_database_connection.startswith( 'sqlite://' ):
kwargs[ 'database_engine_option_pool_size' ] = '10'
kwargs[ 'database_engine_option_max_overflow' ] = '20'
galaxyapp = GalaxyUniverseApplication( **kwargs )
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