mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-25 00:12:08 +08:00
merge
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
If you want to disable registration for users that are using disposable email address
|
||||
rename this file to disposable_email_blacklist.conf and fill it with the disposable domains
|
||||
that you want to have blacklisted. Each on its own line without the '@' character as shown below.
|
||||
Users using emails from these domains will get an error during the registration.
|
||||
|
||||
mailinator.com
|
||||
sogetthis.com
|
||||
spamgourmet.com
|
||||
trashmail.net
|
||||
@@ -16,5 +16,5 @@
|
||||
<params>
|
||||
<param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
|
||||
</params>
|
||||
<template>graphview/templates/graphview.mako</template>
|
||||
<template>graphview.mako</template>
|
||||
</visualization>
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
<params>
|
||||
<param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
|
||||
</params>
|
||||
<template>scatterplot/templates/scatterplot.mako</template>
|
||||
<template>scatterplot.mako</template>
|
||||
</visualization>
|
||||
|
||||
@@ -245,10 +245,6 @@
|
||||
<datatype extension="snpmatrix" type="galaxy.datatypes.genetics:SNPMatrix" display_in_upload="true"/>
|
||||
<datatype extension="xls" type="galaxy.datatypes.tabular:Tabular"/>
|
||||
<!-- End RGenetics Datatypes -->
|
||||
<!-- graph datatypes -->
|
||||
<datatype extension="xgmml" type="galaxy.datatypes.graph:Xgmml" display_in_upload="true"/>
|
||||
<datatype extension="sif" type="galaxy.datatypes.graph:Sif" display_in_upload="true"/>
|
||||
<datatype extension="rdf" type="galaxy.datatypes.graph:Rdf" display_in_upload="true"/>
|
||||
</registration>
|
||||
<sniffers>
|
||||
<!--
|
||||
|
||||
+13
-47
@@ -1,88 +1,54 @@
|
||||
;
|
||||
; Config for building eggs for distribution (via a site such as
|
||||
; eggs.g2.bx.psu.edu). Probably only useful to Galaxy developers at
|
||||
; Penn State. This file is used by scripts/dist-scramble.py
|
||||
; eggs.galaxyproject.org). Probably only useful to members of the Galaxy Team
|
||||
; building eggs for distribution. This file is used by
|
||||
; scripts/dist-scramble.py
|
||||
;
|
||||
; More information: http://wiki.g2.bx.psu.edu/Admin/Config/Eggs
|
||||
; More information: http://wiki.galaxyproject.org/Admin/Config/Eggs
|
||||
;
|
||||
|
||||
[hosts]
|
||||
py2.5-linux-i686-ucs2 = stegmaier.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-i686-ucs2/bin/python2.5
|
||||
py2.5-linux-i686-ucs4 = stegmaier.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-i686-ucs4/bin/python2.5
|
||||
py2.6-linux-i686-ucs2 = stegmaier.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-i686-ucs2/bin/python2.6
|
||||
py2.6-linux-i686-ucs4 = stegmaier.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-i686-ucs4/bin/python2.6
|
||||
py2.7-linux-i686-ucs2 = stegmaier.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-i686-ucs2/bin/python2.7
|
||||
py2.7-linux-i686-ucs4 = stegmaier.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-i686-ucs4/bin/python2.7
|
||||
py2.5-linux-x86_64-ucs2 = straub.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-x86_64-ucs2/bin/python2.5
|
||||
py2.5-linux-x86_64-ucs4 = straub.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-x86_64-ucs4/bin/python2.5
|
||||
py2.6-linux-x86_64-ucs2 = straub.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-x86_64-ucs2/bin/python2.6
|
||||
py2.6-linux-x86_64-ucs4 = straub.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-x86_64-ucs4/bin/python2.6
|
||||
py2.7-linux-x86_64-ucs2 = straub.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-x86_64-ucs2/bin/python2.7
|
||||
py2.7-linux-x86_64-ucs4 = straub.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-x86_64-ucs4/bin/python2.7
|
||||
py2.5-macosx-10.3-fat-ucs2 = weyerbacher.bx.psu.edu /Library/Frameworks/Python.framework/Versions/2.5/bin/python2.5
|
||||
py2.6-macosx-10.3-fat-ucs2 = weyerbacher.bx.psu.edu /Library/Frameworks/Python.framework/Versions/2.6/bin/python2.6
|
||||
py2.7-macosx-10.3-fat-ucs2 = weyerbacher.bx.psu.edu /Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7
|
||||
py2.6-macosx-10.6-universal-ucs2 = lion.bx.psu.edu /usr/bin/python2.6
|
||||
py2.7-macosx-10.6-intel-ucs2 = lion.bx.psu.edu /usr/local/bin/python2.7
|
||||
py2.5-solaris-2.10-i86pc_32-ucs2 = thumper.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.10-i86pc_32-ucs2/bin/python2.5
|
||||
py2.6-solaris-2.10-i86pc_32-ucs2 = thumper.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.10-i86pc_32-ucs2/bin/python2.6
|
||||
py2.7-solaris-2.10-i86pc_32-ucs2 = thumper.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.10-i86pc_32-ucs2/bin/python2.7
|
||||
py2.5-solaris-2.10-i86pc_64-ucs2 = thumper.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.10-i86pc_64-ucs2/bin/python2.5
|
||||
py2.6-solaris-2.10-i86pc_64-ucs2 = thumper.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.10-i86pc_64-ucs2/bin/python2.6
|
||||
py2.7-solaris-2.10-i86pc_64-ucs2 = thumper.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.10-i86pc_64-ucs2/bin/python2.7
|
||||
py2.5-solaris-2.10-sun4u_32-ucs2 = early.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.8-sun4u_32-ucs2/bin/python2.5
|
||||
py2.6-solaris-2.10-sun4u_32-ucs2 = early.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.8-sun4u_32-ucs2/bin/python2.6
|
||||
py2.7-solaris-2.10-sun4u_32-ucs2 = early.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.8-sun4u_32-ucs2/bin/python2.7
|
||||
py2.5-solaris-2.10-sun4u_64-ucs2 = early.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.10-sun4u_64-ucs2/bin/python2.5
|
||||
py2.6-solaris-2.10-sun4u_64-ucs2 = early.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.10-sun4u_64-ucs2/bin/python2.6
|
||||
py2.7-solaris-2.10-sun4u_64-ucs2 = early.bx.psu.edu /afs/bx.psu.edu/project/pythons/solaris-2.10-sun4u_64-ucs2/bin/python2.7
|
||||
|
||||
;
|
||||
; these hosts are used to build eggs with no C extensions
|
||||
py2.5 = straub.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-x86_64-ucs4/bin/python2.5
|
||||
py2.6 = straub.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-x86_64-ucs4/bin/python2.6
|
||||
py2.7 = straub.bx.psu.edu /afs/bx.psu.edu/project/pythons/linux-x86_64-ucs4/bin/python2.7
|
||||
|
||||
[groups]
|
||||
py2.5-linux-i686 = py2.5-linux-i686-ucs2 py2.5-linux-i686-ucs4
|
||||
py2.5-linux-x86_64 = py2.5-linux-x86_64-ucs2 py2.5-linux-x86_64-ucs4
|
||||
py2.6-linux-i686 = py2.6-linux-i686-ucs2 py2.6-linux-i686-ucs4
|
||||
py2.6-linux-x86_64 = py2.6-linux-x86_64-ucs2 py2.6-linux-x86_64-ucs4
|
||||
py2.7-linux-i686 = py2.7-linux-i686-ucs2 py2.7-linux-i686-ucs4
|
||||
py2.7-linux-x86_64 = py2.7-linux-x86_64-ucs2 py2.7-linux-x86_64-ucs4
|
||||
py2.5-linux = py2.5-linux-i686 py2.5-linux-x86_64
|
||||
py2.6-linux = py2.6-linux-i686 py2.6-linux-x86_64
|
||||
py2.7-linux = py2.7-linux-i686 py2.7-linux-x86_64
|
||||
linux-i686 = py2.5-linux-i686 py2.6-linux-i686 py2.7-linux-i686
|
||||
linux-x86_64 = py2.5-linux-x86_64 py2.6-linux-x86_64 py2.7-linux-x86_64
|
||||
linux-i686 = py2.6-linux-i686 py2.7-linux-i686
|
||||
linux-x86_64 = py2.6-linux-x86_64 py2.7-linux-x86_64
|
||||
linux = linux-i686 linux-x86_64
|
||||
py2.5-macosx = py2.5-macosx-10.3-fat-ucs2
|
||||
py2.6-macosx = py2.6-macosx-10.3-fat-ucs2 py2.6-macosx-10.6-universal-ucs2
|
||||
py2.7-macosx = py2.7-macosx-10.3-fat-ucs2 py2.7-macosx-10.6-intel-ucs2
|
||||
macosx = py2.5-macosx py2.6-macosx py2.7-macosx
|
||||
py2.5-solaris-i86pc = py2.5-solaris-2.10-i86pc_32-ucs2 py2.5-solaris-2.10-i86pc_64-ucs2
|
||||
py2.6-solaris-i86pc = py2.6-solaris-2.10-i86pc_32-ucs2 py2.6-solaris-2.10-i86pc_64-ucs2
|
||||
py2.7-solaris-i86pc = py2.7-solaris-2.10-i86pc_32-ucs2 py2.7-solaris-2.10-i86pc_64-ucs2
|
||||
py2.5-solaris-sun4u = py2.5-solaris-2.10-sun4u_32-ucs2 py2.5-solaris-2.10-sun4u_64-ucs2
|
||||
py2.6-solaris-sun4u = py2.6-solaris-2.10-sun4u_32-ucs2 py2.6-solaris-2.10-sun4u_64-ucs2
|
||||
py2.7-solaris-sun4u = py2.7-solaris-2.10-sun4u_32-ucs2 py2.7-solaris-2.10-sun4u_64-ucs2
|
||||
py2.5-solaris = py2.5-solaris-i86pc py2.5-solaris-sun4u
|
||||
py2.6-solaris = py2.6-solaris-i86pc py2.6-solaris-sun4u
|
||||
py2.7-solaris = py2.7-solaris-i86pc py2.7-solaris-sun4u
|
||||
solaris-i86pc = py2.5-solaris-i86pc py2.6-solaris-i86pc py2.7-solaris-i86pc
|
||||
solaris-sun4u = py2.5-solaris-sun4u py2.6-solaris-sun4u py2.7-solaris-sun4u
|
||||
solaris = solaris-i86pc solaris-sun4u
|
||||
py2.5-all = py2.5-linux py2.5-macosx py2.5-solaris
|
||||
py2.6-all = py2.6-linux py2.6-macosx py2.6-solaris
|
||||
py2.7-all = py2.7-linux py2.7-macosx py2.7-solaris
|
||||
macosx = py2.6-macosx py2.7-macosx
|
||||
py2.6-all = py2.6-linux py2.6-macosx
|
||||
py2.7-all = py2.7-linux py2.7-macosx
|
||||
|
||||
; the 'all' key is used internally by the build system to specify which hosts
|
||||
; to build on when no hosts are specified on the dist-eggs.py command line.
|
||||
all = linux macosx solaris
|
||||
all = linux macosx
|
||||
|
||||
; the 'noplatform' key, likewise, is for which build hosts should be used when
|
||||
; building pure python (noplatform) eggs.
|
||||
noplatform = py2.5 py2.6 py2.7
|
||||
noplatform = py2.6 py2.7
|
||||
|
||||
; don't build these eggs on these platforms:
|
||||
[ignore]
|
||||
ctypes = py2.5-linux-i686-ucs2 py2.5-linux-i686-ucs4 py2.6-linux-i686-ucs2 py2.6-linux-i686-ucs4 py2.7-linux-i686-ucs2 py2.7-linux-i686-ucs4 py2.5-linux-x86_64-ucs2 py2.5-linux-x86_64-ucs4 py2.6-linux-x86_64-ucs2 py2.6-linux-x86_64-ucs4 py2.7-linux-x86_64-ucs2 py2.7-linux-x86_64-ucs4 py2.5-macosx-10.3-fat-ucs2 py2.6-macosx-10.3-fat-ucs2 py2.6-macosx-10.6-universal-ucs2 py2.7-macosx-10.3-fat-ucs2 py2.5-solaris-2.10-i86pc_32-ucs2 py2.6-solaris-2.10-i86pc_32-ucs2 py2.7-solaris-2.10-i86pc_32-ucs2 py2.5-solaris-2.10-i86pc_64-ucs2 py2.6-solaris-2.10-i86pc_64-ucs2 py2.7-solaris-2.10-i86pc_64-ucs2 py2.5-solaris-2.10-sun4u_32-ucs2 py2.6-solaris-2.10-sun4u_32-ucs2 py2.7-solaris-2.10-sun4u_32-ucs2 py2.5-solaris-2.10-sun4u_64-ucs2 py2.6-solaris-2.10-sun4u_64-ucs2 py2.7-solaris-2.10-sun4u_64-ucs2
|
||||
;ctypes =
|
||||
|
||||
@@ -21,7 +21,7 @@ MySQL_python = 1.2.3c1
|
||||
PyRods = 3.2.4
|
||||
numpy = 1.6.0
|
||||
pbs_python = 4.3.5
|
||||
psycopg2 = 2.0.13
|
||||
psycopg2 = 2.5.1
|
||||
pycrypto = 2.5
|
||||
pysam = 0.4.2
|
||||
pysqlite = 2.5.6
|
||||
@@ -38,15 +38,16 @@ Beaker = 1.4
|
||||
boto = 2.5.2
|
||||
decorator = 3.1.2
|
||||
docutils = 0.7
|
||||
drmaa = 0.4b3
|
||||
drmaa = 0.6
|
||||
elementtree = 1.2.6_20050316
|
||||
Fabric = 1.4.2
|
||||
Fabric = 1.7.0
|
||||
GeneTrack = 2.0.0_beta_1
|
||||
lrucache = 0.2
|
||||
Mako = 0.4.1
|
||||
nose = 0.11.1
|
||||
NoseHTML = 0.4.1
|
||||
NoseTestDiff = 0.1
|
||||
paramiko = 1.11.1
|
||||
Parsley = 1.1
|
||||
Paste = 1.7.5.1
|
||||
PasteDeploy = 1.5.0
|
||||
@@ -71,7 +72,7 @@ raven = 3.1.8
|
||||
|
||||
; extra version information
|
||||
[tags]
|
||||
psycopg2 = _8.4.2_static
|
||||
psycopg2 = _9.2.4_static
|
||||
pysqlite = _3.6.17_static
|
||||
MySQL_python = _5.1.41_static
|
||||
bx_python = _7b95ff194725
|
||||
@@ -82,7 +83,7 @@ pysam = _kanwei_b10f6e722e9a
|
||||
; the wiki page above
|
||||
[source]
|
||||
MySQL_python = mysql-5.1.41
|
||||
psycopg2 = postgresql-8.4.2
|
||||
psycopg2 = postgresql-9.2.4
|
||||
pysqlite = sqlite-amalgamation-3_6_17
|
||||
|
||||
[dependencies]
|
||||
|
||||
+8
-2
@@ -22,6 +22,8 @@ from galaxy.sample_tracking import external_service_types
|
||||
from galaxy.openid.providers import OpenIDProviders
|
||||
from galaxy.tools.data_manager.manager import DataManagers
|
||||
|
||||
from galaxy.web.base import pluginframework
|
||||
|
||||
import logging
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
@@ -123,8 +125,11 @@ class UniverseApplication( object ):
|
||||
# Load genome indexer tool.
|
||||
load_genome_index_tools( self.toolbox )
|
||||
# visualizations registry: associates resources with visualizations, controls how to render
|
||||
self.visualizations_registry = VisualizationsRegistry.from_config(
|
||||
self.config.visualizations_plugins_directory, self.config )
|
||||
self.visualizations_registry = None
|
||||
if self.config.visualizations_plugins_directory:
|
||||
self.visualizations_registry = VisualizationsRegistry( self,
|
||||
directories_setting=self.config.visualizations_plugins_directory,
|
||||
template_cache_dir=self.config.template_cache )
|
||||
# Load security policy.
|
||||
self.security_agent = self.model.security_agent
|
||||
self.host_security_agent = galaxy.security.HostAgent( model=self.security_agent.model, permitted_actions=self.security_agent.permitted_actions )
|
||||
@@ -167,6 +172,7 @@ class UniverseApplication( object ):
|
||||
self.job_stop_queue = self.job_manager.job_stop_queue
|
||||
# Initialize the external service types
|
||||
self.external_service_types = external_service_types.ExternalServiceTypesCollection( self.config.external_service_type_config_file, self.config.external_service_type_path, self )
|
||||
self.model.engine.dispose()
|
||||
|
||||
def shutdown( self ):
|
||||
self.job_manager.shutdown()
|
||||
|
||||
+24
-5
@@ -63,7 +63,7 @@ class Configuration( object ):
|
||||
elif 'tool_config_files' in kwargs:
|
||||
tcf = kwargs[ 'tool_config_files' ]
|
||||
else:
|
||||
tcf = 'tool_conf.xml'
|
||||
tcf = 'tool_conf.xml,shed_tool_conf.xml'
|
||||
self.tool_filters = listify( kwargs.get( "tool_filters", [] ) )
|
||||
self.tool_label_filters = listify( kwargs.get( "tool_label_filters", [] ) )
|
||||
self.tool_section_filters = listify( kwargs.get( "tool_section_filters", [] ) )
|
||||
@@ -132,6 +132,23 @@ class Configuration( object ):
|
||||
self.admin_users = kwargs.get( "admin_users", "" )
|
||||
self.mailing_join_addr = kwargs.get('mailing_join_addr',"galaxy-announce-join@bx.psu.edu")
|
||||
self.error_email_to = kwargs.get( 'error_email_to', None )
|
||||
self.activation_email = kwargs.get( 'activation_email', None )
|
||||
self.user_activation_on = string_as_bool( kwargs.get( 'user_activation_on', False ) )
|
||||
self.activation_grace_period = kwargs.get( 'activation_grace_period', None )
|
||||
self.inactivity_box_content = kwargs.get( 'inactivity_box_content', None )
|
||||
self.terms_url = kwargs.get( 'terms_url', None )
|
||||
self.instance_resource_url = kwargs.get( 'instance_resource_url', None )
|
||||
self.registration_warning_message = kwargs.get( 'registration_warning_message', None )
|
||||
# Get the disposable email domains blacklist file and its contents
|
||||
self.blacklist_location = kwargs.get( 'blacklist_file', None )
|
||||
self.blacklist_content = None
|
||||
if self.blacklist_location is not None:
|
||||
self.blacklist_file = resolve_path( kwargs.get( 'blacklist_file', None ), self.root )
|
||||
try:
|
||||
with open( self.blacklist_file ) as blacklist:
|
||||
self.blacklist_content = [ line.rstrip() for line in blacklist.readlines() ]
|
||||
except IOError:
|
||||
print ( "CONFIGURATION ERROR: Can't open supplied blacklist file from path: " + str( self.blacklist_file ) )
|
||||
self.smtp_server = kwargs.get( 'smtp_server', None )
|
||||
self.smtp_username = kwargs.get( 'smtp_username', None )
|
||||
self.smtp_password = kwargs.get( 'smtp_password', None )
|
||||
@@ -175,7 +192,7 @@ class Configuration( object ):
|
||||
self.message_box_content = kwargs.get( 'message_box_content', None )
|
||||
self.message_box_class = kwargs.get( 'message_box_class', 'info' )
|
||||
self.support_url = kwargs.get( 'support_url', 'http://wiki.g2.bx.psu.edu/Support' )
|
||||
self.wiki_url = kwargs.get( 'wiki_url', 'http://g2.trac.bx.psu.edu/' )
|
||||
self.wiki_url = kwargs.get( 'wiki_url', 'http://wiki.galaxyproject.org/' )
|
||||
self.blog_url = kwargs.get( 'blog_url', None )
|
||||
self.screencasts_url = kwargs.get( 'screencasts_url', None )
|
||||
self.library_import_dir = kwargs.get( 'library_import_dir', None )
|
||||
@@ -209,6 +226,7 @@ class Configuration( object ):
|
||||
if self.nginx_upload_store:
|
||||
self.nginx_upload_store = os.path.abspath( self.nginx_upload_store )
|
||||
self.object_store = kwargs.get( 'object_store', 'disk' )
|
||||
self.object_store_check_old_style = string_as_bool( kwargs.get( 'object_store_check_old_style', False ) )
|
||||
self.object_store_cache_path = resolve_path( kwargs.get( "object_store_cache_path", "database/object_store_cache" ), self.root )
|
||||
# Handle AWS-specific config options for backward compatibility
|
||||
if kwargs.get( 'aws_access_key', None) is not None:
|
||||
@@ -226,6 +244,9 @@ class Configuration( object ):
|
||||
self.os_is_secure = string_as_bool( kwargs.get( 'os_is_secure', True ) )
|
||||
self.os_conn_path = kwargs.get( 'os_conn_path', '/' )
|
||||
self.object_store_cache_size = float(kwargs.get( 'object_store_cache_size', -1 ))
|
||||
self.object_store_config_file = kwargs.get( 'object_store_config_file', None )
|
||||
if self.object_store_config_file is not None:
|
||||
self.object_store_config_file = resolve_path( self.object_store_config_file, self.root )
|
||||
self.distributed_object_store_config_file = kwargs.get( 'distributed_object_store_config_file', None )
|
||||
if self.distributed_object_store_config_file is not None:
|
||||
self.distributed_object_store_config_file = resolve_path( self.distributed_object_store_config_file, self.root )
|
||||
@@ -294,9 +315,7 @@ class Configuration( object ):
|
||||
self.fluent_log = string_as_bool( kwargs.get( 'fluent_log', False ) )
|
||||
self.fluent_host = kwargs.get( 'fluent_host', 'localhost' )
|
||||
self.fluent_port = int( kwargs.get( 'fluent_port', 24224 ) )
|
||||
# PLUGINS:
|
||||
self.plugin_frameworks = []
|
||||
# visualization framework
|
||||
# visualization plugin framework
|
||||
self.visualizations_plugins_directory = kwargs.get( 'visualizations_plugins_directory', None )
|
||||
|
||||
@property
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
grep -v '^#' $input | sort -k1,1 |
|
||||
|
||||
## Generate coverage bedgraph.
|
||||
bedtools genomecov -bg -split -i stdin -g $chromInfo
|
||||
bedtools genomecov -bg -i stdin -g $chromInfo
|
||||
|
||||
## Only use split option for bed and gff/gff3/gtf.
|
||||
#if $input.ext in [ 'bed', 'gff', 'gff3', 'gtf' ]:
|
||||
-split
|
||||
#end if
|
||||
|
||||
## Streaming the bedgraph file to wigToBigWig is fast but very memory intensive; hence, this
|
||||
## should only be used on systems with large RAM.
|
||||
|
||||
@@ -28,7 +28,7 @@ def main():
|
||||
lines_per_chunk = 4*sequences
|
||||
chunk_begin = 0
|
||||
|
||||
in_file = open(input_name)
|
||||
in_file = open(input_fname)
|
||||
|
||||
out_file.write('{"sections" : [');
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ galhtmlprefix = """<?xml version="1.0" encoding="utf-8" ?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Galaxy %s tool output - see http://g2.trac.bx.psu.edu/" />
|
||||
<meta name="generator" content="Galaxy %s tool output - see http://getgalaxy.org" />
|
||||
<title></title>
|
||||
<link rel="stylesheet" href="/static/style/base.css" type="text/css" />
|
||||
</head>
|
||||
|
||||
@@ -15,7 +15,7 @@ galhtmlprefix = """<?xml version="1.0" encoding="utf-8" ?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Galaxy %s tool output - see http://g2.trac.bx.psu.edu/" />
|
||||
<meta name="generator" content="Galaxy %s tool output - see http://getgalaxy.org" />
|
||||
<title></title>
|
||||
<link rel="stylesheet" href="/static/style/base.css" type="text/css" />
|
||||
</head>
|
||||
|
||||
@@ -13,7 +13,7 @@ galhtmlprefix = """<?xml version="1.0" encoding="utf-8" ?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Galaxy %s tool output - see http://g2.trac.bx.psu.edu/" />
|
||||
<meta name="generator" content="Galaxy %s tool output - see http://getgalaxy.org" />
|
||||
<title></title>
|
||||
<link rel="stylesheet" href="/static/style/base.css" type="text/css" />
|
||||
</head>
|
||||
|
||||
@@ -15,7 +15,7 @@ galhtmlprefix = """<?xml version="1.0" encoding="utf-8" ?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Galaxy %s tool output - see http://g2.trac.bx.psu.edu/" />
|
||||
<meta name="generator" content="Galaxy %s tool output - see http://getgalaxy.org" />
|
||||
<title></title>
|
||||
<link rel="stylesheet" href="/static/style/base.css" type="text/css" />
|
||||
</head>
|
||||
@@ -66,7 +66,7 @@ def main():
|
||||
f = file(outhtmlname,'w')
|
||||
f.write(galhtmlprefix % prog)
|
||||
flist = os.listdir(outfilepath)
|
||||
s = '## Rgenetics: http://rgenetics.org Galaxy Tools %s %s' % (prog,timenow()) # becomes info
|
||||
s = '## Rgenetics: http://bitbucket.org/rgalaxy Galaxy Tools %s %s' % (prog,timenow()) # becomes info
|
||||
print s
|
||||
f.write('<div>%s\n<ol>' % (s))
|
||||
for i, data in enumerate( flist ):
|
||||
|
||||
@@ -33,8 +33,6 @@ class Tabular( data.Text ):
|
||||
MetadataElement( name="column_types", default=[], desc="Column types", param=metadata.ColumnTypesParameter, readonly=True, visible=False, no_value=[] )
|
||||
MetadataElement( name="column_names", default=[], desc="Column names", readonly=True, visible=False, optional=True, no_value=[] )
|
||||
|
||||
def init_meta( self, dataset, copy_from=None ):
|
||||
data.Text.init_meta( self, dataset, copy_from=copy_from )
|
||||
def set_meta( self, dataset, overwrite = True, skip = None, max_data_lines = 100000, max_guess_type_data_lines = None, **kwd ):
|
||||
"""
|
||||
Tries to determine the number of columns as well as those columns that
|
||||
@@ -199,10 +197,13 @@ class Tabular( data.Text ):
|
||||
if not column_names and dataset.metadata.column_names:
|
||||
column_names = dataset.metadata.column_names
|
||||
|
||||
column_headers = [None] * dataset.metadata.columns
|
||||
columns = dataset.metadata.columns
|
||||
if columns is None:
|
||||
columns = dataset.metadata.spec.columns.no_value
|
||||
column_headers = [None] * columns
|
||||
|
||||
# fill in empty headers with data from column_names
|
||||
for i in range( min( dataset.metadata.columns, len( column_names ) ) ):
|
||||
for i in range( min( columns, len( column_names ) ) ):
|
||||
if column_headers[i] is None and column_names[i] is not None:
|
||||
column_headers[i] = column_names[i]
|
||||
|
||||
@@ -213,7 +214,7 @@ class Tabular( data.Text ):
|
||||
i = int( getattr( dataset.metadata, name ) ) - 1
|
||||
except:
|
||||
i = -1
|
||||
if 0 <= i < dataset.metadata.columns and column_headers[i] is None:
|
||||
if 0 <= i < columns and column_headers[i] is None:
|
||||
column_headers[i] = column_parameter_alias.get(name, name)
|
||||
|
||||
out.append( '<tr>' )
|
||||
@@ -236,13 +237,16 @@ class Tabular( data.Text ):
|
||||
try:
|
||||
if not dataset.peek:
|
||||
dataset.set_peek()
|
||||
columns = dataset.metadata.columns
|
||||
if columns is None:
|
||||
columns = dataset.metadata.spec.columns.no_value
|
||||
for line in dataset.peek.splitlines():
|
||||
if line.startswith( tuple( skipchars ) ):
|
||||
out.append( '<tr><td colspan="100%%">%s</td></tr>' % escape( line ) )
|
||||
elif line:
|
||||
elems = line.split( '\t' )
|
||||
# we may have an invalid comment line or invalid data
|
||||
if len( elems ) != dataset.metadata.columns:
|
||||
if len( elems ) != columns:
|
||||
out.append( '<tr><td colspan="100%%">%s</td></tr>' % escape( line ) )
|
||||
else:
|
||||
out.append( '<tr>' )
|
||||
|
||||
@@ -378,7 +378,7 @@ class GalaxyConfig( object ):
|
||||
return True
|
||||
else:
|
||||
try:
|
||||
return { "psycopg2": lambda: self.config.get( "app:main", "database_connection" ).startswith( "postgres://" ),
|
||||
return { "psycopg2": lambda: self.config.get( "app:main", "database_connection" ).startswith( "postgres" ),
|
||||
"MySQL_python": lambda: self.config.get( "app:main", "database_connection" ).startswith( "mysql://" ),
|
||||
"DRMAA_python": lambda: "sge" in self.config.get( "app:main", "start_job_runners" ).split(","),
|
||||
"drmaa": lambda: "drmaa" in self.config.get( "app:main", "start_job_runners" ).split(","),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Support for running a tool in Galaxy via an internal job management system
|
||||
"""
|
||||
|
||||
import time
|
||||
import copy
|
||||
import datetime
|
||||
import galaxy
|
||||
@@ -17,7 +18,7 @@ import threading
|
||||
import traceback
|
||||
from galaxy import model, util
|
||||
from galaxy.datatypes import metadata
|
||||
from galaxy.exceptions import ObjectInvalid
|
||||
from galaxy.exceptions import ObjectInvalid, ObjectNotFound
|
||||
from galaxy.jobs.actions.post import ActionBox
|
||||
from galaxy.jobs.mapper import JobRunnerMapper
|
||||
from galaxy.jobs.runners import BaseJobRunner
|
||||
@@ -179,7 +180,7 @@ class JobConfiguration( object ):
|
||||
if tools is not None:
|
||||
for tool in self.__findall_with_required(tools, 'tool'):
|
||||
# There can be multiple definitions with identical ids, but different params
|
||||
id = tool.get('id')
|
||||
id = tool.get('id').lower()
|
||||
if id not in self.tools:
|
||||
self.tools[id] = list()
|
||||
self.tools[id].append(JobToolConfiguration(**dict(tool.items())))
|
||||
@@ -926,6 +927,17 @@ class JobWrapper( object ):
|
||||
context = self.get_dataset_finish_context( job_context, dataset_assoc.dataset.dataset )
|
||||
#should this also be checking library associations? - can a library item be added from a history before the job has ended? - lets not allow this to occur
|
||||
for dataset in dataset_assoc.dataset.dataset.history_associations + dataset_assoc.dataset.dataset.library_associations: #need to update all associated output hdas, i.e. history was shared with job running
|
||||
trynum = 0
|
||||
while trynum < self.app.config.retry_job_output_collection:
|
||||
try:
|
||||
# Attempt to short circuit NFS attribute caching
|
||||
os.stat( dataset.dataset.file_name )
|
||||
os.chown( dataset.dataset.file_name, os.getuid(), -1 )
|
||||
trynum = self.app.config.retry_job_output_collection
|
||||
except ( OSError, ObjectNotFound ), e:
|
||||
trynum += 1
|
||||
log.warning( 'Error accessing %s, will retry: %s', dataset.dataset.file_name, e )
|
||||
time.sleep( 2 )
|
||||
dataset.blurb = 'done'
|
||||
dataset.peek = 'no peek'
|
||||
dataset.info = (dataset.info or '')
|
||||
@@ -1483,6 +1495,12 @@ class JobWrapper( object ):
|
||||
just copy these files directly to the ulimate destination.
|
||||
"""
|
||||
return output_path
|
||||
|
||||
@property
|
||||
def requires_setting_metadata( self ):
|
||||
if self.tool:
|
||||
return self.tool.requires_setting_metadata
|
||||
return False
|
||||
|
||||
|
||||
class TaskWrapper(JobWrapper):
|
||||
|
||||
@@ -84,12 +84,25 @@ class JobHandlerQueue( object ):
|
||||
Checks all jobs that are in the 'new', 'queued' or 'running' state in
|
||||
the database and requeues or cleans up as necessary. Only run as the
|
||||
job handler starts.
|
||||
In case the activation is enforced it will filter out the jobs of inactive users.
|
||||
"""
|
||||
for job in self.sa_session.query( model.Job ).enable_eagerloads( False ) \
|
||||
jobs_at_startup = []
|
||||
if self.app.config.user_activation_on:
|
||||
jobs_at_startup = self.sa_session.query( model.Job ).enable_eagerloads( False ) \
|
||||
.outerjoin( model.User ) \
|
||||
.filter( ( ( model.Job.state == model.Job.states.NEW ) \
|
||||
| ( model.Job.state == model.Job.states.RUNNING ) \
|
||||
| ( model.Job.state == model.Job.states.QUEUED ) ) \
|
||||
& ( model.Job.handler == self.app.config.server_name ) ):
|
||||
& ( model.Job.handler == self.app.config.server_name ) \
|
||||
& or_( ( model.Job.user_id == None ),( model.User.active == True ) ) ).all()
|
||||
else:
|
||||
jobs_at_startup = self.sa_session.query( model.Job ).enable_eagerloads( False ) \
|
||||
.filter( ( ( model.Job.state == model.Job.states.NEW ) \
|
||||
| ( model.Job.state == model.Job.states.RUNNING ) \
|
||||
| ( model.Job.state == model.Job.states.QUEUED ) ) \
|
||||
& ( model.Job.handler == self.app.config.server_name ) ).all()
|
||||
|
||||
for job in jobs_at_startup:
|
||||
if job.tool_id not in self.app.toolbox.tools_by_id:
|
||||
log.warning( "(%s) Tool '%s' removed from tool config, unable to recover job" % ( job.id, job.tool_id ) )
|
||||
JobWrapper( job, self ).fail( 'This tool was disabled before the job completed. Please contact your Galaxy administrator.' )
|
||||
@@ -146,8 +159,9 @@ class JobHandlerQueue( object ):
|
||||
over all new and waiting jobs to check the state of the jobs each
|
||||
depends on. If the job has dependencies that have not finished, it
|
||||
it goes to the waiting queue. If the job has dependencies with errors,
|
||||
it is marked as having errors and removed from the queue. Otherwise,
|
||||
the job is dispatched.
|
||||
it is marked as having errors and removed from the queue. If the job
|
||||
belongs to an inactive user it is ignored.
|
||||
Otherwise, the job is dispatched.
|
||||
"""
|
||||
# Pull all new jobs from the queue at once
|
||||
jobs_to_check = []
|
||||
@@ -173,7 +187,17 @@ class JobHandlerQueue( object ):
|
||||
(model.LibraryDatasetDatasetAssociation.deleted == True),
|
||||
(model.Dataset.state != model.Dataset.states.OK),
|
||||
(model.Dataset.deleted == True)))).subquery()
|
||||
jobs_to_check = self.sa_session.query(model.Job).enable_eagerloads(False) \
|
||||
if self.app.config.user_activation_on:
|
||||
jobs_to_check = self.sa_session.query(model.Job).enable_eagerloads(False) \
|
||||
.outerjoin( model.User ) \
|
||||
.filter(and_((model.Job.state == model.Job.states.NEW),
|
||||
or_((model.Job.user_id == None),(model.User.active == True)),
|
||||
(model.Job.handler == self.app.config.server_name),
|
||||
~model.Job.table.c.id.in_(hda_not_ready),
|
||||
~model.Job.table.c.id.in_(ldda_not_ready))) \
|
||||
.order_by(model.Job.id).all()
|
||||
else:
|
||||
jobs_to_check = self.sa_session.query(model.Job).enable_eagerloads(False) \
|
||||
.filter(and_((model.Job.state == model.Job.states.NEW),
|
||||
(model.Job.handler == self.app.config.server_name),
|
||||
~model.Job.table.c.id.in_(hda_not_ready),
|
||||
|
||||
@@ -167,16 +167,28 @@ class BaseJobRunner( object ):
|
||||
if job_wrapper.dependency_shell_commands:
|
||||
commands = "; ".join( job_wrapper.dependency_shell_commands + [ commands ] )
|
||||
|
||||
# Coping work dir outputs or setting metadata will mask return code of
|
||||
# tool command. If these are used capture the return code and ensure
|
||||
# the last thing that happens is an exit with return code.
|
||||
capture_return_code_command = "; return_code=$?"
|
||||
captured_return_code = False
|
||||
|
||||
# Append commands to copy job outputs based on from_work_dir attribute.
|
||||
if include_work_dir_outputs:
|
||||
work_dir_outputs = self.get_work_dir_outputs( job_wrapper )
|
||||
if work_dir_outputs:
|
||||
if not captured_return_code:
|
||||
commands += capture_return_code_command
|
||||
captured_return_code = True
|
||||
commands += "; " + "; ".join( [ "if [ -f %s ] ; then cp %s %s ; fi" %
|
||||
( source_file, source_file, destination ) for ( source_file, destination ) in work_dir_outputs ] )
|
||||
|
||||
# Append metadata setting commands, we don't want to overwrite metadata
|
||||
# that was copied over in init_meta(), as per established behavior
|
||||
if include_metadata:
|
||||
if include_metadata and job_wrapper.requires_setting_metadata:
|
||||
if not captured_return_code:
|
||||
commands += capture_return_code_command
|
||||
captured_return_code = True
|
||||
commands += "; cd %s; " % os.path.abspath( os.getcwd() )
|
||||
commands += job_wrapper.setup_external_metadata(
|
||||
exec_dir = os.path.abspath( os.getcwd() ),
|
||||
@@ -185,6 +197,11 @@ class BaseJobRunner( object ):
|
||||
output_fnames = job_wrapper.get_output_fnames(),
|
||||
set_extension = False,
|
||||
kwds = { 'overwrite' : False } )
|
||||
|
||||
|
||||
if captured_return_code:
|
||||
commands += '; sh -c "exit $return_code"'
|
||||
|
||||
return commands
|
||||
|
||||
def get_work_dir_outputs( self, job_wrapper ):
|
||||
|
||||
@@ -166,7 +166,17 @@ class DRMAAJobRunner( AsynchronousJobRunner ):
|
||||
|
||||
# runJob will raise if there's a submit problem
|
||||
if self.external_runJob_script is None:
|
||||
external_job_id = self.ds.runJob(jt)
|
||||
# TODO: create a queue for retrying submission indefinitely
|
||||
# TODO: configurable max tries and sleep
|
||||
trynum = 0
|
||||
external_job_id = None
|
||||
while external_job_id is None and trynum < 5:
|
||||
try:
|
||||
external_job_id = self.ds.runJob(jt)
|
||||
except drmaa.InternalException, e:
|
||||
trynum += 1
|
||||
log.warning( '(%s) drmaa.Session.runJob() failed, will retry: %s', galaxy_id_tag, e )
|
||||
time.sleep( 5 )
|
||||
else:
|
||||
job_wrapper.change_ownership_for_run()
|
||||
log.debug( '(%s) submitting with credentials: %s [uid: %s]' % ( galaxy_id_tag, job_wrapper.user_system_pwent[0], job_wrapper.user_system_pwent[2] ) )
|
||||
@@ -226,7 +236,13 @@ class DRMAAJobRunner( AsynchronousJobRunner ):
|
||||
if state == drmaa.JobState.RUNNING and not ajs.running:
|
||||
ajs.running = True
|
||||
ajs.job_wrapper.change_state( model.Job.states.RUNNING )
|
||||
if state in ( drmaa.JobState.DONE, drmaa.JobState.FAILED ):
|
||||
if state == drmaa.JobState.FAILED:
|
||||
if ajs.job_wrapper.get_state() != model.Job.states.DELETED:
|
||||
ajs.stop_job = False
|
||||
ajs.fail_message = "The cluster DRM system terminated this job"
|
||||
self.work_queue.put( ( self.fail_job, ajs ) )
|
||||
continue
|
||||
if state == drmaa.JobState.DONE:
|
||||
if ajs.job_wrapper.get_state() != model.Job.states.DELETED:
|
||||
self.work_queue.put( ( self.finish_job, ajs ) )
|
||||
continue
|
||||
|
||||
@@ -25,7 +25,7 @@ import galaxy.security.passwords
|
||||
from galaxy.datatypes.metadata import MetadataCollection
|
||||
from galaxy.model.item_attrs import Dictifiable, UsesAnnotations
|
||||
from galaxy.security import get_permitted_actions
|
||||
from galaxy.util import is_multi_byte, nice_size, Params, restore_text, send_mail
|
||||
from galaxy.util import asbool, is_multi_byte, nice_size, Params, restore_text, send_mail
|
||||
from galaxy.util.bunch import Bunch
|
||||
from galaxy.util.hash_util import new_secure_hash
|
||||
from galaxy.web.framework.helpers import to_unicode
|
||||
@@ -34,6 +34,7 @@ from galaxy.web.form_builder import (AddressField, CheckboxField, HistoryField,
|
||||
WorkflowMappingField)
|
||||
from sqlalchemy.orm import object_session
|
||||
from sqlalchemy.sql.expression import func
|
||||
from tool_shed.util import common_util
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
@@ -78,6 +79,8 @@ class User( object, Dictifiable ):
|
||||
self.external = False
|
||||
self.deleted = False
|
||||
self.purged = False
|
||||
self.active = False
|
||||
self.activation_token = None
|
||||
self.username = None
|
||||
# Relationships
|
||||
self.histories = []
|
||||
@@ -1680,7 +1683,7 @@ class HistoryDatasetAssociation( DatasetInstance, Dictifiable, UsesAnnotations )
|
||||
rval += child.get_disk_usage( user )
|
||||
return rval
|
||||
|
||||
def to_dict( self, view='collection' ):
|
||||
def to_dict( self, view='collection', expose_dataset_path=False ):
|
||||
"""
|
||||
Return attributes of this HDA that are exposed using the API.
|
||||
"""
|
||||
@@ -1713,6 +1716,9 @@ class HistoryDatasetAssociation( DatasetInstance, Dictifiable, UsesAnnotations )
|
||||
for name, spec in hda.metadata.spec.items():
|
||||
val = hda.metadata.get( name )
|
||||
if isinstance( val, MetadataFile ):
|
||||
# only when explicitly set: fetching filepaths can be expensive
|
||||
if not expose_dataset_path:
|
||||
continue
|
||||
val = val.file_name
|
||||
# If no value for metadata, look in datatype for metadata.
|
||||
elif val == None and hasattr( hda.datatype, name ):
|
||||
@@ -2323,9 +2329,12 @@ class UCI( object ):
|
||||
self.id = None
|
||||
self.user = None
|
||||
|
||||
|
||||
class StoredWorkflow( object, Dictifiable):
|
||||
|
||||
dict_collection_visible_keys = ( 'id', 'name', 'published' )
|
||||
dict_element_visible_keys = ( 'id', 'name', 'published' )
|
||||
|
||||
def __init__( self ):
|
||||
self.id = None
|
||||
self.user = None
|
||||
@@ -2342,7 +2351,7 @@ class StoredWorkflow( object, Dictifiable):
|
||||
self.tags.append(new_swta)
|
||||
|
||||
def to_dict( self, view='collection', value_mapper = None ):
|
||||
rval = super( StoredWorkflow, self ).to_dict(self, view=view, value_mapper = value_mapper)
|
||||
rval = super( StoredWorkflow, self ).to_dict( view=view, value_mapper = value_mapper )
|
||||
tags_str_list = []
|
||||
for tag in self.tags:
|
||||
tag_str = tag.user_tname
|
||||
@@ -2353,7 +2362,11 @@ class StoredWorkflow( object, Dictifiable):
|
||||
return rval
|
||||
|
||||
|
||||
class Workflow( object ):
|
||||
class Workflow( object, Dictifiable ):
|
||||
|
||||
dict_collection_visible_keys = ( 'name', 'has_cycles', 'has_errors' )
|
||||
dict_element_visible_keys = ( 'name', 'has_cycles', 'has_errors' )
|
||||
|
||||
def __init__( self ):
|
||||
self.user = None
|
||||
self.name = None
|
||||
@@ -2361,7 +2374,9 @@ class Workflow( object ):
|
||||
self.has_errors = None
|
||||
self.steps = []
|
||||
|
||||
|
||||
class WorkflowStep( object ):
|
||||
|
||||
def __init__( self ):
|
||||
self.id = None
|
||||
self.type = None
|
||||
@@ -2372,36 +2387,48 @@ class WorkflowStep( object ):
|
||||
self.input_connections = []
|
||||
self.config = None
|
||||
|
||||
|
||||
class WorkflowStepConnection( object ):
|
||||
|
||||
def __init__( self ):
|
||||
self.output_step_id = None
|
||||
self.output_name = None
|
||||
self.input_step_id = None
|
||||
self.input_name = None
|
||||
|
||||
|
||||
class WorkflowOutput(object):
|
||||
|
||||
def __init__( self, workflow_step, output_name):
|
||||
self.workflow_step = workflow_step
|
||||
self.output_name = output_name
|
||||
|
||||
|
||||
class StoredWorkflowUserShareAssociation( object ):
|
||||
|
||||
def __init__( self ):
|
||||
self.stored_workflow = None
|
||||
self.user = None
|
||||
|
||||
|
||||
class StoredWorkflowMenuEntry( object ):
|
||||
|
||||
def __init__( self ):
|
||||
self.stored_workflow = None
|
||||
self.user = None
|
||||
self.order_index = None
|
||||
|
||||
|
||||
class WorkflowInvocation( object ):
|
||||
pass
|
||||
|
||||
|
||||
class WorkflowInvocationStep( object ):
|
||||
pass
|
||||
|
||||
|
||||
class MetadataFile( object ):
|
||||
|
||||
def __init__( self, dataset = None, name = None ):
|
||||
if isinstance( dataset, HistoryDatasetAssociation ):
|
||||
self.history_dataset = dataset
|
||||
@@ -2416,7 +2443,8 @@ class MetadataFile( object ):
|
||||
da = self.history_dataset or self.library_dataset
|
||||
if self.object_store_id is None and da is not None:
|
||||
self.object_store_id = da.dataset.object_store_id
|
||||
da.dataset.object_store.create( self, extra_dir='_metadata_files', extra_dir_at_root=True, alt_name="metadata_%d.dat" % self.id )
|
||||
if not da.dataset.object_store.exists( self, extra_dir='_metadata_files', extra_dir_at_root=True, alt_name="metadata_%d.dat" % self.id ):
|
||||
da.dataset.object_store.create( self, extra_dir='_metadata_files', extra_dir_at_root=True, alt_name="metadata_%d.dat" % self.id )
|
||||
path = da.dataset.object_store.get_filename( self, extra_dir='_metadata_files', extra_dir_at_root=True, alt_name="metadata_%d.dat" % self.id )
|
||||
return path
|
||||
except AttributeError:
|
||||
@@ -3462,7 +3490,28 @@ class ToolShedRepository( object ):
|
||||
@property
|
||||
def has_repository_dependencies( self ):
|
||||
if self.metadata:
|
||||
return 'repository_dependencies' in self.metadata
|
||||
repository_dependencies_dict = self.metadata.get( 'repository_dependencies', {} )
|
||||
repository_dependencies = repository_dependencies_dict.get( 'repository_dependencies', [] )
|
||||
# [["http://localhost:9009", "package_libgtextutils_0_6", "test", "e2003cbf18cd", "True", "True"]]
|
||||
for rd_tup in repository_dependencies:
|
||||
tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
|
||||
common_util.parse_repository_dependency_tuple( rd_tup )
|
||||
if not asbool( only_if_compiling_contained_td ):
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def has_repository_dependencies_only_if_compiling_contained_td( self ):
|
||||
if self.metadata:
|
||||
repository_dependencies_dict = self.metadata.get( 'repository_dependencies', {} )
|
||||
repository_dependencies = repository_dependencies_dict.get( 'repository_dependencies', [] )
|
||||
# [["http://localhost:9009", "package_libgtextutils_0_6", "test", "e2003cbf18cd", "True", "True"]]
|
||||
for rd_tup in repository_dependencies:
|
||||
tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
|
||||
common_util.parse_repository_dependency_tuple( rd_tup )
|
||||
if not asbool( only_if_compiling_contained_td ):
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
@@ -3694,10 +3743,14 @@ class ToolShedRepository( object ):
|
||||
|
||||
@property
|
||||
def tuples_of_repository_dependencies_needed_for_compiling_td( self ):
|
||||
"""Return this repository's repository dependencies that are necessary only for compiling this repository's tool dependencies."""
|
||||
"""
|
||||
Return tuples defining this repository's repository dependencies that are necessary only for compiling this repository's tool
|
||||
dependencies.
|
||||
"""
|
||||
rd_tups_of_repositories_needed_for_compiling_td = []
|
||||
if self.has_repository_dependencies:
|
||||
rd_tups = self.metadata[ 'repository_dependencies' ][ 'repository_dependencies' ]
|
||||
if self.metadata:
|
||||
repository_dependencies = self.metadata.get( 'repository_dependencies', None )
|
||||
rd_tups = repository_dependencies[ 'repository_dependencies' ]
|
||||
for rd_tup in rd_tups:
|
||||
if len( rd_tup ) == 6:
|
||||
tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = rd_tup
|
||||
@@ -3859,7 +3912,7 @@ class ToolVersion( object, Dictifiable ):
|
||||
return [ tool_version.tool_id for tool_version in self.get_versions( app ) ]
|
||||
|
||||
def to_dict( self, view='element' ):
|
||||
rval = super( ToolVersion, self ).to_dict( self, view )
|
||||
rval = super( ToolVersion, self ).to_dict( view=view )
|
||||
rval['tool_name'] = self.tool_id
|
||||
for a in self.parent_tool_association:
|
||||
rval['parent_tool_id'] = a.parent_id
|
||||
|
||||
@@ -29,6 +29,8 @@ def _sniffnfix_pg9_hex(value):
|
||||
try:
|
||||
if value[0] == 'x':
|
||||
return binascii.unhexlify(value[1:])
|
||||
elif value.startswith( '\\x' ):
|
||||
return binascii.unhexlify( value[2:] )
|
||||
else:
|
||||
return value
|
||||
except Exception, ex:
|
||||
|
||||
@@ -52,7 +52,9 @@ model.User.table = Table( "galaxy_user", metadata,
|
||||
Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ),
|
||||
Column( "deleted", Boolean, index=True, default=False ),
|
||||
Column( "purged", Boolean, index=True, default=False ),
|
||||
Column( "disk_usage", Numeric( 15, 0 ), index=True ) )
|
||||
Column( "disk_usage", Numeric( 15, 0 ), index=True ) ,
|
||||
Column( "active", Boolean, index=True, default=True, nullable=False ),
|
||||
Column( "activation_token", TrimmedString( 64 ), nullable=True, index=True ) )
|
||||
|
||||
model.UserAddress.table = Table( "user_address", metadata,
|
||||
Column( "id", Integer, primary_key=True),
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
'''
|
||||
Created on Sep 10, 2013
|
||||
|
||||
@author: marten
|
||||
|
||||
Adds 'active' and 'activation_token' columns to the galaxy_user table.
|
||||
'''
|
||||
|
||||
from sqlalchemy import *
|
||||
from sqlalchemy.orm import *
|
||||
from migrate import *
|
||||
from migrate.changeset import *
|
||||
from galaxy.model.custom_types import TrimmedString
|
||||
|
||||
import logging
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
user_active_column = Column( "active", Boolean, default=True, nullable=True )
|
||||
user_activation_token_column = Column( "activation_token", TrimmedString( 64 ), nullable=True )
|
||||
|
||||
|
||||
def display_migration_details():
|
||||
print ""
|
||||
print "This migration script adds active and activation_token columns to the user table"
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
print __doc__
|
||||
metadata = MetaData()
|
||||
metadata.bind = migrate_engine
|
||||
metadata.reflect()
|
||||
|
||||
# Add the active and activation_token columns to the user table in one try because the depend on each other.
|
||||
try:
|
||||
user_table = Table( "galaxy_user", metadata, autoload=True )
|
||||
user_active_column.create( table = user_table , populate_default = True)
|
||||
user_activation_token_column.create( table = user_table )
|
||||
assert user_active_column is user_table.c.active
|
||||
assert user_activation_token_column is user_table.c.activation_token
|
||||
except Exception, e:
|
||||
print str(e)
|
||||
log.error( "Adding columns 'active' and 'activation_token' to galaxy_user table failed: %s" % str( e ) )
|
||||
return
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
metadata = MetaData()
|
||||
metadata.bind = migrate_engine
|
||||
metadata.reflect()
|
||||
|
||||
# Drop the user table's active and activation_token columns in one try because the depend on each other.
|
||||
try:
|
||||
user_table = Table( "galaxy_user", metadata, autoload=True )
|
||||
user_active = user_table.c.active
|
||||
user_activation_token = user_table.c.activation_token
|
||||
user_active.drop()
|
||||
user_activation_token.drop()
|
||||
except Exception, e:
|
||||
log.debug( "Dropping 'active' and 'activation_token' columns from galaxy_user table failed: %s" % ( str( e ) ) )
|
||||
@@ -5,7 +5,6 @@ tools
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import random
|
||||
import shutil
|
||||
import logging
|
||||
@@ -15,6 +14,7 @@ from galaxy import util
|
||||
from galaxy.jobs import Sleeper
|
||||
from galaxy.model import directory_hash_id
|
||||
from galaxy.exceptions import ObjectNotFound, ObjectInvalid
|
||||
from galaxy.util.odict import odict
|
||||
|
||||
from sqlalchemy.orm import object_session
|
||||
|
||||
@@ -25,7 +25,7 @@ class ObjectStore(object):
|
||||
"""
|
||||
ObjectStore abstract interface
|
||||
"""
|
||||
def __init__(self, config, **kwargs):
|
||||
def __init__(self, config, config_xml=None, **kwargs):
|
||||
self.running = True
|
||||
self.extra_dirs = {}
|
||||
|
||||
@@ -188,18 +188,26 @@ class DiskObjectStore(ObjectStore):
|
||||
>>> import tempfile
|
||||
>>> file_path=tempfile.mkdtemp()
|
||||
>>> obj = Bunch(id=1)
|
||||
>>> s = DiskObjectStore(Bunch(umask=077, job_working_directory=file_path, new_file_path=file_path), file_path=file_path)
|
||||
>>> s = DiskObjectStore(Bunch(umask=077, job_working_directory=file_path, new_file_path=file_path, object_store_check_old_style=False), file_path=file_path)
|
||||
>>> s.create(obj)
|
||||
>>> s.exists(obj)
|
||||
True
|
||||
>>> assert s.get_filename(obj) == file_path + '/000/dataset_1.dat'
|
||||
"""
|
||||
def __init__(self, config, file_path=None, extra_dirs=None):
|
||||
super(DiskObjectStore, self).__init__(config, file_path=file_path, extra_dirs=extra_dirs)
|
||||
def __init__(self, config, config_xml=None, file_path=None, extra_dirs=None):
|
||||
super(DiskObjectStore, self).__init__(config, config_xml=None, file_path=file_path, extra_dirs=extra_dirs)
|
||||
self.file_path = file_path or config.file_path
|
||||
self.config = config
|
||||
self.check_old_style = config.object_store_check_old_style
|
||||
self.extra_dirs['job_work'] = config.job_working_directory
|
||||
self.extra_dirs['temp'] = config.new_file_path
|
||||
#The new config_xml overrides universe settings.
|
||||
if config_xml:
|
||||
for e in config_xml:
|
||||
if e.tag == 'files_dir':
|
||||
self.file_path = e.get('path')
|
||||
else:
|
||||
self.extra_dirs[e.tag] = e.get('path')
|
||||
if extra_dirs is not None:
|
||||
self.extra_dirs.update( extra_dirs )
|
||||
|
||||
@@ -264,14 +272,13 @@ class DiskObjectStore(ObjectStore):
|
||||
return os.path.abspath(path)
|
||||
|
||||
def exists(self, obj, **kwargs):
|
||||
path = self._construct_path(obj, old_style=True, **kwargs)
|
||||
# For backward compatibility, check root path first; otherwise, construct
|
||||
# and check hashed path
|
||||
if os.path.exists(path):
|
||||
return True
|
||||
else:
|
||||
path = self._construct_path(obj, **kwargs)
|
||||
return os.path.exists(path)
|
||||
if self.check_old_style:
|
||||
path = self._construct_path(obj, old_style=True, **kwargs)
|
||||
# For backward compatibility, check root path first; otherwise, construct
|
||||
# and check hashed path
|
||||
if os.path.exists(path):
|
||||
return True
|
||||
return os.path.exists(self._construct_path(obj, **kwargs))
|
||||
|
||||
def create(self, obj, **kwargs):
|
||||
if not self.exists(obj, **kwargs):
|
||||
@@ -320,13 +327,13 @@ class DiskObjectStore(ObjectStore):
|
||||
return content
|
||||
|
||||
def get_filename(self, obj, **kwargs):
|
||||
path = self._construct_path(obj, old_style=True, **kwargs)
|
||||
# For backward compatibility, check root path first; otherwise, construct
|
||||
# and return hashed path
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
else:
|
||||
return self._construct_path(obj, **kwargs)
|
||||
if self.check_old_style:
|
||||
path = self._construct_path(obj, old_style=True, **kwargs)
|
||||
# For backward compatibility, check root path first; otherwise, construct
|
||||
# and return hashed path
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return self._construct_path(obj, **kwargs)
|
||||
|
||||
def update_from_file(self, obj, file_name=None, create=False, **kwargs):
|
||||
""" `create` parameter is not used in this implementation """
|
||||
@@ -364,29 +371,89 @@ class CachingObjectStore(ObjectStore):
|
||||
super(CachingObjectStore, self).__init__(self, path, backend)
|
||||
|
||||
|
||||
class DistributedObjectStore(ObjectStore):
|
||||
class NestedObjectStore(ObjectStore):
|
||||
"""
|
||||
Base for ObjectStores that use other ObjectStores
|
||||
(DistributedObjectStore, HierarchicalObjectStore)
|
||||
"""
|
||||
|
||||
def __init__(self, config, config_xml=None):
|
||||
super(NestedObjectStore, self).__init__(config, config_xml=config_xml)
|
||||
self.backends = {}
|
||||
|
||||
def shutdown(self):
|
||||
for store in self.backends.values():
|
||||
store.shutdown()
|
||||
super(NestedObjectStore, self).shutdown()
|
||||
|
||||
def exists(self, obj, **kwargs):
|
||||
return self.__call_method('exists', obj, False, False, **kwargs)
|
||||
|
||||
def file_ready(self, obj, **kwargs):
|
||||
return self.__call_method('file_ready', obj, False, False, **kwargs)
|
||||
|
||||
def create(self, obj, **kwargs):
|
||||
random.choice(self.backends.values()).create(obj, **kwargs)
|
||||
|
||||
def empty(self, obj, **kwargs):
|
||||
return self.__call_method('empty', obj, True, False, **kwargs)
|
||||
|
||||
def size(self, obj, **kwargs):
|
||||
return self.__call_method('size', obj, 0, False, **kwargs)
|
||||
|
||||
def delete(self, obj, **kwargs):
|
||||
return self.__call_method('delete', obj, False, False, **kwargs)
|
||||
|
||||
def get_data(self, obj, **kwargs):
|
||||
return self.__call_method('get_data', obj, ObjectNotFound, True, **kwargs)
|
||||
|
||||
def get_filename(self, obj, **kwargs):
|
||||
return self.__call_method('get_filename', obj, ObjectNotFound, True, **kwargs)
|
||||
|
||||
def update_from_file(self, obj, **kwargs):
|
||||
if kwargs.get('create', False):
|
||||
self.create(obj, **kwargs)
|
||||
kwargs['create'] = False
|
||||
return self.__call_method('update_from_file', obj, ObjectNotFound, True, **kwargs)
|
||||
|
||||
def get_object_url(self, obj, **kwargs):
|
||||
return self.__call_method('get_object_url', obj, None, False, **kwargs)
|
||||
|
||||
def __call_method(self, method, obj, default, default_is_exception, **kwargs):
|
||||
"""
|
||||
Check all children object stores for the first one with the dataset
|
||||
"""
|
||||
for key, store in self.backends.items():
|
||||
if store.exists(obj, **kwargs):
|
||||
return store.__getattribute__(method)(obj, **kwargs)
|
||||
if default_is_exception:
|
||||
raise default( 'objectstore, __call_method failed: %s on %s, kwargs: %s'
|
||||
%( method, str( obj ), str( kwargs ) ) )
|
||||
else:
|
||||
return default
|
||||
|
||||
|
||||
class DistributedObjectStore(NestedObjectStore):
|
||||
"""
|
||||
ObjectStore that defers to a list of backends, for getting objects the
|
||||
first store where the object exists is used, objects are created in a
|
||||
store selected randomly, but with weighting.
|
||||
"""
|
||||
|
||||
def __init__(self, config, fsmon=False):
|
||||
super(DistributedObjectStore, self).__init__(config)
|
||||
self.distributed_config = config.distributed_object_store_config_file
|
||||
assert self.distributed_config is not None, "distributed object store ('object_store = distributed') " \
|
||||
"requires a config file, please set one in " \
|
||||
"'distributed_object_store_config_file')"
|
||||
def __init__(self, config, config_xml=None, fsmon=False):
|
||||
super(DistributedObjectStore, self).__init__(config, config_xml=config_xml)
|
||||
if not config_xml:
|
||||
self.distributed_config = config.distributed_object_store_config_file
|
||||
assert self.distributed_config is not None, "distributed object store ('object_store = distributed') " \
|
||||
"requires a config file, please set one in " \
|
||||
"'distributed_object_store_config_file')"
|
||||
self.backends = {}
|
||||
self.weighted_backend_ids = []
|
||||
self.original_weighted_backend_ids = []
|
||||
self.max_percent_full = {}
|
||||
self.global_max_percent_full = 0.0
|
||||
|
||||
random.seed()
|
||||
|
||||
self.__parse_distributed_config(config)
|
||||
|
||||
self.__parse_distributed_config(config, config_xml)
|
||||
self.sleeper = None
|
||||
if fsmon and ( self.global_max_percent_full or filter( lambda x: x != 0.0, self.max_percent_full.values() ) ):
|
||||
self.sleeper = Sleeper()
|
||||
@@ -395,10 +462,14 @@ class DistributedObjectStore(ObjectStore):
|
||||
self.filesystem_monitor_thread.start()
|
||||
log.info("Filesystem space monitor started")
|
||||
|
||||
def __parse_distributed_config(self, config):
|
||||
tree = util.parse_xml(self.distributed_config)
|
||||
root = tree.getroot()
|
||||
log.debug('Loading backends for distributed object store from %s' % self.distributed_config)
|
||||
def __parse_distributed_config(self, config, config_xml = None):
|
||||
if not config_xml:
|
||||
tree = util.parse_xml(self.distributed_config)
|
||||
root = tree.getroot()
|
||||
log.debug('Loading backends for distributed object store from %s' % self.distributed_config)
|
||||
else:
|
||||
root = config_xml.find('backends')
|
||||
log.debug('Loading backends for distributed object store from %s' % config_xml.get('id'))
|
||||
self.global_max_percent_full = float(root.get('maxpctfull', 0))
|
||||
for elem in [ e for e in root if e.tag == 'backend' ]:
|
||||
id = elem.get('id')
|
||||
@@ -427,6 +498,11 @@ class DistributedObjectStore(ObjectStore):
|
||||
self.weighted_backend_ids.append(id)
|
||||
self.original_weighted_backend_ids = self.weighted_backend_ids
|
||||
|
||||
def shutdown(self):
|
||||
super(DistributedObjectStore, self).shutdown()
|
||||
if self.sleeper is not None:
|
||||
self.sleeper.wake()
|
||||
|
||||
def __filesystem_monitor(self):
|
||||
while self.running:
|
||||
new_weighted_backend_ids = self.original_weighted_backend_ids
|
||||
@@ -438,17 +514,6 @@ class DistributedObjectStore(ObjectStore):
|
||||
self.weighted_backend_ids = new_weighted_backend_ids
|
||||
self.sleeper.sleep(120) # Test free space every 2 minutes
|
||||
|
||||
def shutdown(self):
|
||||
super(DistributedObjectStore, self).shutdown()
|
||||
if self.sleeper is not None:
|
||||
self.sleeper.wake()
|
||||
|
||||
def exists(self, obj, **kwargs):
|
||||
return self.__call_method('exists', obj, False, False, **kwargs)
|
||||
|
||||
def file_ready(self, obj, **kwargs):
|
||||
return self.__call_method('file_ready', obj, False, False, **kwargs)
|
||||
|
||||
def create(self, obj, **kwargs):
|
||||
"""
|
||||
create() is the only method in which obj.object_store_id may be None
|
||||
@@ -467,30 +532,6 @@ class DistributedObjectStore(ObjectStore):
|
||||
log.debug("Using preferred backend '%s' for creation of %s %s" % (obj.object_store_id, obj.__class__.__name__, obj.id))
|
||||
self.backends[obj.object_store_id].create(obj, **kwargs)
|
||||
|
||||
def empty(self, obj, **kwargs):
|
||||
return self.__call_method('empty', obj, True, False, **kwargs)
|
||||
|
||||
def size(self, obj, **kwargs):
|
||||
return self.__call_method('size', obj, 0, False, **kwargs)
|
||||
|
||||
def delete(self, obj, **kwargs):
|
||||
return self.__call_method('delete', obj, False, False, **kwargs)
|
||||
|
||||
def get_data(self, obj, **kwargs):
|
||||
return self.__call_method('get_data', obj, ObjectNotFound, True, **kwargs)
|
||||
|
||||
def get_filename(self, obj, **kwargs):
|
||||
return self.__call_method('get_filename', obj, ObjectNotFound, True, **kwargs)
|
||||
|
||||
def update_from_file(self, obj, **kwargs):
|
||||
if kwargs.get('create', False):
|
||||
self.create(obj, **kwargs)
|
||||
kwargs['create'] = False
|
||||
return self.__call_method('update_from_file', obj, ObjectNotFound, True, **kwargs)
|
||||
|
||||
def get_object_url(self, obj, **kwargs):
|
||||
return self.__call_method('get_object_url', obj, None, False, **kwargs)
|
||||
|
||||
def __call_method(self, method, obj, default, default_is_exception, **kwargs):
|
||||
object_store_id = self.__get_store_id_for(obj, **kwargs)
|
||||
if object_store_id is not None:
|
||||
@@ -519,33 +560,65 @@ class DistributedObjectStore(ObjectStore):
|
||||
return None
|
||||
|
||||
|
||||
class HierarchicalObjectStore(ObjectStore):
|
||||
class HierarchicalObjectStore(NestedObjectStore):
|
||||
"""
|
||||
ObjectStore that defers to a list of backends, for getting objects the
|
||||
first store where the object exists is used, objects are always created
|
||||
in the first store.
|
||||
"""
|
||||
|
||||
def __init__(self, backends=[]):
|
||||
super(HierarchicalObjectStore, self).__init__()
|
||||
def __init__(self, config, config_xml=None, fsmon=False):
|
||||
super(HierarchicalObjectStore, self).__init__(config, config_xml=config_xml)
|
||||
self.backends = odict()
|
||||
for b in sorted(config_xml.find('backends'), key=lambda b: int(b.get('order'))):
|
||||
self.backends[int(b.get('order'))] = build_object_store_from_config(config, fsmon=fsmon, config_xml=b)
|
||||
|
||||
def exists(self, obj, **kwargs):
|
||||
"""
|
||||
Exists must check all child object stores
|
||||
"""
|
||||
for store in self.backends.values():
|
||||
if store.exists(obj, **kwargs):
|
||||
return True
|
||||
return False
|
||||
|
||||
def create(self, obj, **kwargs):
|
||||
"""
|
||||
Create will always be called by the primary object_store
|
||||
"""
|
||||
self.backends[0].create(obj, **kwargs)
|
||||
|
||||
|
||||
def build_object_store_from_config(config, fsmon=False):
|
||||
""" Depending on the configuration setting, invoke the appropriate object store
|
||||
def build_object_store_from_config(config, fsmon=False, config_xml=None):
|
||||
"""
|
||||
store = config.object_store
|
||||
Depending on the configuration setting, invoke the appropriate object store
|
||||
"""
|
||||
|
||||
if not config_xml and config.object_store_config_file:
|
||||
# This is a top level invocation of build_object_store_from_config, and
|
||||
# we have an object_store_conf.xml -- read the .xml and build
|
||||
# accordingly
|
||||
tree = util.parse_xml(config.object_store_config_file)
|
||||
root = tree.getroot()
|
||||
store = root.get('type')
|
||||
config_xml = root
|
||||
elif config_xml:
|
||||
store = config_xml.get('type')
|
||||
else:
|
||||
store = config.object_store
|
||||
|
||||
if store == 'disk':
|
||||
return DiskObjectStore(config=config)
|
||||
return DiskObjectStore(config=config, config_xml=config_xml)
|
||||
elif store == 's3' or store == 'swift':
|
||||
from galaxy.objectstore.s3 import S3ObjectStore
|
||||
return S3ObjectStore(config=config)
|
||||
return S3ObjectStore(config=config, config_xml=config_xml)
|
||||
elif store == 'distributed':
|
||||
return DistributedObjectStore(config=config, fsmon=fsmon)
|
||||
return DistributedObjectStore(config=config, fsmon=fsmon, config_xml=config_xml)
|
||||
elif store == 'hierarchical':
|
||||
return HierarchicalObjectStore()
|
||||
return HierarchicalObjectStore(config=config, config_xml=config_xml)
|
||||
elif store == 'irods':
|
||||
from galaxy.objectstore.rods import IRODSObjectStore
|
||||
return IRODSObjectStore(config=config)
|
||||
return IRODSObjectStore(config=config, config_xml=config_xml)
|
||||
else:
|
||||
log.error("Unrecognized object store definition: {0}".format(store))
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class S3ObjectStore(ObjectStore):
|
||||
cache exists that is used as an intermediate location for files between
|
||||
Galaxy and S3.
|
||||
"""
|
||||
def __init__(self, config):
|
||||
def __init__(self, config, config_xml=None):
|
||||
super(S3ObjectStore, self).__init__()
|
||||
self.config = config
|
||||
self.staging_path = self.config.file_path
|
||||
|
||||
@@ -2,18 +2,27 @@ import re
|
||||
|
||||
VALID_PUBLICNAME_RE = re.compile( "^[a-z0-9\-]+$" )
|
||||
VALID_PUBLICNAME_SUB = re.compile( "[^a-z0-9\-]" )
|
||||
# Basic regular expression to check email validity.
|
||||
VALID_EMAIL_RE = re.compile( "[^@]+@[^@]+\.[^@]+" )
|
||||
FILL_CHAR = '-'
|
||||
|
||||
def validate_email( trans, email, user=None, check_dup=True ):
|
||||
"""
|
||||
Validates the email format, also checks whether the domain is blacklisted in the disposable domains configuration.
|
||||
"""
|
||||
message = ''
|
||||
if user and user.email == email:
|
||||
return message
|
||||
if len( email ) == 0 or "@" not in email or "." not in email:
|
||||
message = "Enter a real email address"
|
||||
if not( VALID_EMAIL_RE.match( email ) ):
|
||||
message = "Please enter your real email address."
|
||||
elif len( email ) > 255:
|
||||
message = "Email address exceeds maximum allowable length"
|
||||
message = "Email address exceeds maximum allowable length."
|
||||
elif check_dup and trans.sa_session.query( trans.app.model.User ).filter_by( email=email ).first():
|
||||
message = "User with that email already exists"
|
||||
message = "User with that email already exists."
|
||||
# If the blacklist is not empty filter out the disposable domains.
|
||||
elif trans.app.config.blacklist_content is not None:
|
||||
if email.split('@')[1] in trans.app.config.blacklist_content:
|
||||
message = "Please enter your permanent email address."
|
||||
return message
|
||||
|
||||
def validate_publicname( trans, publicname, user=None ):
|
||||
|
||||
@@ -11,6 +11,7 @@ import re
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import traceback
|
||||
import types
|
||||
import urllib
|
||||
@@ -154,8 +155,24 @@ class ToolBox( object, Dictifiable ):
|
||||
# This will cover cases where the Galaxy administrator manually edited one or more of the tool panel
|
||||
# config files, adding or removing locally developed tools or workflows. The value of integrated_tool_panel
|
||||
# will be False when things like functional tests are the caller.
|
||||
self.fix_integrated_tool_panel_dict()
|
||||
self.write_integrated_tool_panel_config_file()
|
||||
|
||||
def fix_integrated_tool_panel_dict( self ):
|
||||
# HACK: instead of fixing after the fact, I suggest some combination of:
|
||||
# 1) adjusting init_tools() and called methods to get this right
|
||||
# 2) redesigning the code and/or data structure used to read/write integrated_tool_panel.xml
|
||||
for key, value in self.integrated_tool_panel.iteritems():
|
||||
if key.startswith( 'section_' ):
|
||||
for section_key, section_value in value.elems.iteritems():
|
||||
if section_value is None:
|
||||
if section_key.startswith( 'tool_' ):
|
||||
tool_id = section_key[5:]
|
||||
value.elems[section_key] = self.tools_by_id.get( tool_id )
|
||||
elif section_key.startswith( 'workflow_' ):
|
||||
workflow_id = section_key[9:]
|
||||
value.elems[section_key] = self.workflows_by_id.get( workflow_id )
|
||||
|
||||
def init_tools( self, config_filename ):
|
||||
"""
|
||||
Read the configuration file and load each tool. The following tags are currently supported:
|
||||
@@ -954,6 +971,7 @@ class Tool( object, Dictifiable ):
|
||||
"""
|
||||
|
||||
tool_type = 'default'
|
||||
requires_setting_metadata = True
|
||||
default_tool_action = DefaultToolAction
|
||||
dict_collection_visible_keys = ( 'id', 'name', 'version', 'description' )
|
||||
|
||||
@@ -1317,19 +1335,16 @@ class Tool( object, Dictifiable ):
|
||||
help_header = ""
|
||||
help_footer = ""
|
||||
if self.help is not None:
|
||||
# Handle tool help image display for tools that are contained in repositories that are in the tool shed or installed into Galaxy.
|
||||
# When tool config files use the special string $PATH_TO_IMAGES, the following code will replace that string with the path on disk.
|
||||
if self.repository_id and self.help.text.find( '$PATH_TO_IMAGES' ) >= 0:
|
||||
if self.app.name == 'galaxy':
|
||||
repository = self.sa_session.query( self.app.model.ToolShedRepository ).get( self.app.security.decode_id( self.repository_id ) )
|
||||
if repository:
|
||||
path_to_images = '/tool_runner/static/images/%s' % self.repository_id
|
||||
self.help.text = self.help.text.replace( '$PATH_TO_IMAGES', path_to_images )
|
||||
elif self.app.name == 'tool_shed':
|
||||
repository = self.sa_session.query( self.app.model.Repository ).get( self.app.security.decode_id( self.repository_id ) )
|
||||
if repository:
|
||||
path_to_images = '/repository/static/images/%s' % self.repository_id
|
||||
self.help.text = self.help.text.replace( '$PATH_TO_IMAGES', path_to_images )
|
||||
if self.repository_id and self.help.text.find( '.. image:: ' ) >= 0:
|
||||
# Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy.
|
||||
lock = threading.Lock()
|
||||
lock.acquire( True )
|
||||
try:
|
||||
self.help.text = shed_util_common.set_image_paths( self.app, self.repository_id, self.help.text )
|
||||
except Exception, e:
|
||||
log.exception( "Exception in parse_help, so images may not be properly displayed:\n%s" % str( e ) )
|
||||
finally:
|
||||
lock.release()
|
||||
help_pages = self.help.findall( "page" )
|
||||
help_header = self.help.text
|
||||
try:
|
||||
@@ -1852,7 +1867,7 @@ class Tool( object, Dictifiable ):
|
||||
# TODO: Anyway to capture tools that dynamically change their own
|
||||
# outputs?
|
||||
return True
|
||||
def new_state( self, trans, all_pages=False ):
|
||||
def new_state( self, trans, all_pages=False, history=None ):
|
||||
"""
|
||||
Create a new `DefaultToolState` for this tool. It will be initialized
|
||||
with default values for inputs.
|
||||
@@ -1866,16 +1881,16 @@ class Tool( object, Dictifiable ):
|
||||
inputs = self.inputs
|
||||
else:
|
||||
inputs = self.inputs_by_page[ 0 ]
|
||||
self.fill_in_new_state( trans, inputs, state.inputs )
|
||||
self.fill_in_new_state( trans, inputs, state.inputs, history=history )
|
||||
return state
|
||||
def fill_in_new_state( self, trans, inputs, state, context=None ):
|
||||
def fill_in_new_state( self, trans, inputs, state, context=None, history=None ):
|
||||
"""
|
||||
Fill in a tool state dictionary with default values for all parameters
|
||||
in the dictionary `inputs`. Grouping elements are filled in recursively.
|
||||
"""
|
||||
context = ExpressionContext( state, context )
|
||||
for input in inputs.itervalues():
|
||||
state[ input.name ] = input.get_initial_value( trans, context )
|
||||
state[ input.name ] = input.get_initial_value( trans, context, history=history )
|
||||
def get_param_html_map( self, trans, page=0, other_values={} ):
|
||||
"""
|
||||
Return a dictionary containing the HTML representation of each
|
||||
@@ -1938,7 +1953,7 @@ class Tool( object, Dictifiable ):
|
||||
state = DefaultToolState()
|
||||
state.decode( encoded_state, self, trans.app )
|
||||
else:
|
||||
state = self.new_state( trans )
|
||||
state = self.new_state( trans, history=history )
|
||||
# This feels a bit like a hack. It allows forcing full processing
|
||||
# of inputs even when there is no state in the incoming dictionary
|
||||
# by providing either 'runtool_btn' (the name of the submit button
|
||||
@@ -3120,6 +3135,8 @@ class SetMetadataTool( Tool ):
|
||||
dataset.
|
||||
"""
|
||||
tool_type = 'set_metadata'
|
||||
requires_setting_metadata = False
|
||||
|
||||
def exec_after_process( self, app, inp_data, out_data, param_dict, job = None ):
|
||||
for name, dataset in inp_data.iteritems():
|
||||
external_metadata = JobExternalOutputMetadataWrapper( job )
|
||||
|
||||
@@ -43,6 +43,9 @@ class ToolDataTableManager( object ):
|
||||
except KeyError:
|
||||
return default
|
||||
|
||||
def get_tables( self ):
|
||||
return self.data_tables
|
||||
|
||||
def load_from_config_file( self, config_filename, tool_data_path, from_shed_config=False ):
|
||||
"""
|
||||
This method is called under 3 conditions:
|
||||
|
||||
@@ -72,13 +72,13 @@ class ToolParameter( object, Dictifiable ):
|
||||
"""
|
||||
return value
|
||||
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
"""
|
||||
Return the starting value of the parameter
|
||||
"""
|
||||
return None
|
||||
|
||||
def get_initial_value_from_history_prevent_repeats( self, trans, context, already_used ):
|
||||
def get_initial_value_from_history_prevent_repeats( self, trans, context, already_used, history=None ):
|
||||
"""
|
||||
Get the starting value for the parameter, but if fetching from the history, try
|
||||
to find a value that has not yet been used. already_used is a list of objects that
|
||||
@@ -86,7 +86,7 @@ class ToolParameter( object, Dictifiable ):
|
||||
if a value has already been chosen from the history. This is to support the capability to
|
||||
choose each dataset once
|
||||
"""
|
||||
return self.get_initial_value(trans, context);
|
||||
return self.get_initial_value(trans, context, history=history);
|
||||
|
||||
def get_required_enctype( self ):
|
||||
"""
|
||||
@@ -216,7 +216,7 @@ class TextToolParameter( ToolParameter ):
|
||||
return form_builder.TextArea( self.name, self.size, value )
|
||||
else:
|
||||
return form_builder.TextField( self.name, self.size, value )
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
return self.value
|
||||
|
||||
class IntegerToolParameter( TextToolParameter ):
|
||||
@@ -286,7 +286,7 @@ class IntegerToolParameter( TextToolParameter ):
|
||||
if not value and self.optional:
|
||||
return None
|
||||
raise err
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
if self.value:
|
||||
return int( self.value )
|
||||
else:
|
||||
@@ -358,7 +358,7 @@ class FloatToolParameter( TextToolParameter ):
|
||||
if not value and self.optional:
|
||||
return None
|
||||
raise err
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
try:
|
||||
return float( self.value )
|
||||
except:
|
||||
@@ -401,7 +401,7 @@ class BooleanToolParameter( ToolParameter ):
|
||||
return [ 'true' ]
|
||||
def to_python( self, value, app ):
|
||||
return ( value == 'True' )
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
return self.checked
|
||||
def to_param_dict_string( self, value, other_values={} ):
|
||||
if value:
|
||||
@@ -474,7 +474,7 @@ class FileToolParameter( ToolParameter ):
|
||||
return value
|
||||
else:
|
||||
raise Exception( "FileToolParameter cannot be persisted" )
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
return None
|
||||
|
||||
class FTPFileToolParameter( ToolParameter ):
|
||||
@@ -513,7 +513,7 @@ class FTPFileToolParameter( ToolParameter ):
|
||||
return None
|
||||
elif isinstance( value, unicode ) or isinstance( value, str ) or isinstance( value, list ):
|
||||
return value
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
return None
|
||||
|
||||
class HiddenToolParameter( ToolParameter ):
|
||||
@@ -534,7 +534,7 @@ class HiddenToolParameter( ToolParameter ):
|
||||
self.value = elem.get( 'value' )
|
||||
def get_html_field( self, trans=None, value=None, other_values={} ):
|
||||
return form_builder.HiddenField( self.name, self.value )
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
return self.value
|
||||
def get_label( self ):
|
||||
return None
|
||||
@@ -557,7 +557,7 @@ class BaseURLToolParameter( ToolParameter ):
|
||||
return url
|
||||
def get_html_field( self, trans=None, value=None, other_values={} ):
|
||||
return form_builder.HiddenField( self.name, self.get_value( trans ) )
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
return self.value
|
||||
def get_label( self ):
|
||||
# BaseURLToolParameters are ultimately "hidden" parameters
|
||||
@@ -826,7 +826,7 @@ class SelectToolParameter( ToolParameter ):
|
||||
return True
|
||||
# Dynamic, but all dependenceis are known and have values
|
||||
return False
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
# More working around dynamic options for workflow
|
||||
if self.need_late_validation( trans, context ):
|
||||
# Really the best we can do?
|
||||
@@ -1074,7 +1074,7 @@ class ColumnListParameter( SelectToolParameter ):
|
||||
options.append( ( 'c' + col, col, False ) )
|
||||
return options
|
||||
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
if self.default_value is not None:
|
||||
# dataset not ready / in workflow / etc
|
||||
if self.need_late_validation( trans, context ):
|
||||
@@ -1353,8 +1353,7 @@ class DrillDownSelectToolParameter( SelectToolParameter ):
|
||||
else:
|
||||
rval = sanitize_param( rval )
|
||||
return rval
|
||||
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
def recurse_options( initial_values, options ):
|
||||
for option in options:
|
||||
if option['selected']:
|
||||
@@ -1542,10 +1541,10 @@ class DataToolParameter( ToolParameter ):
|
||||
field.add_option( "Selection is Optional", 'None', False )
|
||||
return field
|
||||
|
||||
def get_initial_value( self, trans, context ):
|
||||
return self.get_initial_value_from_history_prevent_repeats(trans, context, None);
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
return self.get_initial_value_from_history_prevent_repeats(trans, context, None, history=history);
|
||||
|
||||
def get_initial_value_from_history_prevent_repeats( self, trans, context, already_used ):
|
||||
def get_initial_value_from_history_prevent_repeats( self, trans, context, already_used, history=None ):
|
||||
"""
|
||||
NOTE: This is wasteful since dynamic options and dataset collection
|
||||
happens twice (here and when generating HTML).
|
||||
@@ -1554,7 +1553,8 @@ class DataToolParameter( ToolParameter ):
|
||||
if trans is None or trans.workflow_building_mode or trans.webapp.name == 'tool_shed':
|
||||
return DummyDataset()
|
||||
assert trans is not None, "DataToolParameter requires a trans"
|
||||
history = trans.get_history()
|
||||
if history is None:
|
||||
history = trans.get_history()
|
||||
assert history is not None, "DataToolParameter requires a history"
|
||||
if self.optional:
|
||||
return None
|
||||
@@ -1613,6 +1613,9 @@ class DataToolParameter( ToolParameter ):
|
||||
rval = [ trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( v ) for v in value ]
|
||||
elif isinstance( value, trans.app.model.HistoryDatasetAssociation ):
|
||||
rval = value
|
||||
elif isinstance( value, dict ) and 'src' in value and 'id' in value:
|
||||
if value['src'] == 'hda':
|
||||
rval = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.app.security.decode_id(value['id']) )
|
||||
else:
|
||||
rval = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( value )
|
||||
if isinstance( rval, list ):
|
||||
@@ -1727,7 +1730,7 @@ class HiddenDataToolParameter( HiddenToolParameter, DataToolParameter ):
|
||||
self.value = "None"
|
||||
self.type = "hidden_data"
|
||||
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
return None
|
||||
|
||||
def get_html_field( self, trans=None, value=None, other_values={} ):
|
||||
|
||||
@@ -41,8 +41,7 @@ class Group( object, Dictifiable ):
|
||||
into the preferred value form.
|
||||
"""
|
||||
return value
|
||||
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
"""
|
||||
Return the initial state/value for this group
|
||||
"""
|
||||
@@ -109,7 +108,7 @@ class Repeat( Group ):
|
||||
callback( new_prefix, input, d[input.name], parent = d )
|
||||
else:
|
||||
input.visit_inputs( new_prefix, d[input.name], callback )
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
rval = []
|
||||
for i in range( self.default ):
|
||||
rval_dict = { '__index__': i}
|
||||
@@ -202,14 +201,14 @@ class UploadDataset( Group ):
|
||||
callback( new_prefix, input, d[input.name], parent = d )
|
||||
else:
|
||||
input.visit_inputs( new_prefix, d[input.name], callback )
|
||||
def get_initial_value( self, trans, context ):
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
d_type = self.get_datatype( trans, context )
|
||||
rval = []
|
||||
for i, ( composite_name, composite_file ) in enumerate( d_type.writable_files.iteritems() ):
|
||||
rval_dict = {}
|
||||
rval_dict['__index__'] = i # create __index__
|
||||
for input in self.inputs.itervalues():
|
||||
rval_dict[ input.name ] = input.get_initial_value( trans, context ) #input.value_to_basic( d[input.name], app )
|
||||
rval_dict[ input.name ] = input.get_initial_value( trans, context, history=history ) #input.value_to_basic( d[input.name], app )
|
||||
rval.append( rval_dict )
|
||||
return rval
|
||||
def get_uploaded_datasets( self, trans, context, override_name = None, override_info = None ):
|
||||
@@ -489,12 +488,12 @@ class Conditional( Group ):
|
||||
callback( prefix, input, value[input.name], parent = value )
|
||||
else:
|
||||
input.visit_inputs( prefix, value[input.name], callback )
|
||||
def get_initial_value( self, trans, context ):
|
||||
# State for a conditional is a plain dictionary.
|
||||
def get_initial_value( self, trans, context, history=None ):
|
||||
# State for a conditional is a plain dictionary.
|
||||
rval = {}
|
||||
# Get the default value for the 'test element' and use it
|
||||
# to determine the current case
|
||||
test_value = self.test_param.get_initial_value( trans, context )
|
||||
test_value = self.test_param.get_initial_value( trans, context, history=None )
|
||||
current_case = self.get_current_case( test_value, trans )
|
||||
# Store the current case in a special value
|
||||
rval['__current_case__'] = current_case
|
||||
@@ -503,7 +502,7 @@ class Conditional( Group ):
|
||||
# Fill in state for selected case
|
||||
child_context = ExpressionContext( rval, context )
|
||||
for child_input in self.cases[current_case].inputs.itervalues():
|
||||
rval[ child_input.name ] = child_input.get_initial_value( trans, child_context )
|
||||
rval[ child_input.name ] = child_input.get_initial_value( trans, child_context, history=None )
|
||||
return rval
|
||||
|
||||
class ConditionalWhen( object ):
|
||||
|
||||
@@ -65,7 +65,7 @@ class ValueToolOutputActionConditionalWhen( ToolOutputActionConditionalWhen ):
|
||||
def is_case( self, output_dataset, other_values ):
|
||||
ref = self.get_ref( output_dataset, other_values )
|
||||
return bool( str( ref ) == self.value )
|
||||
|
||||
|
||||
class DatatypeIsInstanceToolOutputActionConditionalWhen( ToolOutputActionConditionalWhen ):
|
||||
tag = "when datatype_isinstance"
|
||||
def __init__( self, parent, config_elem, value ):
|
||||
@@ -215,11 +215,12 @@ class FromDataTableOutputActionOption( ToolOutputActionOption ):
|
||||
self.offset = elem.get( 'offset', -1 )
|
||||
self.offset = int( self.offset )
|
||||
else:
|
||||
self.options = []
|
||||
self.missing_tool_data_table_name = self.name
|
||||
def get_value( self, other_values ):
|
||||
try:
|
||||
if self.options:
|
||||
options = self.options
|
||||
except:
|
||||
else:
|
||||
options = []
|
||||
for filter in self.filters:
|
||||
options = filter.filter_options( options, other_values )
|
||||
@@ -248,7 +249,7 @@ class FormatToolOutputAction( ToolOutputAction ):
|
||||
def __init__( self, parent, elem ):
|
||||
super( FormatToolOutputAction, self ).__init__( parent, elem )
|
||||
self.default = elem.get( 'default', None )
|
||||
|
||||
|
||||
def apply_action( self, output_dataset, other_values ):
|
||||
value = self.option.get_value( other_values )
|
||||
if value is None and self.default is not None:
|
||||
@@ -431,7 +432,7 @@ class MetadataValueFilter( ToolOutputActionOptionFilter ):
|
||||
value = str( getattr( ref.metadata, self.name ) )
|
||||
rval = []
|
||||
for fields in options:
|
||||
if self.keep == ( self.compare( fields[self.column], value ) ):
|
||||
if self.keep == ( self.compare( fields[self.column], value ) ):
|
||||
rval.append( fields )
|
||||
return rval
|
||||
|
||||
@@ -452,7 +453,7 @@ class BooleanFilter( ToolOutputActionOptionFilter ):
|
||||
value = self.cast( value )
|
||||
except:
|
||||
value = False #unable to cast or access value; treat as false
|
||||
if self.keep == bool( value ):
|
||||
if self.keep == bool( value ):
|
||||
rval.append( fields )
|
||||
return rval
|
||||
|
||||
@@ -480,7 +481,7 @@ for action_type in [ MetadataToolOutputAction, FormatToolOutputAction ]:
|
||||
option_types = {}
|
||||
for option_type in [ NullToolOutputActionOption, FromFileToolOutputActionOption, FromParamToolOutputActionOption, FromDataTableOutputActionOption ]:
|
||||
option_types[ option_type.tag ] = option_type
|
||||
|
||||
|
||||
filter_types = {}
|
||||
for filter_type in [ ParamValueToolOutputActionOptionFilter, InsertColumnToolOutputActionOptionFilter, MultipleSplitterFilter, ColumnStripFilter, MetadataValueFilter, BooleanFilter, StringFunctionFilter, ColumnReplaceFilter ]:
|
||||
filter_types[ filter_type.tag ] = filter_type
|
||||
@@ -524,5 +525,5 @@ def compare_endswith( value1, value2 ):
|
||||
def compare_re_search( value1, value2 ):
|
||||
#checks pattern=value2 in value1
|
||||
return bool( re.search( value2, value1 ) )
|
||||
|
||||
|
||||
compare_types = { 'eq':compare_eq, 'neq':compare_neq, 'gt':compare_gt, 'gte':compare_gte, 'lt':compare_lt, 'lte':compare_lte, 'in':compare_in, 'startswith':compare_startswith, 'endswith':compare_endswith, "re_search":compare_re_search }
|
||||
|
||||
@@ -690,33 +690,38 @@ class VcfDataProvider( GenomeDataProvider ):
|
||||
allele_counts = [ 0 for i in range ( alt.count( ',' ) + 1 ) ]
|
||||
sample_gts = []
|
||||
|
||||
# Process and pack samples' genotype and count alleles across samples.
|
||||
alleles_seen = {}
|
||||
has_alleles = False
|
||||
|
||||
for i, sample in enumerate( samples_data ):
|
||||
# Parse and count alleles.
|
||||
genotype = sample.split( ':' )[ 0 ]
|
||||
if samples_data:
|
||||
# Process and pack samples' genotype and count alleles across samples.
|
||||
alleles_seen = {}
|
||||
has_alleles = False
|
||||
alleles_seen.clear()
|
||||
for allele in genotype_re.split( genotype ):
|
||||
try:
|
||||
# This may throw a ValueError if allele is missing.
|
||||
allele = int( allele )
|
||||
|
||||
# Only count allele if it hasn't been seen yet.
|
||||
if allele != 0 and allele not in alleles_seen:
|
||||
allele_counts[ allele - 1 ] += 1
|
||||
alleles_seen[ allele ] = True
|
||||
has_alleles = True
|
||||
except ValueError:
|
||||
pass
|
||||
for i, sample in enumerate( samples_data ):
|
||||
# Parse and count alleles.
|
||||
genotype = sample.split( ':' )[ 0 ]
|
||||
has_alleles = False
|
||||
alleles_seen.clear()
|
||||
for allele in genotype_re.split( genotype ):
|
||||
try:
|
||||
# This may throw a ValueError if allele is missing.
|
||||
allele = int( allele )
|
||||
|
||||
# If no alleles, use empty string as proxy.
|
||||
if not has_alleles:
|
||||
genotype = ''
|
||||
# Only count allele if it hasn't been seen yet.
|
||||
if allele != 0 and allele not in alleles_seen:
|
||||
allele_counts[ allele - 1 ] += 1
|
||||
alleles_seen[ allele ] = True
|
||||
has_alleles = True
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
sample_gts.append( genotype )
|
||||
# If no alleles, use empty string as proxy.
|
||||
if not has_alleles:
|
||||
genotype = ''
|
||||
|
||||
sample_gts.append( genotype )
|
||||
else:
|
||||
# No samples, so set allele count and sample genotype manually.
|
||||
allele_counts = [ 1 ]
|
||||
sample_gts = [ '1/1' ]
|
||||
|
||||
# Add locus data.
|
||||
locus_data = [
|
||||
|
||||
@@ -39,9 +39,10 @@ user_pref for ordering/ex/inclusion of particular visualizations
|
||||
"""
|
||||
|
||||
# ------------------------------------------------------------------- the registry
|
||||
class VisualizationsRegistry( pluginframework.PluginFramework ):
|
||||
class VisualizationsRegistry( pluginframework.PageServingPluginManager ):
|
||||
"""
|
||||
Main responsibilities are:
|
||||
- discovering visualization plugins in the filesystem
|
||||
- testing if an object has a visualization that can be applied to it
|
||||
- generating a link to controllers.visualization.render with
|
||||
the appropriate params
|
||||
@@ -58,31 +59,81 @@ class VisualizationsRegistry( pluginframework.PluginFramework ):
|
||||
'sweepster',
|
||||
'phyloviz'
|
||||
]
|
||||
#: directories under plugin_directory that aren't plugins
|
||||
non_plugin_directories = []
|
||||
|
||||
def __init__( self, registry_filepath, template_cache_dir ):
|
||||
super( VisualizationsRegistry, self ).__init__( registry_filepath, 'visualizations', template_cache_dir )
|
||||
def __str__( self ):
|
||||
return self.__class__.__name__
|
||||
|
||||
def __init__( self, app, **kwargs ):
|
||||
self.config_parser = VisualizationsConfigParser()
|
||||
super( VisualizationsRegistry, self ).__init__( app, 'visualizations', **kwargs )
|
||||
# what to use to parse query strings into resources/vars for the template
|
||||
self.resource_parser = ResourceParser()
|
||||
log.debug( '%s loaded', str( self ) )
|
||||
|
||||
def load_configuration( self ):
|
||||
def is_plugin( self, plugin_path ):
|
||||
"""
|
||||
Builds the registry by parsing the `config/*.xml` files for every plugin
|
||||
in ``get_plugin_directories`` and stores the results in ``self.listings``.
|
||||
Determines whether the given filesystem path contains a plugin.
|
||||
|
||||
..note::
|
||||
This could be used to re-load a new configuration without restarting
|
||||
the instance.
|
||||
In this base class, all sub-directories are considered plugins.
|
||||
|
||||
:type plugin_path: string
|
||||
:param plugin_path: relative or absolute filesystem path to the
|
||||
potential plugin
|
||||
:rtype: bool
|
||||
:returns: True if the path contains a plugin
|
||||
"""
|
||||
try:
|
||||
self.listings = VisualizationsConfigParser.parse( self.get_plugin_directories() )
|
||||
# plugin_path must be a directory, have a config dir, and a config file matching the plugin dir name
|
||||
if not os.path.isdir( plugin_path ):
|
||||
# super won't work here - different criteria
|
||||
return False
|
||||
if not 'config' in os.listdir( plugin_path ):
|
||||
return False
|
||||
expected_config_filename = '%s.xml' %( os.path.split( plugin_path )[1] )
|
||||
if not os.path.isfile( os.path.join( plugin_path, 'config', expected_config_filename ) ):
|
||||
return False
|
||||
return True
|
||||
|
||||
except Exception, exc:
|
||||
log.exception( 'Error parsing visualizations plugins %s', self.plugin_directories )
|
||||
raise
|
||||
def load_plugin( self, plugin_path ):
|
||||
"""
|
||||
Create the visualization plugin object, parse its configuration file,
|
||||
and return it.
|
||||
|
||||
Plugin bunches are decorated with:
|
||||
* config_file : the path to this visualization's config file
|
||||
* config : the parsed configuration for this visualization
|
||||
|
||||
:type plugin_path: string
|
||||
:param plugin_path: relative or absolute filesystem path to the plugin
|
||||
:rtype: ``util.bunch.Bunch``
|
||||
:returns: the loaded plugin object
|
||||
"""
|
||||
#TODO: possibly move this after the config parsing to allow config to override?
|
||||
plugin = super( VisualizationsRegistry, self ).load_plugin( plugin_path )
|
||||
|
||||
# config file is required, otherwise skip this visualization
|
||||
plugin[ 'config_file' ] = os.path.join( plugin_path, 'config', ( plugin.name + '.xml' ) )
|
||||
config = self.config_parser.parse_file( plugin.config_file )
|
||||
|
||||
if not config:
|
||||
return None
|
||||
plugin[ 'config' ] = config
|
||||
|
||||
return plugin
|
||||
|
||||
# -- getting resources for visualization templates from link query strings --
|
||||
# -- building links to visualizations from objects --
|
||||
def get_visualizations( self, trans, target_object ):
|
||||
"""
|
||||
Get the names of visualizations usable on the `target_object` and
|
||||
the urls to call in order to render the visualizations.
|
||||
"""
|
||||
#TODO:?? a list of objects? YAGNI?
|
||||
applicable_visualizations = []
|
||||
for vis_name in self.plugins:
|
||||
url_data = self.get_visualization( trans, vis_name, target_object )
|
||||
if url_data:
|
||||
applicable_visualizations.append( url_data )
|
||||
return applicable_visualizations
|
||||
|
||||
def get_visualization( self, trans, visualization_name, target_object ):
|
||||
"""
|
||||
@@ -90,12 +141,11 @@ class VisualizationsRegistry( pluginframework.PluginFramework ):
|
||||
`visualization_name` if it's applicable to `target_object` or
|
||||
`None` if it's not.
|
||||
"""
|
||||
# a little weird to pass trans because this registry is part of the trans.app
|
||||
listing_data = self.listings.get( visualization_name, None )
|
||||
if not listing_data:
|
||||
visualization = self.plugins.get( visualization_name, None )
|
||||
if not visualization:
|
||||
return None
|
||||
|
||||
data_sources = listing_data[ 'data_sources' ]
|
||||
data_sources = visualization.config[ 'data_sources' ]
|
||||
for data_source in data_sources:
|
||||
# currently a model class is required
|
||||
model_class = data_source[ 'model_class' ]
|
||||
@@ -109,11 +159,11 @@ class VisualizationsRegistry( pluginframework.PluginFramework ):
|
||||
|
||||
param_data = data_source[ 'to_params' ]
|
||||
url = self.get_visualization_url( trans, target_object, visualization_name, param_data )
|
||||
link_text = listing_data.get( 'link_text', None )
|
||||
link_text = visualization.config.get( 'link_text', None )
|
||||
if not link_text:
|
||||
# default to visualization name, titlecase, and replace underscores
|
||||
link_text = visualization_name.title().replace( '_', ' ' )
|
||||
render_location = listing_data.get( 'render_location' )
|
||||
render_location = visualization.config.get( 'render_location' )
|
||||
# remap some of these vars for direct use in ui.js, PopupMenu (e.g. text->html)
|
||||
return {
|
||||
'href' : url,
|
||||
@@ -123,25 +173,12 @@ class VisualizationsRegistry( pluginframework.PluginFramework ):
|
||||
|
||||
return None
|
||||
|
||||
# -- building links to visualizations from objects --
|
||||
def get_visualizations( self, trans, target_object ):
|
||||
"""
|
||||
Get the names of visualizations usable on the `target_object` and
|
||||
the urls to call in order to render the visualizations.
|
||||
"""
|
||||
#TODO:?? a list of objects? YAGNI?
|
||||
applicable_visualizations = []
|
||||
for vis_name in self.listings:
|
||||
url_data = self.get_visualization( trans, vis_name, target_object )
|
||||
if url_data:
|
||||
applicable_visualizations.append( url_data )
|
||||
return applicable_visualizations
|
||||
|
||||
def is_object_applicable( self, trans, target_object, data_source_tests ):
|
||||
"""
|
||||
Run a visualization's data_source tests to find out if
|
||||
it be applied to the target_object.
|
||||
"""
|
||||
#log.debug( 'is_object_applicable( self, trans, %s, %s )', target_object, data_source_tests )
|
||||
for test in data_source_tests:
|
||||
test_type = test[ 'type' ]
|
||||
result_type = test[ 'result_type' ]
|
||||
@@ -164,7 +201,6 @@ class VisualizationsRegistry( pluginframework.PluginFramework ):
|
||||
if test_fn( target_object, test_result ):
|
||||
#log.debug( 'test passed' )
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_visualization_url( self, trans, target_object, visualization_name, param_data ):
|
||||
@@ -219,9 +255,9 @@ class VisualizationsRegistry( pluginframework.PluginFramework ):
|
||||
|
||||
Both `params` and `param_modifiers` default to an empty dictionary.
|
||||
"""
|
||||
visualization = self.listings.get( visualization_name )
|
||||
expected_params = visualization.get( 'params', {} )
|
||||
param_modifiers = visualization.get( 'param_modifiers', {} )
|
||||
visualization = self.plugins.get( visualization_name )
|
||||
expected_params = visualization.config.get( 'params', {} )
|
||||
param_modifiers = visualization.config.get( 'param_modifiers', {} )
|
||||
return ( expected_params, param_modifiers )
|
||||
|
||||
def query_dict_to_resources( self, trans, controller, visualization_name, query_dict ):
|
||||
@@ -259,13 +295,6 @@ class VisualizationsConfigParser( object ):
|
||||
"""
|
||||
VALID_RENDER_LOCATIONS = [ 'galaxy_main', '_top', '_blank' ]
|
||||
|
||||
@classmethod
|
||||
def parse( cls, plugin_directories, debug=False ):
|
||||
"""
|
||||
Static class interface.
|
||||
"""
|
||||
return cls( debug ).parse_plugins( plugin_directories )
|
||||
|
||||
def __init__( self, debug=False ):
|
||||
self.debug = debug
|
||||
|
||||
@@ -274,58 +303,19 @@ class VisualizationsConfigParser( object ):
|
||||
self.param_parser = ParamParser()
|
||||
self.param_modifier_parser = ParamModifierParser()
|
||||
|
||||
def parse_plugins( self, plugin_directories ):
|
||||
"""
|
||||
Parses the config files for each plugin sub-dir in `base_path`.
|
||||
|
||||
:param plugin_directories: a list of paths to enabled plugins.
|
||||
|
||||
:returns: registry data in dictionary form
|
||||
"""
|
||||
returned = {}
|
||||
for plugin_path in plugin_directories:
|
||||
returned.update( self.parse_plugin( plugin_path ) )
|
||||
return returned
|
||||
|
||||
def parse_plugin( self, plugin_path ):
|
||||
"""
|
||||
Parses any XML files in ``<plugin_path>/config``.
|
||||
|
||||
If an error occurs while parsing a visualizations entry, it is skipped.
|
||||
:returns: registry data in dictionary form
|
||||
..note::
|
||||
assumes config files are in a 'config' sub-dir of each plugin
|
||||
"""
|
||||
returned = {}
|
||||
|
||||
plugin_config_path = os.path.join( plugin_path, 'config' )
|
||||
if not os.path.isdir( plugin_config_path ):
|
||||
return returned
|
||||
|
||||
for xml_filepath in glob.glob( os.path.join( plugin_config_path, '*.xml' ) ):
|
||||
try:
|
||||
visualization_name, visualization = self.parse_file( xml_filepath )
|
||||
# skip vis' with parsing errors - don't shutdown the startup
|
||||
except ParsingException, parse_exc:
|
||||
log.error( 'Skipped visualization config "%s" due to parsing errors: %s',
|
||||
xml_filepath, str( parse_exc ), exc_info=self.debug )
|
||||
|
||||
if visualization:
|
||||
returned[ visualization_name ] = visualization
|
||||
log.debug( 'Visualization config loaded for: %s', visualization_name )
|
||||
|
||||
return returned
|
||||
|
||||
def parse_file( self, xml_filepath ):
|
||||
"""
|
||||
Parse the given XML file for visualizations data.
|
||||
:returns: tuple of ( `visualization_name`, `visualization` )
|
||||
"""
|
||||
xml_tree = galaxy.util.parse_xml( xml_filepath )
|
||||
visualization_conf = xml_tree.getroot()
|
||||
visualization_name = visualization_conf.get( 'name' )
|
||||
visualization = self.parse_visualization( visualization_conf )
|
||||
return visualization_name, visualization
|
||||
try:
|
||||
xml_tree = galaxy.util.parse_xml( xml_filepath )
|
||||
visualization = self.parse_visualization( xml_tree.getroot() )
|
||||
return visualization
|
||||
# skip vis' with parsing errors - don't shutdown the startup
|
||||
except ParsingException, parse_exc:
|
||||
log.exception( 'Skipped visualization config "%s" due to parsing errors', xml_filepath )
|
||||
return None
|
||||
|
||||
def parse_visualization( self, xml_tree ):
|
||||
"""
|
||||
|
||||
@@ -24,6 +24,7 @@ from galaxy import web
|
||||
from galaxy import model
|
||||
from galaxy import security
|
||||
from galaxy import util
|
||||
from galaxy import objectstore
|
||||
|
||||
from galaxy.web import error, url_for
|
||||
from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, TextArea, TextField
|
||||
@@ -583,12 +584,15 @@ class UsesHistoryDatasetAssociationMixin:
|
||||
"""
|
||||
#precondition: the user's access to this hda has already been checked
|
||||
#TODO:?? postcondition: all ids are encoded (is this really what we want at this level?)
|
||||
hda_dict = hda.to_dict( view='element' )
|
||||
expose_dataset_path = trans.user_is_admin() or trans.app.config.expose_dataset_path
|
||||
hda_dict = hda.to_dict( view='element', expose_dataset_path=expose_dataset_path )
|
||||
hda_dict[ 'api_type' ] = "file"
|
||||
|
||||
# Add additional attributes that depend on trans can hence must be added here rather than at the model level.
|
||||
|
||||
#NOTE: access is an expensive operation - removing it and adding the precondition of access is already checked
|
||||
can_access_hda = trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), hda.dataset )
|
||||
can_access_hda = ( trans.user_is_admin() or can_access_hda )
|
||||
if not can_access_hda:
|
||||
return self.get_inaccessible_hda_dict( trans, hda )
|
||||
hda_dict[ 'accessible' ] = True
|
||||
|
||||
# ---- return here if deleted AND purged OR can't access
|
||||
@@ -597,8 +601,11 @@ class UsesHistoryDatasetAssociationMixin:
|
||||
#TODO: to_dict should really go AFTER this - only summary data
|
||||
return trans.security.encode_dict_ids( hda_dict )
|
||||
|
||||
if trans.user_is_admin() or trans.app.config.expose_dataset_path:
|
||||
hda_dict[ 'file_name' ] = hda.file_name
|
||||
if expose_dataset_path:
|
||||
try:
|
||||
hda_dict[ 'file_name' ] = hda.file_name
|
||||
except objectstore.ObjectNotFound, onf:
|
||||
log.exception( 'objectstore.ObjectNotFound, HDA %s: %s', hda.id, onf )
|
||||
|
||||
hda_dict[ 'download_url' ] = url_for( 'history_contents_display',
|
||||
history_id = trans.security.encode_id( hda.history.id ),
|
||||
@@ -634,6 +641,18 @@ class UsesHistoryDatasetAssociationMixin:
|
||||
|
||||
return trans.security.encode_dict_ids( hda_dict )
|
||||
|
||||
def get_inaccessible_hda_dict( self, trans, hda ):
|
||||
return trans.security.encode_dict_ids({
|
||||
'id' : hda.id,
|
||||
'history_id': hda.history.id,
|
||||
'hid' : hda.hid,
|
||||
'name' : hda.name,
|
||||
'state' : hda.state,
|
||||
'deleted' : hda.deleted,
|
||||
'visible' : hda.visible,
|
||||
'accessible': False
|
||||
})
|
||||
|
||||
def get_hda_dict_with_error( self, trans, hda, error_msg='' ):
|
||||
return trans.security.encode_dict_ids({
|
||||
'id' : hda.id,
|
||||
@@ -1063,7 +1082,6 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin, UsesLibraryMix
|
||||
return {
|
||||
"dataset_id": trans.security.decode_id( dataset_dict['id'] ),
|
||||
"hda_ldda": dataset_dict.get('hda_ldda', 'hda'),
|
||||
"name": track_dict['name'],
|
||||
"track_type": track_dict['track_type'],
|
||||
"prefs": track_dict['prefs'],
|
||||
"mode": track_dict['mode'],
|
||||
@@ -1082,7 +1100,6 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin, UsesLibraryMix
|
||||
drawable = unpack_collection( drawable_json )
|
||||
unpacked_drawables.append( drawable )
|
||||
return {
|
||||
"name": collection_json.get( 'name', '' ),
|
||||
"obj_type": collection_json[ 'obj_type' ],
|
||||
"drawables": unpacked_drawables,
|
||||
"prefs": collection_json.get( 'prefs' , [] ),
|
||||
@@ -1173,7 +1190,6 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin, UsesLibraryMix
|
||||
return {
|
||||
"track_type": dataset.datatype.track_type,
|
||||
"dataset": trans.security.encode_dict_ids( dataset.to_dict() ),
|
||||
"name": track_dict['name'],
|
||||
"prefs": prefs,
|
||||
"mode": track_dict.get( 'mode', 'Auto' ),
|
||||
"filters": track_dict.get( 'filters', { 'filters' : track_data_provider.get_filters() } ),
|
||||
@@ -1189,7 +1205,6 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin, UsesLibraryMix
|
||||
else:
|
||||
drawables.append( pack_collection( drawable_dict ) )
|
||||
return {
|
||||
'name': collection_dict.get( 'name', 'dummy' ),
|
||||
'obj_type': collection_dict[ 'obj_type' ],
|
||||
'drawables': drawables,
|
||||
'prefs': collection_dict.get( 'prefs', [] ),
|
||||
|
||||
@@ -1,38 +1,352 @@
|
||||
"""
|
||||
Base class for plugins - frameworks or systems that may:
|
||||
* add code at startup
|
||||
* allow hooks to be called
|
||||
and base class for plugins that:
|
||||
* serve static content
|
||||
* serve templated html
|
||||
* have some configuration at startup
|
||||
"""
|
||||
|
||||
import os.path
|
||||
import glob
|
||||
import sys
|
||||
import imp
|
||||
|
||||
import pkg_resources
|
||||
pkg_resources.require( 'MarkupSafe' )
|
||||
pkg_resources.require( 'Mako' )
|
||||
import mako
|
||||
|
||||
from galaxy.util import listify
|
||||
from galaxy import util
|
||||
from galaxy.util import odict
|
||||
from galaxy.util import bunch
|
||||
|
||||
import logging
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
# ============================================================================= exceptions
|
||||
class PluginFrameworkException( Exception ):
|
||||
class PluginManagerException( Exception ):
|
||||
"""Base exception for plugin frameworks.
|
||||
"""
|
||||
pass
|
||||
class PluginFrameworkConfigException( PluginFrameworkException ):
|
||||
class PluginManagerConfigException( PluginManagerException ):
|
||||
"""Exception for plugin framework configuration errors.
|
||||
"""
|
||||
pass
|
||||
class PluginFrameworkStaticException( PluginFrameworkException ):
|
||||
|
||||
|
||||
# ============================================================================= base
|
||||
class PluginManager( object ):
|
||||
"""
|
||||
Plugins represents an section of code that is not tracked in the
|
||||
Galaxy repository, allowing the addition of custom code to a Galaxy
|
||||
installation without changing the code base.
|
||||
|
||||
A PluginManager discovers and manages these plugins.
|
||||
|
||||
This is an non-abstract class but it's usefulness is limited and is meant
|
||||
to be inherited.
|
||||
"""
|
||||
|
||||
def __init__( self, app, directories_setting=None, skip_bad_plugins=True, **kwargs ):
|
||||
"""
|
||||
Set up the manager and load all plugins.
|
||||
|
||||
:type app: UniverseApplication
|
||||
:param app: the application (and it's configuration) using this manager
|
||||
:type directories_setting: string (default: None)
|
||||
:param directories_setting: the filesystem path (or paths)
|
||||
to search for plugins. Can be CSV string of paths. Will be treated as
|
||||
absolute if a path starts with '/', relative otherwise.
|
||||
:type skip_bad_plugins: boolean (default: True)
|
||||
:param skip_bad_plugins: whether to skip plugins that cause
|
||||
exceptions when loaded or to raise that exception
|
||||
"""
|
||||
log.debug( 'PluginManager.init: %s, %s', directories_setting, kwargs )
|
||||
self.directories = []
|
||||
self.skip_bad_plugins = skip_bad_plugins
|
||||
self.plugins = odict.odict()
|
||||
|
||||
self.directories = self.parse_directories_setting( app.config.root, directories_setting )
|
||||
#log.debug( '\t directories: %s', self.directories )
|
||||
|
||||
self.load_configuration()
|
||||
self.load_plugins()
|
||||
|
||||
def parse_directories_setting( self, galaxy_root, directories_setting ):
|
||||
"""
|
||||
Parse the ``directories_setting`` into a list of relative or absolute
|
||||
filesystem paths that will be searched to discover plugins.
|
||||
|
||||
:type galaxy_root: string
|
||||
:param galaxy_root: the root path of this galaxy installation
|
||||
:type directories_setting: string (default: None)
|
||||
:param directories_setting: the filesystem path (or paths)
|
||||
to search for plugins. Can be CSV string of paths. Will be treated as
|
||||
absolute if a path starts with '/', relative otherwise.
|
||||
:rtype: list of strings
|
||||
:returns: list of filesystem paths
|
||||
"""
|
||||
directories = []
|
||||
if not directories_setting:
|
||||
return directories
|
||||
|
||||
for directory in util.listify( directories_setting ):
|
||||
directory = directory.strip()
|
||||
if not directory.startswith( '/' ):
|
||||
directory = os.path.join( galaxy_root, directory )
|
||||
if not os.path.exists( directory ):
|
||||
log.warn( '%s, directory not found: %s', self, directory )
|
||||
continue
|
||||
directories.append( directory )
|
||||
return directories
|
||||
|
||||
def load_configuration( self ):
|
||||
"""
|
||||
Override to load some framework/plugin specifc configuration.
|
||||
"""
|
||||
# Abstract method
|
||||
return True
|
||||
|
||||
def load_plugins( self ):
|
||||
"""
|
||||
Search ``self.directories`` for potential plugins, load them, and cache
|
||||
in ``self.plugins``.
|
||||
:rtype: odict
|
||||
:returns: ``self.plugins``
|
||||
"""
|
||||
for plugin_path in self.find_plugins():
|
||||
try:
|
||||
plugin = self.load_plugin( plugin_path )
|
||||
if not plugin:
|
||||
log.warn( '%s, plugin load failed: %s. Skipping...', self, plugin_path )
|
||||
#NOTE: prevent silent, implicit overwrite here (two plugins in two diff directories)
|
||||
#TODO: overwriting may be desired
|
||||
elif plugin.name in self.plugins:
|
||||
log.warn( '%s, plugin with name already exists: %s. Skipping...', self, plugin.name )
|
||||
else:
|
||||
self.plugins[ plugin.name ] = plugin
|
||||
log.info( '%s, loaded plugin: %s', self, plugin.name )
|
||||
|
||||
except Exception, exc:
|
||||
if not self.skip_bad_plugins:
|
||||
raise
|
||||
log.exception( 'Plugin loading raised exception: %s. Skipping...', plugin_path )
|
||||
|
||||
return self.plugins
|
||||
|
||||
def find_plugins( self ):
|
||||
"""
|
||||
Return the directory paths of plugins within ``self.directories``.
|
||||
|
||||
Paths are considered a plugin path if they pass ``self.is_plugin``.
|
||||
:rtype: string generator
|
||||
:returns: paths of valid plugins
|
||||
"""
|
||||
# due to the ordering of listdir, there is an implicit plugin loading order here
|
||||
# could instead explicitly list on/off in master config file
|
||||
for directory in self.directories:
|
||||
for plugin_dir in os.listdir( directory ):
|
||||
plugin_path = os.path.join( directory, plugin_dir )
|
||||
if self.is_plugin( plugin_path ):
|
||||
yield plugin_path
|
||||
|
||||
def is_plugin( self, plugin_path ):
|
||||
"""
|
||||
Determines whether the given filesystem path contains a plugin.
|
||||
|
||||
In this base class, all sub-directories are considered plugins.
|
||||
|
||||
:type plugin_path: string
|
||||
:param plugin_path: relative or absolute filesystem path to the
|
||||
potential plugin
|
||||
:rtype: bool
|
||||
:returns: True if the path contains a plugin
|
||||
"""
|
||||
if not os.path.isdir( plugin_path ):
|
||||
return False
|
||||
return True
|
||||
|
||||
def load_plugin( self, plugin_path ):
|
||||
"""
|
||||
Create, load, and/or initialize the plugin and return it.
|
||||
|
||||
Plugin bunches are decorated with:
|
||||
* name : the plugin name
|
||||
* path : the plugin path
|
||||
|
||||
:type plugin_path: string
|
||||
:param plugin_path: relative or absolute filesystem path to the plugin
|
||||
:rtype: ``util.bunch.Bunch``
|
||||
:returns: the loaded plugin object
|
||||
"""
|
||||
plugin = bunch.Bunch(
|
||||
#TODO: need a better way to define plugin names
|
||||
# pro: filesystem name ensures uniqueness
|
||||
# con: rel. inflexible
|
||||
name = os.path.split( plugin_path )[1],
|
||||
path = plugin_path
|
||||
)
|
||||
return plugin
|
||||
|
||||
|
||||
# ============================================================================= plugin managers using hooks
|
||||
class HookPluginManager( PluginManager ):
|
||||
"""
|
||||
A hook plugin is a directory containing python modules or packages that:
|
||||
* allow creating, including, and running custom code at specific 'hook'
|
||||
points/events
|
||||
* are not tracked in the Galaxy repository and allow adding custom code
|
||||
to a Galaxy installation
|
||||
|
||||
A HookPluginManager imports the plugin code needed and calls the plugin's
|
||||
hook functions at the specified time.
|
||||
"""
|
||||
#: the python file that will be imported - hook functions should be contained here
|
||||
loading_point_filename = 'plugin.py'
|
||||
hook_fn_prefix = 'hook_'
|
||||
|
||||
def is_plugin( self, plugin_path ):
|
||||
"""
|
||||
Determines whether the given filesystem path contains a hookable plugin.
|
||||
|
||||
All sub-directories that contain ``loading_point_filename`` are considered
|
||||
plugins.
|
||||
|
||||
:type plugin_path: string
|
||||
:param plugin_path: relative or absolute filesystem path to the
|
||||
potential plugin
|
||||
:rtype: bool
|
||||
:returns: True if the path contains a plugin
|
||||
"""
|
||||
if not super( HookPluginManager, self ).is_plugin( plugin_path ):
|
||||
return False
|
||||
#TODO: possibly switch to <plugin.name>.py or __init__.py
|
||||
if self.loading_point_filename not in os.listdir( plugin_path ):
|
||||
return False
|
||||
return True
|
||||
|
||||
def load_plugin( self, plugin_path ):
|
||||
"""
|
||||
Import the plugin ``loading_point_filename`` and attach to the plugin bunch.
|
||||
|
||||
Plugin bunches are decorated with:
|
||||
* name : the plugin name
|
||||
* path : the plugin path
|
||||
* module : the plugin code
|
||||
|
||||
:type plugin_path: string
|
||||
:param plugin_path: relative or absolute filesystem path to the plugin
|
||||
:rtype: ``util.bunch.Bunch``
|
||||
:returns: the loaded plugin object
|
||||
"""
|
||||
plugin = super( HookPluginManager, self ).load_plugin( plugin_path )
|
||||
|
||||
loading_point_name = self.loading_point_filename[:-3]
|
||||
plugin[ 'module' ] = self.import_plugin_module( loading_point_name, plugin )
|
||||
return plugin
|
||||
|
||||
def import_plugin_module( self, loading_point_name, plugin, import_as=None ):
|
||||
"""
|
||||
Import the plugin code and cache the module in the plugin object.
|
||||
|
||||
:type loading_point_name: string
|
||||
:param loading_point_name: name of the python file to import (w/o extension)
|
||||
:type plugin: ``util.bunch.Bunch``
|
||||
:param plugin: the plugin containing the template to render
|
||||
:type import_as: string
|
||||
:param import_as: namespace to use for imported module
|
||||
This will be prepended with the ``__name__`` of this file.
|
||||
Defaults to ``plugin.name``
|
||||
:rtype: ``util.bunch.Bunch``
|
||||
:returns: the loaded plugin object
|
||||
"""
|
||||
# add this name to import_as (w/ default to plugin.name) to prevent namespace pollution in sys.modules
|
||||
import_as = '%s.%s' %( __name__, ( import_as or plugin.name ) )
|
||||
module_file, pathname, description = imp.find_module( loading_point_name, [ plugin.path ] )
|
||||
try:
|
||||
#TODO: hate this hack but only way to get package imports inside the plugin to work?
|
||||
sys.path.append( plugin.path )
|
||||
# sys.modules will now have import_as in it's list
|
||||
module = imp.load_module( import_as, module_file, pathname, description )
|
||||
finally:
|
||||
module_file.close()
|
||||
if plugin.path in sys.path:
|
||||
sys.path.remove( plugin.path )
|
||||
return module
|
||||
|
||||
def run_hook( self, hook_name, *args, **kwargs ):
|
||||
"""
|
||||
Search all plugins for a function named ``hook_fn_prefix`` + ``hook_name``
|
||||
and run it passing in args and kwargs.
|
||||
|
||||
Return values from each hook are returned in a dictionary keyed with the
|
||||
plugin names.
|
||||
|
||||
:type hook_name: string
|
||||
:param hook_name: name (suffix) of the hook to run
|
||||
:rtype: dictionary
|
||||
:returns: where keys are plugin.names and
|
||||
values return values from the hooks
|
||||
"""
|
||||
#TODO: is hook prefix necessary?
|
||||
#TODO: could be made more efficient if cached by hook_name in the manager on load_plugin
|
||||
# (low maint. overhead since no dynamic loading/unloading of plugins)
|
||||
hook_fn_name = ''.join([ self.hook_fn_prefix, hook_name ])
|
||||
returned = {}
|
||||
for plugin_name, plugin in self.plugins.items():
|
||||
hook_fn = getattr( plugin.module, hook_fn_name, None )
|
||||
|
||||
if hook_fn and hasattr( hook_fn, '__call__' ):
|
||||
try:
|
||||
#log.debug( 'calling %s from %s(%s)', hook_fn.func_name, plugin.name, plugin.module )
|
||||
fn_returned = hook_fn( *args, **kwargs )
|
||||
returned[ plugin.name ] = fn_returned
|
||||
except Exception, exc:
|
||||
# fail gracefully and continue with other plugins
|
||||
log.exception( 'Hook function "%s" failed for plugin "%s"', hook_name, plugin.name )
|
||||
|
||||
# not sure of utility of this - seems better to be fire-and-forget pub-sub
|
||||
return returned
|
||||
|
||||
def filter_hook( self, hook_name, hook_arg, *args, **kwargs ):
|
||||
"""
|
||||
Search all plugins for a function named ``hook_fn_prefix`` + ``hook_name``
|
||||
and run the first with ``hook_arg`` and every function after with the
|
||||
return value of the previous.
|
||||
|
||||
..note:
|
||||
This makes plugin load order very important.
|
||||
|
||||
:type hook_name: string
|
||||
:param hook_name: name (suffix) of the hook to run
|
||||
:type hook_arg: any
|
||||
:param hook_arg: the arg to be passed between hook functions
|
||||
:rtype: any
|
||||
:returns: the modified hook_arg
|
||||
"""
|
||||
hook_fn_name = ''.join([ self.hook_fn_prefix, hook_name ])
|
||||
for plugin_name, plugin in self.plugins.items():
|
||||
hook_fn = getattr( plugin.module, hook_fn_name, None )
|
||||
|
||||
if hook_fn and hasattr( hook_fn, '__call__' ):
|
||||
try:
|
||||
hook_arg = hook_fn( hook_arg, *args, **kwargs )
|
||||
|
||||
except Exception, exc:
|
||||
# fail gracefully and continue with other plugins
|
||||
log.exception( 'Filter hook function "%s" failed for plugin "%s"', hook_name, plugin.name )
|
||||
|
||||
# may have been altered by hook fns, return
|
||||
return hook_arg
|
||||
|
||||
|
||||
# ============================================================================= exceptions
|
||||
class PluginManagerStaticException( PluginManagerException ):
|
||||
"""Exception for plugin framework static directory set up errors.
|
||||
"""
|
||||
pass
|
||||
class PluginFrameworkTemplateException( PluginFrameworkException ):
|
||||
class PluginManagerTemplateException( PluginManagerException ):
|
||||
"""Exception for plugin framework template directory
|
||||
and template rendering errors.
|
||||
"""
|
||||
@@ -40,118 +354,139 @@ class PluginFrameworkTemplateException( PluginFrameworkException ):
|
||||
|
||||
|
||||
# ============================================================================= base
|
||||
class PluginFramework( object ):
|
||||
class PageServingPluginManager( PluginManager ):
|
||||
"""
|
||||
Plugins are files/directories living outside the Galaxy ``lib`` directory
|
||||
that serve static files (css, js, images, etc.), use and serve mako templates,
|
||||
and have some configuration to control the rendering.
|
||||
Page serving plugins are files/directories that:
|
||||
* are not tracked in the Galaxy repository and allow adding custom code
|
||||
to a Galaxy installation
|
||||
* serve static files (css, js, images, etc.),
|
||||
* render templates
|
||||
|
||||
A plugin framework sets up all the above components.
|
||||
A PageServingPluginManager sets up all the above components.
|
||||
"""
|
||||
#: does the class need a config file(s) to be parsed?
|
||||
has_config = True
|
||||
#TODO: I'm unclear of the utility of this class - it prob. will only have one subclass (vis reg). Fold into?
|
||||
|
||||
#: does the class need static files served?
|
||||
serves_static = True
|
||||
#: does the class need template files served?
|
||||
serves_templates = True
|
||||
#TODO: allow plugin mako inheritance from existing ``/templates`` files
|
||||
#uses_galaxy_templates = True
|
||||
#TODO: possibly better as instance var (or a combo)
|
||||
#: the directories in ``plugin_directory`` with basenames listed here will
|
||||
#: be ignored for config, static, and templates
|
||||
non_plugin_directories = []
|
||||
#: default number of templates to search for plugin template lookup
|
||||
DEFAULT_TEMPLATE_COLLECTION_SIZE = 10
|
||||
#: default encoding of plugin templates
|
||||
DEFAULT_TEMPLATE_ENCODING = 'utf-8'
|
||||
|
||||
# ------------------------------------------------------------------------- setup
|
||||
@classmethod
|
||||
def from_config( cls, config_plugin_directory, config ):
|
||||
def __init__( self, app, base_url, template_cache_dir=None, **kwargs ):
|
||||
"""
|
||||
Set up the framework based on data from some config object by:
|
||||
* constructing it's absolute plugin_directory filepath
|
||||
* getting a template_cache
|
||||
* and appending itself to the config object's ``plugin_frameworks`` list
|
||||
Set up the manager and load all plugins.
|
||||
|
||||
.. note::
|
||||
precondition: config obj should have attributes:
|
||||
root, template_cache, and (list) plugin_frameworks
|
||||
:type app: UniverseApplication
|
||||
:param app: the application (and it's configuration) using this manager
|
||||
:type base_url: string
|
||||
:param base_url: url to prefix all plugin urls with
|
||||
:type template_cache_dir: string
|
||||
:param template_cache_dir: filesytem path to the directory where cached
|
||||
templates are kept
|
||||
"""
|
||||
# currently called from (base) app.py - defined here to allow override if needed
|
||||
if not config_plugin_directory:
|
||||
return None
|
||||
try:
|
||||
# create the plugin path and if plugin dir begins with '/' assume absolute path
|
||||
full_plugin_filepath = os.path.join( config.root, config_plugin_directory )
|
||||
if config_plugin_directory.startswith( os.path.sep ):
|
||||
full_plugin_filepath = config_plugin_directory
|
||||
if not os.path.exists( full_plugin_filepath ):
|
||||
raise PluginFrameworkException( 'Plugin path not found: %s' %( full_plugin_filepath ) )
|
||||
self.base_url = base_url
|
||||
self.template_cache_dir = template_cache_dir
|
||||
|
||||
template_cache = config.template_cache if cls.serves_static else None
|
||||
plugin = cls( full_plugin_filepath, template_cache )
|
||||
super( PageServingPluginManager, self ).__init__( app, **kwargs )
|
||||
|
||||
config.plugin_frameworks.append( plugin )
|
||||
return plugin
|
||||
|
||||
except PluginFrameworkException, plugin_exc:
|
||||
log.exception( "Error loading framework %s. Skipping...", cls.__class__.__name__ )
|
||||
return None
|
||||
|
||||
def __str__( self ):
|
||||
return '%s(%s)' %( self.__class__.__name__, self.plugin_directories )
|
||||
|
||||
def __init__( self, plugin_directories, name=None, template_cache_dir=None, debug=False, assert_exists=True ):
|
||||
def is_plugin( self, plugin_path ):
|
||||
"""
|
||||
:type plugin_directories: string or list
|
||||
:param plugin_directories: the base directory where plugin code is kept
|
||||
:type name: (optional) string (default: None)
|
||||
:param name: the name of this plugin
|
||||
(that will appear in url pathing, etc.)
|
||||
:type template_cache_dir: (optional) string (default: None)
|
||||
:param template_cache_dir: the cache directory to store compiled mako
|
||||
:type assert_exists: (optional) bool (default: False)
|
||||
:param assert_exists: If True, each configured plugin directory must exist.
|
||||
Determines whether the given filesystem path contains a plugin.
|
||||
|
||||
If the manager ``serves_templates`` and a sub-directory contains another
|
||||
sub-directory named 'templates' it's considered valid.
|
||||
If the manager ``serves_static`` and a sub-directory contains another
|
||||
sub-directory named 'static' it's considered valid.
|
||||
|
||||
:type plugin_path: string
|
||||
:param plugin_path: relative or absolute filesystem path to the
|
||||
potential plugin
|
||||
:rtype: bool
|
||||
:returns: True if the path contains a plugin
|
||||
"""
|
||||
self.plugin_directories = listify( plugin_directories )
|
||||
if assert_exists:
|
||||
for plugin_directory in self.plugin_directories:
|
||||
if not os.path.isdir( plugin_directory ):
|
||||
raise PluginFrameworkException( 'Framework plugin directory not found: %s, %s'
|
||||
% ( self.__class__.__name__, plugin_directory ) )
|
||||
|
||||
#TODO: or pass in from config
|
||||
self.name = name or os.path.basename( self.plugin_directories[0] )
|
||||
|
||||
if self.has_config:
|
||||
self.load_configuration()
|
||||
# set_up_static_urls will be called during the static middleware creation (if serves_static)
|
||||
if self.serves_templates:
|
||||
self.set_up_templates( template_cache_dir )
|
||||
|
||||
def get_plugin_directories( self ):
|
||||
"""
|
||||
Return the plugin directory paths for this plugin.
|
||||
|
||||
Gets any directories within ``plugin_directory`` that are directories
|
||||
themselves and whose ``basename`` is not in ``plugin_directory``.
|
||||
"""
|
||||
# could instead explicitly list on/off in master config file
|
||||
for plugin_directory in self.plugin_directories:
|
||||
for plugin_path in glob.glob( os.path.join( plugin_directory, '*' ) ):
|
||||
if not os.path.isdir( plugin_path ):
|
||||
continue
|
||||
|
||||
if os.path.basename( plugin_path ) in self.non_plugin_directories:
|
||||
continue
|
||||
|
||||
yield plugin_path
|
||||
|
||||
# ------------------------------------------------------------------------- config
|
||||
def load_configuration( self ):
|
||||
"""
|
||||
Override to load some framework/plugin specifc configuration.
|
||||
"""
|
||||
# Abstract method
|
||||
if not super( PageServingPluginManager, self ).is_plugin( plugin_path ):
|
||||
return False
|
||||
# reject only if we don't have either
|
||||
listdir = os.listdir( plugin_path )
|
||||
if( ( 'templates' not in listdir )
|
||||
and ( 'static' not in listdir ) ):
|
||||
return False
|
||||
return True
|
||||
|
||||
def load_plugin( self, plugin_path ):
|
||||
"""
|
||||
Create the plugin and decorate with static and/or template paths and urls.
|
||||
|
||||
Plugin bunches are decorated with:
|
||||
* name : the plugin name
|
||||
* path : the plugin path
|
||||
* base_url : a url to the plugin
|
||||
|
||||
:type plugin_path: string
|
||||
:param plugin_path: relative or absolute filesystem path to the plugin
|
||||
:rtype: ``util.bunch.Bunch``
|
||||
:returns: the loaded plugin object
|
||||
"""
|
||||
plugin = super( PageServingPluginManager, self ).load_plugin( plugin_path )
|
||||
#TODO: urlencode?
|
||||
plugin[ 'base_url' ] = '/'.join([ self.base_url, plugin.name ])
|
||||
plugin = self._set_up_static_plugin( plugin )
|
||||
plugin = self._set_up_template_plugin( plugin )
|
||||
|
||||
return plugin
|
||||
|
||||
def _set_up_static_plugin( self, plugin ):
|
||||
"""
|
||||
Decorate the plugin with paths and urls needed to serve static content.
|
||||
|
||||
Plugin bunches are decorated with:
|
||||
* serves_static : whether this plugin will serve static content
|
||||
|
||||
If the plugin path contains a 'static' sub-dir, the following are added:
|
||||
* static_path : the filesystem path to the static content
|
||||
* static_url : the url to use when serving static content
|
||||
|
||||
:type plugin: ``util.bunch.Bunch``
|
||||
:param plugin: the plugin to decorate
|
||||
:rtype: ``util.bunch.Bunch``
|
||||
:returns: the loaded plugin object
|
||||
"""
|
||||
plugin[ 'serves_static' ] = False
|
||||
static_path = os.path.join( plugin.path, 'static' )
|
||||
if self.serves_static and os.path.isdir( static_path ):
|
||||
plugin.serves_static = True
|
||||
plugin[ 'static_path' ] = static_path
|
||||
plugin[ 'static_url' ] = '/'.join([ plugin.base_url, 'static' ])
|
||||
return plugin
|
||||
|
||||
def _set_up_template_plugin( self, plugin ):
|
||||
"""
|
||||
Decorate the plugin with paths needed to fill templates.
|
||||
|
||||
Plugin bunches are decorated with:
|
||||
* serves_templates : whether this plugin will use templates
|
||||
|
||||
If the plugin path contains a 'static' sub-dir, the following are added:
|
||||
* template_path : the filesystem path to the template sub-dir
|
||||
* template_lookup : the (currently Mako) TemplateLookup used to search
|
||||
for templates
|
||||
|
||||
:type plugin: ``util.bunch.Bunch``
|
||||
:param plugin: the plugin to decorate
|
||||
:rtype: ``util.bunch.Bunch``
|
||||
:returns: the loaded plugin object
|
||||
"""
|
||||
plugin[ 'serves_templates' ] = False
|
||||
template_path = os.path.join( plugin.path, 'templates' )
|
||||
if self.serves_templates and os.path.isdir( template_path ):
|
||||
plugin.serves_templates = True
|
||||
plugin[ 'template_path' ] = template_path
|
||||
plugin[ 'template_lookup' ] = self.build_plugin_template_lookup( plugin )
|
||||
return plugin
|
||||
|
||||
# ------------------------------------------------------------------------- serving static files
|
||||
def get_static_urls_and_paths( self ):
|
||||
"""
|
||||
@@ -160,81 +495,63 @@ class PluginFramework( object ):
|
||||
same files.
|
||||
|
||||
Meant to be passed to a Static url map.
|
||||
|
||||
:rtype: list of 2-tuples
|
||||
:returns: all urls and paths for each plugin serving static content
|
||||
"""
|
||||
url_and_paths = []
|
||||
# called during the static middleware creation (buildapp.py, wrap_in_static)
|
||||
|
||||
# NOTE: this only searches for static dirs two levels deep (i.e. <plugin_directory>/<plugin-name>/static)
|
||||
for plugin_path in self.get_plugin_directories():
|
||||
# that path is a plugin, search for subdirs named static in THAT dir
|
||||
plugin_static_path = os.path.join( plugin_path, 'static' )
|
||||
if not os.path.isdir( plugin_static_path ):
|
||||
continue
|
||||
|
||||
# build a url for that static subdir and create a Static urlmap entry for it
|
||||
plugin_name = os.path.splitext( os.path.basename( plugin_path ) )[0]
|
||||
plugin_url = self.name + '/' + plugin_name + '/static'
|
||||
url_and_paths.append( ( plugin_url, plugin_static_path ) )
|
||||
|
||||
return url_and_paths
|
||||
urls_and_paths = []
|
||||
for plugin in self.plugins.values():
|
||||
if plugin.serves_static:
|
||||
urls_and_paths.append( ( plugin.static_url, plugin.static_path ) )
|
||||
return urls_and_paths
|
||||
|
||||
# ------------------------------------------------------------------------- templates
|
||||
def set_up_templates( self, template_cache_dir ):
|
||||
def build_plugin_template_lookup( self, plugin ):
|
||||
"""
|
||||
Add a ``template_lookup`` attribute to the framework that can be passed
|
||||
to the mako renderer to find templates.
|
||||
"""
|
||||
if not template_cache_dir:
|
||||
raise PluginFrameworkTemplateException( 'Plugins that serve templates require a template_cache_dir' )
|
||||
self.template_lookup = self._create_mako_template_lookup( template_cache_dir, self._get_template_paths() )
|
||||
return self.template_lookup
|
||||
Builds the object that searches for templates (cached or not) when rendering.
|
||||
|
||||
def _get_template_paths( self ):
|
||||
:type plugin: ``util.bunch.Bunch``
|
||||
:param plugin: the plugin containing the templates
|
||||
:rtype: ``Mako.lookup.TemplateLookup``
|
||||
:returns: template lookup for this plugin
|
||||
"""
|
||||
Get the paths that will be searched for templates.
|
||||
"""
|
||||
return self.plugin_directories
|
||||
if not plugin.serves_templates:
|
||||
return None
|
||||
template_lookup = self._create_mako_template_lookup( self.template_cache_dir, plugin.template_path )
|
||||
return template_lookup
|
||||
|
||||
def _create_mako_template_lookup( self, cache_dir, paths, collection_size=500, output_encoding='utf-8' ):
|
||||
def _create_mako_template_lookup( self, cache_dir, paths,
|
||||
collection_size=DEFAULT_TEMPLATE_COLLECTION_SIZE, output_encoding=DEFAULT_TEMPLATE_ENCODING ):
|
||||
"""
|
||||
Create a ``TemplateLookup`` with defaults.
|
||||
|
||||
:rtype: ``Mako.lookup.TemplateLookup``
|
||||
:returns: all urls and paths for each plugin serving static content
|
||||
"""
|
||||
#TODO: possible to add galaxy/templates into the lookup here?
|
||||
return mako.lookup.TemplateLookup(
|
||||
directories = paths,
|
||||
module_directory = cache_dir,
|
||||
collection_size = collection_size,
|
||||
output_encoding = output_encoding )
|
||||
|
||||
#TODO: do we want to remove trans and app from the plugin template context?
|
||||
def fill_template( self, trans, template_filename, **kwargs ):
|
||||
def fill_template( self, trans, plugin, template_filename, **kwargs ):
|
||||
"""
|
||||
Pass control over to trans and render the ``template_filename``.
|
||||
Pass control over to trans and render ``template_filename``.
|
||||
|
||||
:type trans: ``galaxy.web.framework.GalaxyWebTransaction``
|
||||
:param trans: transaction doing the rendering
|
||||
:type plugin: ``util.bunch.Bunch``
|
||||
:param plugin: the plugin containing the template to render
|
||||
:type template_filename: string
|
||||
:param template_filename: the path of the template to render relative to
|
||||
``plugin.template_path``
|
||||
:returns: rendered template
|
||||
"""
|
||||
# defined here to be overridden
|
||||
return trans.fill_template( template_filename, template_lookup=self.template_lookup, **kwargs )
|
||||
return trans.fill_template( template_filename, template_lookup=plugin.template_lookup, **kwargs )
|
||||
|
||||
def fill_template_with_plugin_imports( self, trans, template_filename, **kwargs ):
|
||||
"""
|
||||
Returns a rendered plugin template but allows importing modules from inside
|
||||
the plugin directory within the template.
|
||||
|
||||
..example:: I.e. given this layout for a plugin:
|
||||
bler/
|
||||
template/
|
||||
bler.mako
|
||||
static/
|
||||
conifg/
|
||||
my_script.py
|
||||
this version of `fill_template` allows `bler.mako` to call `import my_script`.
|
||||
"""
|
||||
try:
|
||||
plugin_path = os.path.dirname( os.path.dirname( template_filename ) )
|
||||
sys.path.append( plugin_path )
|
||||
filled_template = self.fill_template( trans, template_filename, **kwargs )
|
||||
|
||||
finally:
|
||||
sys.path.remove( plugin_path )
|
||||
|
||||
return filled_template
|
||||
|
||||
#TODO: could add plugin template helpers here
|
||||
#TODO: add fill_template fn that is able to load extra libraries beforehand (and remove after)
|
||||
#TODO: add template helpers specific to the plugins
|
||||
#TODO: some sort of url_for for these plugins
|
||||
|
||||
@@ -756,13 +756,6 @@ def build_select_field( trans, objs, label_attr, select_field_name, initial_val
|
||||
refresh_on_change=refresh_on_change,
|
||||
refresh_on_change_values=refresh_on_change_values,
|
||||
size=size )
|
||||
if display is None and initial_value == 'none':
|
||||
# Only insert an initial "Select one" option if we are not displaying check boxes
|
||||
# or radio buttons and we have not received an initial_value other than 'none'.
|
||||
if selected_value == initial_value:
|
||||
select_field.add_option( 'Select one', initial_value, selected=True )
|
||||
else:
|
||||
select_field.add_option( 'Select one', initial_value )
|
||||
for obj in objs:
|
||||
if label_attr == 'self':
|
||||
# Each obj is a string
|
||||
|
||||
@@ -379,7 +379,7 @@ class Response( object ):
|
||||
"""
|
||||
Send an HTTP redirect response to (target `url`)
|
||||
"""
|
||||
raise httpexceptions.HTTPFound( url, headers=self.wsgi_headeritems() )
|
||||
raise httpexceptions.HTTPFound( url.encode('utf-8'), headers=self.wsgi_headeritems() )
|
||||
|
||||
def wsgi_headeritems( self ):
|
||||
"""
|
||||
|
||||
@@ -198,7 +198,10 @@ class Grid( object ):
|
||||
if page_num == 0:
|
||||
# Show all rows in page.
|
||||
total_num_rows = query.count()
|
||||
# persistent page='all'
|
||||
page_num = 1
|
||||
#page_num = 'all'
|
||||
#extra_url_args['page'] = page_num
|
||||
num_pages = 1
|
||||
else:
|
||||
# Show a limited number of rows. Before modifying query, get the total number of rows that query
|
||||
|
||||
@@ -8,5 +8,5 @@ class RequestIDMiddleware( object ):
|
||||
def __init__( self, app, global_conf=None ):
|
||||
self.app = app
|
||||
def __call__( self, environ, start_response ):
|
||||
environ['request_id'] = uuid.uuid1().hex
|
||||
return self.app( environ, start_response )
|
||||
environ['request_id'] = uuid.uuid1().hex
|
||||
return self.app( environ, start_response )
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
API operations allowing clients to determine datatype supported by Galaxy.
|
||||
"""
|
||||
|
||||
from galaxy import web
|
||||
from galaxy.web.base.controller import BaseAPIController
|
||||
from galaxy.datatypes.registry import Registry
|
||||
|
||||
import logging
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
class DatatypesController( BaseAPIController ):
|
||||
@web.expose_api
|
||||
def index( self, trans, **kwd ):
|
||||
"""
|
||||
GET /api/datatypes
|
||||
Return an object containing datatypes.
|
||||
"""
|
||||
try:
|
||||
return trans.app.datatypes_registry.upload_file_formats
|
||||
|
||||
except Exception, exception:
|
||||
log.error( 'could not get datatypes: %s', str( exception ), exc_info=True )
|
||||
trans.response.status = 500
|
||||
return { 'error': str( exception ) }
|
||||
@@ -52,7 +52,7 @@ class FoldersController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinIt
|
||||
o payload's relevant params:
|
||||
- folder_id: This is the parent folder's id (required)
|
||||
"""
|
||||
log.debug( "FoldersController.create: enter" )
|
||||
# log.debug( "FoldersController.create: enter" )
|
||||
# TODO: Create a single point of exit if possible. For now we only
|
||||
# exit at the end and on exceptions.
|
||||
if 'folder_id' not in payload:
|
||||
@@ -90,7 +90,7 @@ class FoldersController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinIt
|
||||
name = v.name,
|
||||
url = url_for( 'folder', id=encoded_id ) ) )
|
||||
else:
|
||||
log.debug( "Error creating folder; setting output and status" )
|
||||
# log.debug( "Error creating folder; setting output and status" )
|
||||
trans.response.status = status
|
||||
rval = output
|
||||
return rval
|
||||
|
||||
@@ -214,62 +214,6 @@ class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociatio
|
||||
trans.sa_session.flush()
|
||||
return hda.to_dict()
|
||||
|
||||
# copy from upload
|
||||
if source == 'upload':
|
||||
|
||||
# get upload specific features
|
||||
dbkey = payload.get('dbkey', None)
|
||||
extension = payload.get('extension', None)
|
||||
space_to_tabs = payload.get('space_to_tabs', False)
|
||||
|
||||
# check for filename
|
||||
if content.filename is None:
|
||||
trans.response.status = 400
|
||||
return "history_contents:create() : The contents parameter needs to contain the uploaded file content."
|
||||
|
||||
# create a dataset
|
||||
dataset = trans.app.model.Dataset()
|
||||
trans.sa_session.add(dataset)
|
||||
trans.sa_session.flush()
|
||||
|
||||
# get file destination
|
||||
file_destination = dataset.get_file_name()
|
||||
|
||||
# save file locally
|
||||
fn = os.path.basename(content.filename)
|
||||
open(file_destination, 'wb').write(content.file.read())
|
||||
|
||||
# log
|
||||
log.info ('The file "' + fn + '" was uploaded successfully.')
|
||||
|
||||
# replace separation with tabs
|
||||
if space_to_tabs:
|
||||
log.info ('Replacing spaces with tabs.')
|
||||
sniff.convert_newlines_sep2tabs(file_destination)
|
||||
|
||||
# guess extension
|
||||
if extension is None:
|
||||
log.info ('Guessing extension.')
|
||||
extension = sniff.guess_ext(file_destination)
|
||||
|
||||
# create hda
|
||||
hda = trans.app.model.HistoryDatasetAssociation(dataset = dataset, name = content.filename,
|
||||
extension = extension, dbkey = dbkey, history = history, sa_session = trans.sa_session)
|
||||
|
||||
# add status ok
|
||||
hda.state = hda.states.OK
|
||||
|
||||
# add dataset to history
|
||||
history.add_dataset(hda, genome_build = dbkey)
|
||||
permissions = trans.app.security_agent.history_get_default_permissions( history )
|
||||
trans.app.security_agent.set_all_dataset_permissions( hda.dataset, permissions )
|
||||
|
||||
# add to session
|
||||
trans.sa_session.add(hda)
|
||||
trans.sa_session.flush()
|
||||
|
||||
# get name
|
||||
return hda.to_dict()
|
||||
else:
|
||||
# other options
|
||||
trans.response.status = 501
|
||||
@@ -321,6 +265,75 @@ class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociatio
|
||||
|
||||
return changed
|
||||
|
||||
@web.expose_api
|
||||
def delete( self, trans, history_id, id, **kwd ):
|
||||
"""
|
||||
delete( self, trans, history_id, id, **kwd )
|
||||
* DELETE /api/histories/{history_id}/contents/{id}
|
||||
delete the HDA with the given ``id``
|
||||
.. note:: Currently does not stop any active jobs for which this dataset is an output.
|
||||
|
||||
:type id: str
|
||||
:param id: the encoded id of the history to delete
|
||||
:type kwd: dict
|
||||
:param kwd: (optional) dictionary structure containing:
|
||||
|
||||
* payload: a dictionary itself containing:
|
||||
* purge: if True, purge the HDA
|
||||
|
||||
:rtype: dict
|
||||
:returns: an error object if an error occurred or a dictionary containing:
|
||||
* id: the encoded id of the history,
|
||||
* deleted: if the history was marked as deleted,
|
||||
* purged: if the history was purged
|
||||
"""
|
||||
# a request body is optional here
|
||||
purge = False
|
||||
if kwd.get( 'payload', None ):
|
||||
purge = string_as_bool( kwd['payload'].get( 'purge', False ) )
|
||||
|
||||
rval = { 'id' : id }
|
||||
try:
|
||||
hda = self.get_dataset( trans, id,
|
||||
check_ownership=True, check_accessible=True, check_state=True )
|
||||
hda.deleted = True
|
||||
|
||||
if purge:
|
||||
if not trans.app.config.allow_user_dataset_purge:
|
||||
raise HTTPForbidden( detail='This instance does not allow user dataset purging' )
|
||||
|
||||
hda.purged = True
|
||||
trans.sa_session.add( hda )
|
||||
trans.sa_session.flush()
|
||||
|
||||
if hda.dataset.user_can_purge:
|
||||
try:
|
||||
hda.dataset.full_delete()
|
||||
trans.sa_session.add( hda.dataset )
|
||||
except:
|
||||
pass
|
||||
# flush now to preserve deleted state in case of later interruption
|
||||
trans.sa_session.flush()
|
||||
|
||||
rval[ 'purged' ] = True
|
||||
|
||||
trans.sa_session.flush()
|
||||
rval[ 'deleted' ] = True
|
||||
|
||||
except HTTPInternalServerError, http_server_err:
|
||||
log.exception( 'HDA API, delete: uncaught HTTPInternalServerError: %s, %s\n%s',
|
||||
id, str( kwd ), str( http_server_err ) )
|
||||
raise
|
||||
except HTTPException, http_exc:
|
||||
raise
|
||||
except Exception, exc:
|
||||
log.exception( 'HDA API, delete: uncaught exception: %s, %s\n%s',
|
||||
id, str( kwd ), str( exc ) )
|
||||
trans.response.status = 500
|
||||
rval.update({ 'error': str( exc ) })
|
||||
|
||||
return rval
|
||||
|
||||
def _validate_and_parse_update_payload( self, payload ):
|
||||
"""
|
||||
Validate and parse incomming data payload for an HDA.
|
||||
|
||||
@@ -28,7 +28,7 @@ class LibrariesController( BaseAPIController ):
|
||||
:returns: list of dictionaries containing library information
|
||||
.. seealso:: :attr:`galaxy.model.Library.dict_collection_visible_keys`
|
||||
"""
|
||||
log.debug( "LibrariesController.index: enter" )
|
||||
# log.debug( "LibrariesController.index: enter" )
|
||||
query = trans.sa_session.query( trans.app.model.Library )
|
||||
deleted = util.string_as_bool( deleted )
|
||||
if deleted:
|
||||
@@ -73,7 +73,7 @@ class LibrariesController( BaseAPIController ):
|
||||
:returns: detailed library information
|
||||
.. seealso:: :attr:`galaxy.model.Library.dict_element_visible_keys`
|
||||
"""
|
||||
log.debug( "LibraryContentsController.show: enter" )
|
||||
# log.debug( "LibraryContentsController.show: enter" )
|
||||
library_id = id
|
||||
deleted = util.string_as_bool( deleted )
|
||||
try:
|
||||
|
||||
@@ -53,8 +53,8 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar
|
||||
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:
|
||||
log.debug( "type(folder): %s" % type( folder ) )
|
||||
log.debug( "type(api_path): %s; folder.api_path: %s" % ( type(folder.api_path), folder.api_path ) )
|
||||
#log.debug( "type(folder): %s" % type( folder ) )
|
||||
#log.debug( "type(api_path): %s; folder.api_path: %s" % ( type(folder.api_path), folder.api_path ) )
|
||||
#log.debug( "attributes of folder: %s" % str(dir(folder)) )
|
||||
ld.api_path = folder.api_path + '/' + ld.name
|
||||
ld.api_type = 'file'
|
||||
@@ -72,13 +72,13 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar
|
||||
if not library or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library( current_user_roles, library ) ):
|
||||
trans.response.status = 400
|
||||
return "Invalid library id ( %s ) specified." % str( library_id )
|
||||
log.debug( "Root folder type: %s" % type( library.root_folder ) )
|
||||
#log.debug( "Root folder type: %s" % type( library.root_folder ) )
|
||||
encoded_id = 'F' + trans.security.encode_id( library.root_folder.id )
|
||||
rval.append( dict( id = encoded_id,
|
||||
type = 'folder',
|
||||
name = '/',
|
||||
url = url_for( 'library_content', library_id=library_id, id=encoded_id ) ) )
|
||||
log.debug( "Root folder attributes: %s" % str(dir(library.root_folder)) )
|
||||
#log.debug( "Root folder attributes: %s" % str(dir(library.root_folder)) )
|
||||
library.root_folder.api_path = ''
|
||||
for content in traverse( library.root_folder ):
|
||||
encoded_id = trans.security.encode_id( content.id )
|
||||
|
||||
@@ -10,6 +10,7 @@ from galaxy.web.base.controller import BaseAPIController
|
||||
from tool_shed.galaxy_install import repository_util
|
||||
from tool_shed.util import common_util
|
||||
from tool_shed.util import encoding_util
|
||||
from tool_shed.util import workflow_util
|
||||
import tool_shed.util.shed_util_common as suc
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
@@ -32,6 +33,102 @@ def get_message_for_no_shed_tool_config():
|
||||
class ToolShedRepositoriesController( BaseAPIController ):
|
||||
"""RESTful controller for interactions with tool shed repositories."""
|
||||
|
||||
@web.expose_api
|
||||
def exported_workflows( self, trans, id, **kwd ):
|
||||
"""
|
||||
GET /api/tool_shed_repositories/{encoded_tool_shed_repository_id}/exported_workflows
|
||||
|
||||
Display a list of dictionaries containing information about this tool shed repository's exported workflows.
|
||||
|
||||
:param id: the encoded id of the ToolShedRepository object
|
||||
"""
|
||||
# Example URL: http://localhost:8763/api/tool_shed_repositories/f2db41e1fa331b3e/exported_workflows
|
||||
# Since exported workflows are dictionaries with very few attributes that differentiate them from each other, we'll build the
|
||||
# list based on the following dictionary of those few attributes.
|
||||
exported_workflows = []
|
||||
repository = suc.get_tool_shed_repository_by_id( trans, id )
|
||||
metadata = repository.metadata
|
||||
if metadata:
|
||||
exported_workflow_tups = metadata.get( 'workflows', [] )
|
||||
else:
|
||||
exported_workflow_tups = []
|
||||
for index, exported_workflow_tup in enumerate( exported_workflow_tups ):
|
||||
# The exported_workflow_tup looks like ( relative_path, exported_workflow_dict ), where the value of relative_path is the location
|
||||
# on disk (relative to the root of the installed repository) where the exported_workflow_dict file (.ga file) is located.
|
||||
exported_workflow_dict = exported_workflow_tup[ 1 ]
|
||||
annotation = exported_workflow_dict.get( 'annotation', '' )
|
||||
format_version = exported_workflow_dict.get( 'format-version', '' )
|
||||
workflow_name = exported_workflow_dict.get( 'name', '' )
|
||||
# Since we don't have an in-memory object with an id, we'll identify the exported workflow via it's location (i.e., index) in the list.
|
||||
display_dict = dict( index=index, annotation=annotation, format_version=format_version, workflow_name=workflow_name )
|
||||
exported_workflows.append( display_dict )
|
||||
return exported_workflows
|
||||
|
||||
@web.expose_api
|
||||
def import_workflow( self, trans, payload, **kwd ):
|
||||
"""
|
||||
POST /api/tool_shed_repositories/import_workflow
|
||||
|
||||
Import the specified exported workflow contained in the specified installed tool shed repository into Galaxy.
|
||||
|
||||
:param key: the API key of the Galaxy user with which the imported workflow will be associated.
|
||||
:param id: the encoded id of the ToolShedRepository object
|
||||
|
||||
The following parameters are included in the payload.
|
||||
:param index: the index location of the workflow tuple in the list of exported workflows stored in the metadata for the specified repository
|
||||
"""
|
||||
api_key = kwd.get( 'key', None )
|
||||
if api_key is None:
|
||||
raise HTTPBadRequest( detail="Missing required parameter 'key' whose value is the API key for the Galaxy user importing the specified workflow." )
|
||||
tool_shed_repository_id = kwd.get( 'id', '' )
|
||||
if not tool_shed_repository_id:
|
||||
raise HTTPBadRequest( detail="Missing required parameter 'id'." )
|
||||
index = payload.get( 'index', None )
|
||||
if index is None:
|
||||
raise HTTPBadRequest( detail="Missing required parameter 'index'." )
|
||||
repository = suc.get_tool_shed_repository_by_id( trans, tool_shed_repository_id )
|
||||
exported_workflows = json.from_json_string( self.exported_workflows( trans, tool_shed_repository_id ) )
|
||||
# Since we don't have an in-memory object with an id, we'll identify the exported workflow via it's location (i.e., index) in the list.
|
||||
exported_workflow = exported_workflows[ int( index ) ]
|
||||
workflow_name = exported_workflow[ 'workflow_name' ]
|
||||
workflow, status, message = workflow_util.import_workflow( trans, repository, workflow_name )
|
||||
if status == 'error':
|
||||
log.error( message, exc_info=True )
|
||||
trans.response.status = 500
|
||||
return message
|
||||
else:
|
||||
return workflow.to_dict( view='element' )
|
||||
|
||||
@web.expose_api
|
||||
def import_workflows( self, trans, **kwd ):
|
||||
"""
|
||||
POST /api/tool_shed_repositories/import_workflow
|
||||
|
||||
Import all of the exported workflows contained in the specified installed tool shed repository into Galaxy.
|
||||
|
||||
:param key: the API key of the Galaxy user with which the imported workflows will be associated.
|
||||
:param id: the encoded id of the ToolShedRepository object
|
||||
"""
|
||||
api_key = kwd.get( 'key', None )
|
||||
if api_key is None:
|
||||
raise HTTPBadRequest( detail="Missing required parameter 'key' whose value is the API key for the Galaxy user importing the specified workflow." )
|
||||
tool_shed_repository_id = kwd.get( 'id', '' )
|
||||
if not tool_shed_repository_id:
|
||||
raise HTTPBadRequest( detail="Missing required parameter 'id'." )
|
||||
repository = suc.get_tool_shed_repository_by_id( trans, tool_shed_repository_id )
|
||||
exported_workflows = json.from_json_string( self.exported_workflows( trans, tool_shed_repository_id ) )
|
||||
imported_workflow_dicts = []
|
||||
for exported_workflow_dict in exported_workflows:
|
||||
workflow_name = exported_workflow_dict[ 'workflow_name' ]
|
||||
workflow, status, message = workflow_util.import_workflow( trans, repository, workflow_name )
|
||||
if status == 'error':
|
||||
log.error( message, exc_info=True )
|
||||
trans.response.status = 500
|
||||
return message
|
||||
else:
|
||||
imported_workflow_dicts.append( workflow.to_dict( view='element' ) )
|
||||
return imported_workflow_dicts
|
||||
|
||||
@web.expose_api
|
||||
def index( self, trans, **kwd ):
|
||||
"""
|
||||
@@ -57,28 +154,6 @@ class ToolShedRepositoriesController( BaseAPIController ):
|
||||
trans.response.status = 500
|
||||
return message
|
||||
|
||||
@web.expose_api
|
||||
def show( self, trans, id, **kwd ):
|
||||
"""
|
||||
GET /api/tool_shed_repositories/{encoded_tool_shed_repsository_id}
|
||||
Display a dictionary containing information about a specified tool_shed_repository.
|
||||
|
||||
:param id: the encoded id of the ToolShedRepository object
|
||||
"""
|
||||
# Example URL: http://localhost:8763/api/tool_shed_repositories/df7a1f0c02a5b08e
|
||||
try:
|
||||
tool_shed_repository = suc.get_tool_shed_repository_by_id( trans, id )
|
||||
tool_shed_repository_dict = tool_shed_repository.as_dict( value_mapper=default_tool_shed_repository_value_mapper( trans, tool_shed_repository ) )
|
||||
tool_shed_repository_dict[ 'url' ] = web.url_for( controller='tool_shed_repositories',
|
||||
action='show',
|
||||
id=trans.security.encode_id( tool_shed_repository.id ) )
|
||||
return tool_shed_repository_dict
|
||||
except Exception, e:
|
||||
message = "Error in tool_shed_repositories API in index: " + str( e )
|
||||
log.error( message, exc_info=True )
|
||||
trans.response.status = 500
|
||||
return message
|
||||
|
||||
@web.expose_api
|
||||
def install_repository_revision( self, trans, payload, **kwd ):
|
||||
"""
|
||||
@@ -211,7 +286,6 @@ class ToolShedRepositoriesController( BaseAPIController ):
|
||||
installation_dict = dict( install_repository_dependencies=install_repository_dependencies,
|
||||
new_tool_panel_section=new_tool_panel_section,
|
||||
no_changes_checked=False,
|
||||
reinstalling=False,
|
||||
repo_info_dicts=repo_info_dicts,
|
||||
tool_panel_section=tool_panel_section,
|
||||
tool_path=tool_path,
|
||||
@@ -412,3 +486,25 @@ class ToolShedRepositoriesController( BaseAPIController ):
|
||||
tool_shed_repositories.append( repository_dict )
|
||||
# Display the list of repaired repositories.
|
||||
return tool_shed_repositories
|
||||
|
||||
@web.expose_api
|
||||
def show( self, trans, id, **kwd ):
|
||||
"""
|
||||
GET /api/tool_shed_repositories/{encoded_tool_shed_repsository_id}
|
||||
Display a dictionary containing information about a specified tool_shed_repository.
|
||||
|
||||
:param id: the encoded id of the ToolShedRepository object
|
||||
"""
|
||||
# Example URL: http://localhost:8763/api/tool_shed_repositories/df7a1f0c02a5b08e
|
||||
try:
|
||||
tool_shed_repository = suc.get_tool_shed_repository_by_id( trans, id )
|
||||
tool_shed_repository_dict = tool_shed_repository.as_dict( value_mapper=default_tool_shed_repository_value_mapper( trans, tool_shed_repository ) )
|
||||
tool_shed_repository_dict[ 'url' ] = web.url_for( controller='tool_shed_repositories',
|
||||
action='show',
|
||||
id=trans.security.encode_id( tool_shed_repository.id ) )
|
||||
return tool_shed_repository_dict
|
||||
except Exception, e:
|
||||
message = "Error in tool_shed_repositories API in index: " + str( e )
|
||||
log.error( message, exc_info=True )
|
||||
trans.response.status = 500
|
||||
return message
|
||||
|
||||
@@ -32,7 +32,7 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
|
||||
|
||||
# Create return value.
|
||||
try:
|
||||
return self.app.toolbox.to_dict( trans, in_panel=in_panel, trackster=trackster )
|
||||
return self.app.toolbox.to_dict( trans, in_panel=in_panel, trackster=trackster)
|
||||
except Exception, exc:
|
||||
log.error( 'could not convert toolbox to dictionary: %s', str( exc ), exc_info=True )
|
||||
trans.response.status = 500
|
||||
@@ -86,6 +86,17 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
|
||||
for k, v in payload.iteritems():
|
||||
if k.startswith("files_"):
|
||||
inputs[k] = v
|
||||
|
||||
#for inputs that are coming from the Library, copy them into the history
|
||||
input_patch = {}
|
||||
for k, v in inputs.iteritems():
|
||||
if isinstance(v, dict) and v.get('src', '') == 'ldda' and 'id' in v:
|
||||
ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id(v['id']) )
|
||||
if trans.user_is_admin() or trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), ldda.dataset ):
|
||||
input_patch[k] = ldda.to_history_dataset_association(target_history, add_to_history=True)
|
||||
|
||||
for k, v in input_patch.iteritems():
|
||||
inputs[k] = v
|
||||
|
||||
# HACK: add run button so that tool.handle_input will run tool.
|
||||
inputs['runtool_btn'] = 'Execute'
|
||||
|
||||
@@ -46,11 +46,17 @@ def app_factory( global_conf, **kwargs ):
|
||||
atexit.register( app.shutdown )
|
||||
# Create the universe WSGI application
|
||||
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
|
||||
# The following route will handle displaying tool shelp images for tools contained in repositories installed from the tool shed.
|
||||
webapp.add_route( '/tool_runner/static/images/:repository_id/:image_file', controller='tool_runner', action='display_tool_help_image_in_repository', repository_id=None, image_file=None )
|
||||
# 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' )
|
||||
# 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' )
|
||||
@@ -156,6 +162,7 @@ def app_factory( global_conf, **kwargs ):
|
||||
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.resource( 'datatype', 'datatypes', 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' )
|
||||
|
||||
@@ -176,7 +183,10 @@ def app_factory( global_conf, **kwargs ):
|
||||
# Galaxy API for tool shed features.
|
||||
webapp.mapper.resource( 'tool_shed_repository',
|
||||
'tool_shed_repositories',
|
||||
member={ 'repair_repository_revision' : 'POST' },
|
||||
member={ 'repair_repository_revision' : 'POST',
|
||||
'exported_workflows' : 'GET',
|
||||
'import_workflow' : 'POST',
|
||||
'import_workflows' : 'POST' },
|
||||
controller='tool_shed_repositories',
|
||||
name_prefix='tool_shed_repository_',
|
||||
path_prefix='/api',
|
||||
@@ -193,7 +203,8 @@ def app_factory( global_conf, **kwargs ):
|
||||
if kwargs.get( 'middleware', True ):
|
||||
webapp = wrap_in_middleware( webapp, global_conf, **kwargs )
|
||||
if asbool( kwargs.get( 'static_enabled', True ) ):
|
||||
webapp = wrap_in_static( webapp, global_conf, plugin_frameworks=app.config.plugin_frameworks, **kwargs )
|
||||
webapp = wrap_in_static( webapp, global_conf, plugin_frameworks=[ app.visualizations_registry ], **kwargs )
|
||||
#webapp = wrap_in_static( webapp, global_conf, plugin_frameworks=None, **kwargs )
|
||||
if asbool(kwargs.get('pack_scripts', False)):
|
||||
pack_scripts()
|
||||
# Close any pooled database connections before forking
|
||||
@@ -362,12 +373,13 @@ def wrap_in_static( app, global_conf, plugin_frameworks=None, **local_conf ):
|
||||
|
||||
# wrap any static dirs for plugins
|
||||
plugin_frameworks = plugin_frameworks or []
|
||||
for static_serving_framework in ( framework for framework in plugin_frameworks if framework.serves_static ):
|
||||
# invert control to each plugin for finding their own static dirs
|
||||
for plugin_url, plugin_static_path in static_serving_framework.get_static_urls_and_paths():
|
||||
plugin_url = '/plugins/' + plugin_url
|
||||
urlmap[( plugin_url )] = Static( plugin_static_path, cache_time )
|
||||
log.debug( 'added url, path to static middleware: %s, %s', plugin_url, plugin_static_path )
|
||||
for framework in plugin_frameworks:
|
||||
if framework and framework.serves_static:
|
||||
# invert control to each plugin for finding their own static dirs
|
||||
for plugin_url, plugin_static_path in framework.get_static_urls_and_paths():
|
||||
plugin_url = '/plugins/' + plugin_url
|
||||
urlmap[( plugin_url )] = Static( plugin_static_path, cache_time )
|
||||
log.debug( 'added url, path to static middleware: %s, %s', plugin_url, plugin_static_path )
|
||||
|
||||
# URL mapper becomes the root webapp
|
||||
return urlmap
|
||||
|
||||
@@ -284,6 +284,39 @@ class AdminToolshed( AdminGalaxy ):
|
||||
message=message,
|
||||
status=status )
|
||||
|
||||
@web.expose
|
||||
def display_image_in_repository( self, trans, **kwd ):
|
||||
"""
|
||||
Open an image file that is contained in an installed tool shed repository or that is referenced by a URL for display. The
|
||||
image can be defined in either a README.rst file contained in the repository or the help section of a Galaxy tool config that
|
||||
is contained in the repository. The following image definitions are all supported. The former $PATH_TO_IMAGES is no longer
|
||||
required, and is now ignored.
|
||||
.. image:: https://raw.github.com/galaxy/some_image.png
|
||||
.. image:: $PATH_TO_IMAGES/some_image.png
|
||||
.. image:: /static/images/some_image.gif
|
||||
.. image:: some_image.jpg
|
||||
.. image:: /deep/some_image.png
|
||||
"""
|
||||
repository_id = kwd.get( 'repository_id', None )
|
||||
relative_path_to_image_file = kwd.get( 'image_file', None )
|
||||
if repository_id and relative_path_to_image_file:
|
||||
repository = suc.get_tool_shed_repository_by_id( trans, repository_id )
|
||||
if repository:
|
||||
repo_files_dir = repository.repo_files_directory( trans.app )
|
||||
path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
|
||||
if os.path.exists( path_to_file ):
|
||||
file_name = os.path.basename( relative_path_to_image_file )
|
||||
try:
|
||||
extension = file_name.split( '.' )[ -1 ]
|
||||
except Exception, e:
|
||||
extension = None
|
||||
if extension:
|
||||
mimetype = trans.app.datatypes_registry.get_mimetype_by_extension( extension )
|
||||
if mimetype:
|
||||
trans.response.set_content_type( mimetype )
|
||||
return open( path_to_file, 'r' )
|
||||
return None
|
||||
|
||||
@web.expose
|
||||
@web.require_admin
|
||||
def find_tools_in_tool_shed( self, trans, **kwd ):
|
||||
@@ -358,66 +391,26 @@ class AdminToolshed( AdminGalaxy ):
|
||||
@web.expose
|
||||
@web.require_admin
|
||||
def import_workflow( self, trans, workflow_name, repository_id, **kwd ):
|
||||
"""Import a workflow contained in an installed tool shed repository into the Galaxy instance."""
|
||||
"""Import a workflow contained in an installed tool shed repository into Galaxy."""
|
||||
message = kwd.get( 'message', '' )
|
||||
status = kwd.get( 'status', 'done' )
|
||||
if workflow_name:
|
||||
workflow_name = encoding_util.tool_shed_decode( workflow_name )
|
||||
repository = suc.get_tool_shed_repository_by_id( trans, repository_id )
|
||||
changeset_revision = repository.changeset_revision
|
||||
metadata = repository.metadata
|
||||
workflows = metadata.get( 'workflows', [] )
|
||||
tools_metadata = metadata.get( 'tools', [] )
|
||||
workflow_dict = None
|
||||
for workflow_data_tuple in workflows:
|
||||
# The value of workflow_data_tuple is ( relative_path_to_workflow_file, exported_workflow_dict ).
|
||||
relative_path_to_workflow_file, exported_workflow_dict = workflow_data_tuple
|
||||
if exported_workflow_dict[ 'name' ] == workflow_name:
|
||||
# If the exported workflow is available on disk, import it.
|
||||
if os.path.exists( relative_path_to_workflow_file ):
|
||||
workflow_file = open( relative_path_to_workflow_file, 'rb' )
|
||||
workflow_data = workflow_file.read()
|
||||
workflow_file.close()
|
||||
workflow_dict = json.from_json_string( workflow_data )
|
||||
repository = suc.get_tool_shed_repository_by_id( trans, repository_id )
|
||||
if repository:
|
||||
workflow, status, message = workflow_util.import_workflow( trans, repository, workflow_name )
|
||||
if workflow:
|
||||
workflow_name = encoding_util.tool_shed_encode( str( workflow.name ) )
|
||||
else:
|
||||
# Use the current exported_workflow_dict.
|
||||
workflow_dict = exported_workflow_dict
|
||||
break
|
||||
if workflow_dict:
|
||||
# Create workflow if possible.
|
||||
workflow, missing_tool_tups = workflow_util.get_workflow_from_dict( trans=trans,
|
||||
workflow_dict=workflow_dict,
|
||||
tools_metadata=tools_metadata,
|
||||
repository_id=repository_id,
|
||||
changeset_revision=changeset_revision )
|
||||
# Save the workflow in the Galaxy database.
|
||||
# Pass workflow_dict along to create annotation at this point
|
||||
stored_workflow = workflow_util.save_workflow( trans, workflow, workflow_dict )
|
||||
# Use the latest version of the saved workflow.
|
||||
workflow = stored_workflow.latest_workflow
|
||||
if workflow_name:
|
||||
workflow.name = workflow_name
|
||||
# Provide user feedback and show workflow list.
|
||||
if workflow.has_errors:
|
||||
message += "Imported, but some steps in this workflow have validation errors. "
|
||||
status = "error"
|
||||
if workflow.has_cycles:
|
||||
message += "Imported, but this workflow contains cycles. "
|
||||
status = "error"
|
||||
message += 'Unable to locate a workflow named <b>%s</b> within the installed tool shed repository named <b>%s</b>' % \
|
||||
( str( workflow_name ), str( repository.name ) )
|
||||
status = 'error'
|
||||
else:
|
||||
message += "Workflow <b>%s</b> imported successfully. " % workflow.name
|
||||
if missing_tool_tups:
|
||||
# TODO: rework this since it is used in the tool shed, but shoudn't be used in Galaxy.
|
||||
name_and_id_str = ''
|
||||
for missing_tool_tup in missing_tool_tups:
|
||||
tool_id, tool_name, other = missing_tool_tup
|
||||
name_and_id_str += 'name: %s, id: %s' % ( str( tool_id ), str( tool_name ) )
|
||||
log.debug( "The following tools required by this workflow are missing from this Galaxy instance: %s" % name_and_id_str )
|
||||
message = 'Invalid repository id <b>%s</b> received.' % str( repository_id )
|
||||
status = 'error'
|
||||
else:
|
||||
message += 'The workflow named %s is not included in the metadata for revision %s of repository %s' % \
|
||||
( str( workflow_name ), str( changeset_revision ), str( repository.name ) )
|
||||
message = 'The value of workflow_name is required, but was not received.'
|
||||
status = 'error'
|
||||
workflow_name = encoding_util.tool_shed_encode( workflow.name ),
|
||||
return trans.response.send_redirect( web.url_for( controller='admin_toolshed',
|
||||
action='view_workflow',
|
||||
workflow_name=workflow_name,
|
||||
@@ -708,11 +701,11 @@ class AdminToolshed( AdminGalaxy ):
|
||||
installed_tool_dependencies_select_field = suc.build_tool_dependencies_select_field( trans,
|
||||
tool_shed_repository=tool_shed_repository,
|
||||
name='inst_td_ids',
|
||||
uninstalled=False )
|
||||
uninstalled_only=False )
|
||||
uninstalled_tool_dependencies_select_field = suc.build_tool_dependencies_select_field( trans,
|
||||
tool_shed_repository=tool_shed_repository,
|
||||
name='uninstalled_tool_dependency_ids',
|
||||
uninstalled=True )
|
||||
uninstalled_only=True )
|
||||
return trans.fill_template( '/admin/tool_shed_repository/manage_repository_tool_dependencies.mako',
|
||||
repository=tool_shed_repository,
|
||||
installed_tool_dependencies_select_field=installed_tool_dependencies_select_field,
|
||||
@@ -811,8 +804,8 @@ class AdminToolshed( AdminGalaxy ):
|
||||
message = kwd.get( 'message', '' )
|
||||
status = kwd.get( 'status', 'done' )
|
||||
shed_tool_conf = kwd.get( 'shed_tool_conf', None )
|
||||
tool_shed_url = kwd[ 'tool_shed_url' ]
|
||||
# Handle repository dependencies.
|
||||
tool_shed_url = kwd.get( 'tool_shed_url', None )
|
||||
# Handle repository dependencies, which do not include those that are required only for compiling a dependent repository's tool dependencies.
|
||||
has_repository_dependencies = util.string_as_bool( kwd.get( 'has_repository_dependencies', False ) )
|
||||
install_repository_dependencies = kwd.get( 'install_repository_dependencies', '' )
|
||||
# Every repository will be installed into the same tool panel section or all will be installed outside of any sections.
|
||||
@@ -825,7 +818,9 @@ class AdminToolshed( AdminGalaxy ):
|
||||
includes_tools_for_display_in_tool_panel = util.string_as_bool( kwd.get( 'includes_tools_for_display_in_tool_panel', False ) )
|
||||
includes_tool_dependencies = util.string_as_bool( kwd.get( 'includes_tool_dependencies', False ) )
|
||||
install_tool_dependencies = kwd.get( 'install_tool_dependencies', '' )
|
||||
encoded_repo_info_dicts = util.listify( kwd.get( 'encoded_repo_info_dicts', None ) )
|
||||
encoded_repo_info_dicts = kwd.get( 'encoded_repo_info_dicts', '' )
|
||||
if encoded_repo_info_dicts:
|
||||
encoded_repo_info_dicts = encoded_repo_info_dicts.split( encoding_util.encoding_sep )
|
||||
if not encoded_repo_info_dicts:
|
||||
# The request originated in the tool shed via a tool search.
|
||||
repository_ids = kwd.get( 'repository_ids', None )
|
||||
@@ -859,7 +854,6 @@ class AdminToolshed( AdminGalaxy ):
|
||||
installation_dict = dict( install_repository_dependencies=install_repository_dependencies,
|
||||
new_tool_panel_section=new_tool_panel_section,
|
||||
no_changes_checked=False,
|
||||
reinstalling=False,
|
||||
repo_info_dicts=repo_info_dicts,
|
||||
tool_panel_section=tool_panel_section,
|
||||
tool_path=tool_path,
|
||||
@@ -981,6 +975,8 @@ class AdminToolshed( AdminGalaxy ):
|
||||
install_tool_dependencies_check_box = CheckboxField( 'install_tool_dependencies', checked=install_tool_dependencies_check_box_checked )
|
||||
# Handle repository dependencies check box.
|
||||
install_repository_dependencies_check_box = CheckboxField( 'install_repository_dependencies', checked=True )
|
||||
encoded_repo_info_dicts = encoding_util.encoding_sep.join( encoded_repo_info_dicts )
|
||||
tool_shed_url = kwd[ 'tool_shed_url' ]
|
||||
if includes_tools_for_display_in_tool_panel:
|
||||
return trans.fill_template( '/admin/tool_shed_repository/select_tool_panel_section.mako',
|
||||
encoded_repo_info_dicts=encoded_repo_info_dicts,
|
||||
@@ -995,7 +991,7 @@ class AdminToolshed( AdminGalaxy ):
|
||||
shed_tool_conf=shed_tool_conf,
|
||||
shed_tool_conf_select_field=shed_tool_conf_select_field,
|
||||
tool_panel_section_select_field=tool_panel_section_select_field,
|
||||
tool_shed_url=kwd[ 'tool_shed_url' ],
|
||||
tool_shed_url=tool_shed_url,
|
||||
message=message,
|
||||
status=status )
|
||||
else:
|
||||
@@ -1014,7 +1010,7 @@ class AdminToolshed( AdminGalaxy ):
|
||||
shed_tool_conf=shed_tool_conf,
|
||||
shed_tool_conf_select_field=shed_tool_conf_select_field,
|
||||
tool_panel_section_select_field=tool_panel_section_select_field,
|
||||
tool_shed_url=kwd[ 'tool_shed_url' ],
|
||||
tool_shed_url=tool_shed_url,
|
||||
message=message,
|
||||
status=status )
|
||||
|
||||
@@ -1061,7 +1057,7 @@ class AdminToolshed( AdminGalaxy ):
|
||||
repository_clone_url,
|
||||
metadata,
|
||||
trans.model.ToolShedRepository.installation_status.NEW,
|
||||
tool_shed_repository.installed_changeset_revision,
|
||||
tool_shed_repository.changeset_revision,
|
||||
tool_shed_repository.owner,
|
||||
tool_shed_repository.dist_to_shed )
|
||||
ctx_rev = suc.get_ctx_rev( trans.app,
|
||||
@@ -1102,7 +1098,6 @@ class AdminToolshed( AdminGalaxy ):
|
||||
tool_path=tool_path,
|
||||
tool_shed_url=tool_shed_url,
|
||||
repo_info_dicts=repo_info_dicts,
|
||||
reinstalling=True,
|
||||
install_repository_dependencies=install_repository_dependencies,
|
||||
no_changes_checked=no_changes_checked,
|
||||
tool_panel_section=tool_panel_section,
|
||||
@@ -1320,7 +1315,6 @@ class AdminToolshed( AdminGalaxy ):
|
||||
missing_tool_dependencies = dependencies_for_repository_dict.get( 'missing_tool_dependencies', None )
|
||||
repository_name = dependencies_for_repository_dict.get( 'name', None )
|
||||
repository_owner = dependencies_for_repository_dict.get( 'repository_owner', None )
|
||||
|
||||
if installed_repository_dependencies or missing_repository_dependencies:
|
||||
has_repository_dependencies = True
|
||||
else:
|
||||
|
||||
@@ -53,6 +53,8 @@ class CloudController(BaseUIController):
|
||||
kps = ec2_conn.get_all_key_pairs()
|
||||
except EC2ResponseError, e:
|
||||
log.error("Problem starting an instance: %s\n%s" % (e, e.body))
|
||||
trans.response.status = 400
|
||||
return e.error_message or "Instance failure, but no specific error was detected. Please check your AWS Console."
|
||||
account_info['keypairs'] = [akp.name for akp in kps]
|
||||
#Existing Clusters
|
||||
s3_conn = S3Connection(key_id, secret, calling_format=OrdinaryCallingFormat())
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
import logging
|
||||
import mimetypes
|
||||
import os
|
||||
import string
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib
|
||||
import zipfile
|
||||
|
||||
from galaxy.web.base.controller import *
|
||||
from galaxy.web.framework.helpers import time_ago, iff, grids
|
||||
from galaxy import util, datatypes, web, model
|
||||
from galaxy.datatypes.display_applications.util import encode_dataset_user, decode_dataset_user
|
||||
from galaxy.util.sanitize_html import sanitize_html
|
||||
from galaxy import datatypes, eggs, model, util, web
|
||||
from galaxy.datatypes.display_applications.util import decode_dataset_user, encode_dataset_user
|
||||
from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings
|
||||
from galaxy.util import inflector
|
||||
from galaxy.model.item_attrs import *
|
||||
from galaxy.util.sanitize_html import sanitize_html
|
||||
from galaxy.web.base.controller import BaseUIController, ERROR, SUCCESS, url_for, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin
|
||||
from galaxy.web.framework.helpers import grids, iff, time_ago
|
||||
from galaxy.web.framework.helpers import to_unicode
|
||||
|
||||
import pkg_resources;
|
||||
pkg_resources.require( "Paste" )
|
||||
eggs.require( "Paste" )
|
||||
import paste.httpexceptions
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
tmpd = tempfile.mkdtemp()
|
||||
comptypes=[]
|
||||
ziptype = '32'
|
||||
@@ -40,7 +39,6 @@ except OSError:
|
||||
pass
|
||||
os.rmdir( tmpd )
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
error_report_template = """
|
||||
GALAXY TOOL ERROR REPORT
|
||||
@@ -66,6 +64,7 @@ ${message}
|
||||
-----------------------------------------------------------------------------
|
||||
job id: ${job_id}
|
||||
tool id: ${job_tool_id}
|
||||
job pid or drm id: ${job_runner_external_id}
|
||||
-----------------------------------------------------------------------------
|
||||
job command line:
|
||||
${job_command_line}
|
||||
@@ -204,7 +203,7 @@ class DatasetInterface( BaseUIController, UsesAnnotations, UsesHistoryMixin, Use
|
||||
trans.response.set_content_type( 'text/plain' )
|
||||
exit_code = ""
|
||||
try:
|
||||
job = _get_job_for_dataset( dataset_id )
|
||||
job = self._get_job_for_dataset( dataset_id )
|
||||
exit_code = job.exit_code
|
||||
except:
|
||||
exit_code = "Invalid dataset ID or you are not allowed to access this dataset"
|
||||
@@ -233,6 +232,7 @@ class DatasetInterface( BaseUIController, UsesAnnotations, UsesHistoryMixin, Use
|
||||
history_view_link=history_view_link,
|
||||
job_id=job.id,
|
||||
job_tool_id=job.tool_id,
|
||||
job_runner_external_id=job.job_runner_external_id,
|
||||
job_command_line=job.command_line,
|
||||
job_stderr=util.unicodify( job.stderr ),
|
||||
job_stdout=util.unicodify( job.stdout ),
|
||||
@@ -563,19 +563,15 @@ class DatasetInterface( BaseUIController, UsesAnnotations, UsesHistoryMixin, Use
|
||||
@web.expose
|
||||
def imp( self, trans, dataset_id=None, **kwd ):
|
||||
""" Import another user's dataset via a shared URL; dataset is added to user's current history. """
|
||||
msg = ""
|
||||
|
||||
# Set referer message.
|
||||
referer = trans.request.referer
|
||||
if referer is not "":
|
||||
referer_message = "<a href='%s'>return to the previous page</a>" % referer
|
||||
else:
|
||||
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
|
||||
|
||||
# Error checking.
|
||||
if not dataset_id:
|
||||
return trans.show_error_message( "You must specify a dataset to import. You can %s." % referer_message, use_panels=True )
|
||||
|
||||
# Do import.
|
||||
cur_history = trans.get_history( create=True )
|
||||
status, message = self._copy_datasets( trans, [ dataset_id ], [ cur_history ], imported=True )
|
||||
@@ -615,7 +611,7 @@ class DatasetInterface( BaseUIController, UsesAnnotations, UsesHistoryMixin, Use
|
||||
return trans.show_error_message( "The specified dataset does not exist." )
|
||||
|
||||
# Rate dataset.
|
||||
dataset_rating = self.rate_item( rate_item, trans.get_user(), dataset, rating )
|
||||
self.rate_item( trans.sa_session, trans.get_user(), dataset, rating )
|
||||
|
||||
return self.get_ave_item_rating_data( trans.sa_session, dataset )
|
||||
|
||||
@@ -705,10 +701,9 @@ class DatasetInterface( BaseUIController, UsesAnnotations, UsesHistoryMixin, Use
|
||||
if 'display_url' not in kwd or 'redirect_url' not in kwd:
|
||||
return trans.show_error_message( 'Invalid parameters specified for "display at" link, please contact a Galaxy administrator' )
|
||||
try:
|
||||
redirect_url = kwd['redirect_url'] % urllib.quote_plus( kwd['display_url'] )
|
||||
redirect_url = kwd['redirect_url'] % urllib.quote_plus( kwd['display_url'] )
|
||||
except:
|
||||
redirect_url = kwd['redirect_url'] # not all will need custom text
|
||||
current_user_roles = trans.get_current_user_roles()
|
||||
redirect_url = kwd['redirect_url'] # not all will need custom text
|
||||
if trans.app.security_agent.dataset_is_public( data.dataset ):
|
||||
return trans.response.send_redirect( redirect_url ) # anon access already permitted by rbac
|
||||
if self._can_access_dataset( trans, data ):
|
||||
@@ -809,7 +804,6 @@ class DatasetInterface( BaseUIController, UsesAnnotations, UsesHistoryMixin, Use
|
||||
id = None
|
||||
try:
|
||||
id = trans.app.security.decode_id( dataset_id )
|
||||
history = trans.get_history()
|
||||
hda = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id )
|
||||
assert hda, 'Invalid HDA: %s' % id
|
||||
# Walk up parent datasets to find the containing history
|
||||
@@ -856,7 +850,7 @@ class DatasetInterface( BaseUIController, UsesAnnotations, UsesHistoryMixin, Use
|
||||
hda.mark_undeleted()
|
||||
trans.sa_session.flush()
|
||||
trans.log_event( "Dataset id %s has been undeleted" % str(id) )
|
||||
except Exception, e:
|
||||
except Exception:
|
||||
msg = 'HDA undeletion failed (encoded: %s, decoded: %s)' % ( dataset_id, id )
|
||||
log.exception( msg )
|
||||
trans.log_event( msg )
|
||||
@@ -927,7 +921,7 @@ class DatasetInterface( BaseUIController, UsesAnnotations, UsesHistoryMixin, Use
|
||||
except:
|
||||
log.exception( 'Unable to purge dataset (%s) on purge of HDA (%s):' % ( hda.dataset.id, hda.id ) )
|
||||
trans.sa_session.flush()
|
||||
except Exception, e:
|
||||
except Exception:
|
||||
msg = 'HDA purge failed (encoded: %s, decoded: %s)' % ( dataset_id, id )
|
||||
log.exception( msg )
|
||||
trans.log_event( msg )
|
||||
@@ -1038,7 +1032,6 @@ class DatasetInterface( BaseUIController, UsesAnnotations, UsesHistoryMixin, Use
|
||||
|
||||
@web.expose
|
||||
def copy_datasets( self, trans, source_history=None, source_dataset_ids="", target_history_id=None, target_history_ids="", new_history_name="", do_copy=False, **kwd ):
|
||||
params = util.Params( kwd )
|
||||
user = trans.get_user()
|
||||
if source_history is not None:
|
||||
history = self.get_history(trans, source_history)
|
||||
|
||||
@@ -309,7 +309,6 @@ class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesI
|
||||
# If deleting the current history, make a new current.
|
||||
if history == trans.get_history():
|
||||
deleted_current = True
|
||||
trans.get_or_create_default_history()
|
||||
trans.log_event( "History (%s) marked as deleted" % history.name )
|
||||
n_deleted += 1
|
||||
if purge and trans.app.config.allow_user_dataset_purge:
|
||||
@@ -339,6 +338,8 @@ class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesI
|
||||
part += " but the datasets were not removed from disk because that feature is not enabled in this Galaxy instance"
|
||||
message_parts.append( "%s. " % part )
|
||||
if deleted_current:
|
||||
#note: this needs to come after commits above or will use an empty history that was deleted above
|
||||
trans.get_or_create_default_history()
|
||||
message_parts.append( "Your active history was deleted, a new empty history is now active. " )
|
||||
status = INFO
|
||||
return ( status, " ".join( message_parts ) )
|
||||
@@ -626,10 +627,11 @@ class HistoryController( BaseUIController, SharableMixin, UsesAnnotations, UsesI
|
||||
else:
|
||||
assert history.user == trans.user
|
||||
# Rename
|
||||
history.name = sanitize_html( new_name )
|
||||
new_name = sanitize_html( new_name )
|
||||
history.name = new_name
|
||||
trans.sa_session.add( history )
|
||||
trans.sa_session.flush()
|
||||
return history.name
|
||||
return new_name
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "use Galaxy histories" )
|
||||
|
||||
@@ -61,6 +61,7 @@ class ToolRunner( BaseUIController ):
|
||||
if not tool:
|
||||
log.error( "index 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 tool.require_login and not trans.user:
|
||||
message = "You must be logged in to use this tool."
|
||||
@@ -97,22 +98,6 @@ class ToolRunner( BaseUIController ):
|
||||
form_input_auto_focus=True,
|
||||
**vars )
|
||||
|
||||
@web.expose
|
||||
def display_tool_help_image_in_repository( self, trans, **kwd ):
|
||||
repository_id = kwd.get( 'repository_id', None )
|
||||
image_file = kwd.get( 'image_file', None )
|
||||
if repository_id and image_file:
|
||||
repository = suc.get_tool_shed_repository_by_id( trans, repository_id )
|
||||
repo_files_dir = os.path.join( repository.repo_files_directory( trans.app ) )
|
||||
default_path = os.path.abspath( os.path.join( repo_files_dir, 'static', 'images', image_file ) )
|
||||
if os.path.exists( default_path ):
|
||||
return open( default_path, 'r' )
|
||||
else:
|
||||
path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, image_file )
|
||||
if os.path.exists( path_to_file ):
|
||||
return open( path_to_file, 'r' )
|
||||
return None
|
||||
|
||||
@web.expose
|
||||
def rerun( self, trans, id=None, from_noframe=None, **kwd ):
|
||||
"""
|
||||
|
||||
@@ -8,6 +8,7 @@ import os
|
||||
import socket
|
||||
import string
|
||||
import random
|
||||
import urllib
|
||||
from galaxy import web
|
||||
from galaxy import util, model
|
||||
from galaxy.model.orm import and_
|
||||
@@ -17,6 +18,8 @@ from galaxy.web import url_for
|
||||
from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin
|
||||
from galaxy.web.form_builder import CheckboxField, build_select_field
|
||||
from galaxy.web.framework.helpers import time_ago, grids
|
||||
from datetime import datetime, timedelta
|
||||
from galaxy.util import hash_util
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
@@ -147,7 +150,14 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
|
||||
if trans.user:
|
||||
if user_openid.user and user_openid.user.id != trans.user.id:
|
||||
message = "The OpenID <strong>%s</strong> is already associated with another Galaxy account, <strong>%s</strong>. Please disassociate it from that account before attempting to associate it with a new account." % ( display_identifier, user_openid.user.email )
|
||||
status = "error"
|
||||
if not trans.user.active and trans.app.config.user_activation_on: # Account activation is ON and the user is INACTIVE.
|
||||
if ( trans.app.config.activation_grace_period != 0 ): # grace period is ON
|
||||
if self.is_outside_grace_period( trans, trans.user.create_time ): # User is outside the grace period. Login is disabled and he will have the activation email resent.
|
||||
message, status = self.resend_verification_email( trans, trans.user.email, trans.user.username )
|
||||
else: # User is within the grace period, let him log in.
|
||||
pass
|
||||
else: # Grace period is off. Login is disabled and user will have the activation email resent.
|
||||
message, status = self.resend_verification_email( trans, trans.user.email, trans.user.username )
|
||||
elif not user_openid.user or user_openid.user == trans.user:
|
||||
if openid_provider_obj.id:
|
||||
user_openid.provider = openid_provider_obj.id
|
||||
@@ -282,7 +292,7 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
|
||||
subscribe_checked = CheckboxField.is_checked( subscribe )
|
||||
error = ''
|
||||
if not trans.app.config.allow_user_creation and not trans.user_is_admin():
|
||||
error = 'User registration is disabled. Please contact your Galaxy administrator for an account.'
|
||||
error = 'User registration is disabled. Please contact your local Galaxy administrator for an account.'
|
||||
else:
|
||||
# Check email and password validity
|
||||
error = self.__validate( trans, params, email, password, confirm, username )
|
||||
@@ -465,36 +475,101 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
|
||||
openid_providers=trans.app.openid_providers,
|
||||
form_input_auto_focus=True,
|
||||
active_view="user" )
|
||||
|
||||
def __validate_login( self, trans, **kwd ):
|
||||
"""
|
||||
Function validates numerous cases that might happen during the login time.
|
||||
"""
|
||||
message = kwd.get( 'message', '' )
|
||||
status = kwd.get( 'status', 'done' )
|
||||
status = kwd.get( 'status', 'error' )
|
||||
email = kwd.get( 'email', '' )
|
||||
password = kwd.get( 'password', '' )
|
||||
username = kwd.get( 'username', '' )
|
||||
redirect = kwd.get( 'redirect', trans.request.referer ).strip()
|
||||
success = False
|
||||
user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email==email ).first()
|
||||
if not user:
|
||||
message = "No such user (please note that login is case sensitive)"
|
||||
status = 'error'
|
||||
elif user.deleted:
|
||||
message = "This account has been marked deleted, contact your Galaxy administrator to restore the account."
|
||||
status = 'error'
|
||||
message = "This account has been marked deleted, contact your local Galaxy administrator to restore the account."
|
||||
if trans.app.config.error_email_to is not None:
|
||||
message += ' Contact: %s' % trans.app.config.error_email_to
|
||||
elif user.external:
|
||||
message = "This account was created for use with an external authentication method, contact your local Galaxy administrator to activate it."
|
||||
status = 'error'
|
||||
if trans.app.config.error_email_to is not None:
|
||||
message += ' Contact: %s' % trans.app.config.error_email_to
|
||||
elif not user.check_password( password ):
|
||||
message = "Invalid password"
|
||||
elif trans.app.config.user_activation_on and not user.active: # activation is ON and the user is INACTIVE
|
||||
if ( trans.app.config.activation_grace_period != 0 ): # grace period is ON
|
||||
if self.is_outside_grace_period( trans, user.create_time ): # User is outside the grace period. Login is disabled and he will have the activation email resent.
|
||||
message, status = self.resend_verification_email( trans, email, username )
|
||||
else: # User is within the grace period, let him log in.
|
||||
message, success, status = self.proceed_login( trans, user, redirect )
|
||||
else: # Grace period is off. Login is disabled and user will have the activation email resent.
|
||||
message, status = self.resend_verification_email( trans, email, username )
|
||||
else: # activation is OFF
|
||||
message, success, status = self.proceed_login( trans, user, redirect )
|
||||
return ( message, status, user, success )
|
||||
|
||||
def proceed_login ( self, trans, user, redirect ):
|
||||
"""
|
||||
Function processes user login. It is called in case all the login requirements are valid.
|
||||
"""
|
||||
message = ''
|
||||
trans.handle_user_login( user )
|
||||
if trans.webapp.name == 'galaxy':
|
||||
trans.log_event( "User logged in" )
|
||||
message = 'You are now logged in as %s.<br>You can <a target="_top" href="%s">go back to the page you were visiting</a> or <a target="_top" href="%s">go to the home page</a>.' % \
|
||||
( user.email, redirect, url_for( '/' ) )
|
||||
if trans.app.config.require_login:
|
||||
message += ' <a target="_top" href="%s">Click here</a> to continue to the home page.' % web.url_for( controller="root", action="welcome" )
|
||||
success = True
|
||||
status = 'done'
|
||||
return message, success, status
|
||||
|
||||
@web.expose
|
||||
def resend_verification ( self, trans ):
|
||||
"""
|
||||
Exposed function for use outside of the class. E.g. when user click on the resend link in the masthead.
|
||||
"""
|
||||
message, status = self.resend_verification_email( trans, None, None )
|
||||
if status == 'done':
|
||||
return trans.show_ok_message( message )
|
||||
else:
|
||||
return trans.show_error_message( message )
|
||||
|
||||
def resend_verification_email( self, trans, email, username ):
|
||||
"""
|
||||
Function resends the verification email in case user wants to log in with an inactive account or he clicks the resend link.
|
||||
"""
|
||||
if email is None: # User is coming from outside registration form, load email from trans
|
||||
email = trans.user.email
|
||||
if username is None: # User is coming from outside registration form, load email from trans
|
||||
username = trans.user.username
|
||||
is_activation_sent = self.send_verification_email( trans, email, username)
|
||||
if is_activation_sent:
|
||||
message = 'This account has not been activated yet. The activation link has been sent again. Please check your email address <b>%s</b> including the spam/trash folder.<br><a target="_top" href="%s">Return to the home page</a>.' % ( email, url_for( '/' ) )
|
||||
status = 'error'
|
||||
else:
|
||||
trans.handle_user_login( user )
|
||||
if trans.webapp.name == 'galaxy':
|
||||
trans.log_event( "User logged in" )
|
||||
message = 'You are now logged in as %s.<br>You can <a target="_top" href="%s">go back to the page you were visiting</a> or <a target="_top" href="%s">go to the home page</a>.' % \
|
||||
( user.email, redirect, url_for( '/' ) )
|
||||
if trans.app.config.require_login:
|
||||
message += ' <a target="_top" href="%s">Click here</a> to continue to the home page.' % web.url_for( controller="root", action="welcome" )
|
||||
success = True
|
||||
return ( message, status, user, success )
|
||||
message = 'This account has not been activated yet but we are unable to send the activation link. Please contact your local Galaxy administrator.<br><a target="_top" href="%s">Return to the home page</a>.' % url_for( '/' )
|
||||
status = 'error'
|
||||
if trans.app.config.error_email_to is not None:
|
||||
message += '<br>Error contact: %s' % trans.app.config.error_email_to
|
||||
return message, status
|
||||
|
||||
def is_outside_grace_period ( self, trans, create_time ):
|
||||
"""
|
||||
Function checks whether the user is outside the config-defined grace period for inactive accounts.
|
||||
"""
|
||||
# Activation is forced and the user is not active yet. Check the grace period.
|
||||
activation_grace_period = trans.app.config.activation_grace_period
|
||||
# Default value is 3 hours.
|
||||
if activation_grace_period is None:
|
||||
activation_grace_period = 3
|
||||
delta = timedelta( hours = int( activation_grace_period ) )
|
||||
time_difference = datetime.utcnow() - create_time
|
||||
return ( time_difference > delta or activation_grace_period == 0 )
|
||||
|
||||
@web.expose
|
||||
def logout( self, trans, logout_all=False ):
|
||||
@@ -533,7 +608,9 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
|
||||
redirect = kwd.get( 'redirect', trans.request.referer ).strip()
|
||||
is_admin = cntrller == 'admin' and trans.user_is_admin
|
||||
if not trans.app.config.allow_user_creation and not trans.user_is_admin():
|
||||
message = 'User registration is disabled. Please contact your Galaxy administrator for an account.'
|
||||
message = 'User registration is disabled. Please contact your local Galaxy administrator for an account.'
|
||||
if trans.app.config.error_email_to is not None:
|
||||
message += ' Contact: %s' % trans.app.config.error_email_to
|
||||
status = 'error'
|
||||
else:
|
||||
if not refresh_frames:
|
||||
@@ -578,10 +655,13 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
|
||||
user_type_fd_id = trans.security.encode_id( user_type_form_definition.id )
|
||||
user_type_fd_id_select_field = self.__build_user_type_fd_id_select_field( trans, selected_value=user_type_fd_id )
|
||||
widgets = self.__get_widgets( trans, user_type_form_definition, user=None, **kwd )
|
||||
# Warning message that is shown on the registration page.
|
||||
registration_warning_message = trans.app.config.registration_warning_message
|
||||
else:
|
||||
user_type_fd_id_select_field = None
|
||||
user_type_form_definition = None
|
||||
widgets = []
|
||||
registration_warning_message = None
|
||||
return trans.fill_template( '/user/register.mako',
|
||||
cntrller=cntrller,
|
||||
email=email,
|
||||
@@ -594,6 +674,7 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
|
||||
redirect=redirect,
|
||||
redirect_url=redirect_url,
|
||||
refresh_frames=refresh_frames,
|
||||
registration_warning_message=registration_warning_message,
|
||||
message=message,
|
||||
status=status )
|
||||
|
||||
@@ -601,15 +682,21 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
|
||||
email = util.restore_text( kwd.get( 'email', '' ) )
|
||||
password = kwd.get( 'password', '' )
|
||||
username = util.restore_text( kwd.get( 'username', '' ) )
|
||||
message = kwd.get( 'message', '' )
|
||||
status = kwd.get( 'status', 'done' )
|
||||
is_admin = cntrller == 'admin' and trans.user_is_admin()
|
||||
user = trans.app.model.User( email=email )
|
||||
user.set_password_cleartext( password )
|
||||
user.username = username
|
||||
if trans.app.config.user_activation_on:
|
||||
user.active = False
|
||||
else:
|
||||
user.active = True # Activation is off, every new user is active by default.
|
||||
trans.sa_session.add( user )
|
||||
trans.sa_session.flush()
|
||||
trans.app.security_agent.create_private_user_role( user )
|
||||
error = ''
|
||||
success = True
|
||||
if trans.webapp.name == 'galaxy':
|
||||
# We set default user permissions, before we log in and set the default history permissions
|
||||
trans.app.security_agent.user_set_default_permissions( user,
|
||||
@@ -633,7 +720,7 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
|
||||
if subscribe_checked:
|
||||
# subscribe user to email list
|
||||
if trans.app.config.smtp_server is None:
|
||||
error = "Now logged in as " + user.email + ". However, subscribing to the mailing list has failed because mail is not configured for this Galaxy instance."
|
||||
error = "Now logged in as " + user.email + ". However, subscribing to the mailing list has failed because mail is not configured for this Galaxy instance. <br>Please contact your local Galaxy administrator."
|
||||
else:
|
||||
body = 'Join Mailing list.\n'
|
||||
to = trans.app.config.mailing_join_addr
|
||||
@@ -659,9 +746,92 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
|
||||
status = 'error'
|
||||
success = False
|
||||
else:
|
||||
message = 'Now logged in as %s.<br><a target="_top" href="%s">Return to the home page.</a>' % ( user.email, url_for( '/' ) )
|
||||
success = True
|
||||
if trans.webapp.name == 'galaxy' and trans.app.config.user_activation_on:
|
||||
is_activation_sent = self.send_verification_email( trans, email, username )
|
||||
if is_activation_sent:
|
||||
message = 'Now logged in as %s.<br>Verification email has been sent to your email address. Please verify it by clicking the activation link in the email.<br>Please check your spam/trash folder in case you cannot find the message.<br><a target="_top" href="%s">Return to the home page.</a>' % ( user.email, url_for( '/' ) )
|
||||
success = True
|
||||
else:
|
||||
message = 'Unable to send activation email, please contact your local Galaxy administrator.'
|
||||
if trans.app.config.error_email_to is not None:
|
||||
message += ' Contact: %s' % trans.app.config.error_email_to
|
||||
success = False
|
||||
else: # User activation is OFF, proceed without sending the activation email.
|
||||
message = 'Now logged in as %s.<br><a target="_top" href="%s">Return to the home page.</a>' % ( user.email, url_for( '/' ) )
|
||||
success = True
|
||||
return ( message, status, user, success )
|
||||
|
||||
def send_verification_email( self, trans, email, username ):
|
||||
"""
|
||||
Send the verification email containing the activation link to the user's email.
|
||||
"""
|
||||
if username is None:
|
||||
username = trans.user.username
|
||||
activation_link = self.prepare_activation_link( trans, email )
|
||||
|
||||
body = ("Hello %s,\n\n"
|
||||
"In order to complete the activation process for %s begun on %s at %s, please click on the following link to verify your account:\n\n"
|
||||
"%s \n\n"
|
||||
"By clicking on the above link and opening a Galaxy account you are also confirming that you have read and agreed to Galaxy's Terms and Conditions for use of this service (%s). This includes a quota limit of one account per user. Attempts to subvert this limit by creating multiple accounts or through any other method may result in termination of all associated accounts and data.\n\n"
|
||||
"Please contact us if you need help with your account at: %s. You can also browse resources available at: %s. \n\n"
|
||||
"More about the Galaxy Project can be found at galaxyproject.org\n\n"
|
||||
"Your Galaxy Team" % ( username, email, datetime.utcnow().strftime( "%D" ), trans.request.host, activation_link,trans.app.config.terms_url, trans.app.config.error_email_to, trans.app.config.instance_resource_url ))
|
||||
to = email
|
||||
frm = trans.app.config.activation_email
|
||||
subject = 'Galaxy Account Activation'
|
||||
try:
|
||||
util.send_mail( frm, to, subject, body, trans.app.config )
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def prepare_activation_link( self, trans, email ):
|
||||
"""
|
||||
Prepares the account activation link for the user.
|
||||
"""
|
||||
activation_token = self.get_activation_token( trans, email )
|
||||
activation_link = str( trans.request.host ) + url_for( controller='user', action='activate' ) + "?activation_token=" + str( activation_token ) + "&email=" + urllib.quote( email )
|
||||
return activation_link
|
||||
|
||||
def get_activation_token ( self, trans, email ):
|
||||
"""
|
||||
Checks for the activation token. Creates new activation token and stores it in the database if none found.
|
||||
"""
|
||||
user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email == email ).first()
|
||||
activation_token = user.activation_token
|
||||
if activation_token is None:
|
||||
activation_token = hash_util.new_secure_hash( str( random.getrandbits( 256 ) ) )
|
||||
user.activation_token = activation_token
|
||||
trans.sa_session.add( user )
|
||||
trans.sa_session.flush()
|
||||
return activation_token
|
||||
|
||||
@web.expose
|
||||
def activate( self, trans, **kwd ):
|
||||
"""
|
||||
Function checks whether token fits the user and then activates the user's account.
|
||||
"""
|
||||
params = util.Params( kwd, sanitize=False )
|
||||
email = urllib.unquote( params.get( 'email', None ) )
|
||||
activation_token = params.get( 'activation_token', None )
|
||||
|
||||
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" )
|
||||
else:
|
||||
# Find the user
|
||||
user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email==email ).first()
|
||||
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' )
|
||||
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
|
||||
|
||||
def __get_user_type_form_definition( self, trans, user=None, **kwd ):
|
||||
params = util.Params( kwd )
|
||||
if user and user.values:
|
||||
@@ -885,7 +1055,7 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
|
||||
@web.expose
|
||||
def reset_password( self, trans, email=None, **kwd ):
|
||||
if trans.app.config.smtp_server is None:
|
||||
return trans.show_error_message( "Mail is not configured for this Galaxy instance. Please contact an administrator." )
|
||||
return trans.show_error_message( "Mail is not configured for this Galaxy instance. Please contact your local Galaxy administrator." )
|
||||
message = util.restore_text( kwd.get( 'message', '' ) )
|
||||
status = 'done'
|
||||
if kwd.get( 'reset_password_button', False ):
|
||||
@@ -1042,7 +1212,7 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
|
||||
phone = util.restore_text( params.get( 'phone', '' ) )
|
||||
ok = True
|
||||
if not trans.app.config.allow_user_creation and not is_admin:
|
||||
return trans.show_error_message( 'User registration is disabled. Please contact your Galaxy administrator for an account.' )
|
||||
return trans.show_error_message( 'User registration is disabled. Please contact your local Galaxy administrator for an account.' )
|
||||
if params.get( 'new_address_button', False ):
|
||||
if not short_desc:
|
||||
ok = False
|
||||
|
||||
@@ -698,10 +698,10 @@ class VisualizationController( BaseUIController, SharableMixin, UsesAnnotations,
|
||||
registry = trans.app.visualizations_registry
|
||||
if not registry:
|
||||
raise HTTPNotFound( 'No visualization registry (possibly disabled in universe_wsgi.ini)' )
|
||||
if visualization_name not in registry.listings:
|
||||
if visualization_name not in registry.plugins:
|
||||
raise HTTPNotFound( 'Unknown or invalid visualization: ' + visualization_name )
|
||||
# or redirect to list?
|
||||
registry_listing = registry.listings[ visualization_name ]
|
||||
plugin = registry.plugins[ visualization_name ]
|
||||
|
||||
returned = None
|
||||
try:
|
||||
@@ -711,8 +711,8 @@ class VisualizationController( BaseUIController, SharableMixin, UsesAnnotations,
|
||||
resources = registry.query_dict_to_resources( trans, self, visualization_name, kwargs )
|
||||
|
||||
# look up template and render
|
||||
template_path = registry_listing[ 'template' ]
|
||||
returned = registry.fill_template( trans, template_path,
|
||||
template_path = plugin.config[ 'template' ]
|
||||
returned = registry.fill_template( trans, plugin, template_path,
|
||||
visualization_name=visualization_name, query_args=kwargs,
|
||||
embedded=embedded, shared_vars={}, **resources )
|
||||
#NOTE: passing *unparsed* kwargs as query_args
|
||||
|
||||
@@ -2,6 +2,7 @@ import os
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from galaxy.web.base.controller import BaseUIController, web
|
||||
from decimal import Decimal
|
||||
from galaxy import model, util
|
||||
from galaxy.model.orm import and_, desc, eagerload
|
||||
log = logging.getLogger( __name__ )
|
||||
@@ -39,7 +40,8 @@ class System( BaseUIController ):
|
||||
userless_histories_days=userless_histories_days,
|
||||
deleted_histories_days=deleted_histories_days,
|
||||
deleted_datasets_days=deleted_datasets_days,
|
||||
message=message )
|
||||
message=message,
|
||||
nice_size=nice_size )
|
||||
def userless_histories( self, trans, **kwd ):
|
||||
"""The number of userless histories and associated datasets that have not been updated for the specified number of days."""
|
||||
params = util.Params( kwd )
|
||||
@@ -89,7 +91,8 @@ class System( BaseUIController ):
|
||||
except:
|
||||
pass
|
||||
history_count += 1
|
||||
message = "%d histories ( including a total of %d datasets ) were deleted more than %d days ago, but have not yet been purged. Disk space: " %( history_count, dataset_count, deleted_histories_days ) + str( disk_space )
|
||||
message = "%d histories ( including a total of %d datasets ) were deleted more than %d days ago, but have not yet been purged, " \
|
||||
"disk space: %s." % ( history_count, dataset_count, deleted_histories_days, nice_size( disk_space, True ) )
|
||||
else:
|
||||
message = "Enter the number of days."
|
||||
return str( deleted_histories_days ), message
|
||||
@@ -111,8 +114,8 @@ class System( BaseUIController ):
|
||||
disk_space += dataset.file_size
|
||||
except:
|
||||
pass
|
||||
message = str( dataset_count ) + " datasets were deleted more than " + str( deleted_datasets_days ) + \
|
||||
" days ago, but have not yet been purged, disk space: " + nice_size( disk_space ) + "."
|
||||
message = "%d datasets were deleted more than %d days ago, but have not yet been purged," \
|
||||
" disk space: %s." % ( dataset_count, deleted_datasets_days, nice_size( disk_space, True ))
|
||||
else:
|
||||
message = "Enter the number of days."
|
||||
return str( deleted_datasets_days ), message
|
||||
@@ -178,17 +181,23 @@ class System( BaseUIController ):
|
||||
.order_by( desc( model.Dataset.table.c.file_size ) )
|
||||
return file_path, disk_usage, datasets, file_size_str
|
||||
|
||||
def nice_size( size ):
|
||||
def nice_size(size, include_bytes=False):
|
||||
"""Returns a readably formatted string with the size"""
|
||||
words = [ 'bytes', 'Kb', 'Mb', 'Gb' ]
|
||||
niced = False
|
||||
nice_string = "%s bytes" % size
|
||||
try:
|
||||
size = float( size )
|
||||
nsize = Decimal(size)
|
||||
for x in ['bytes','KB','MB','GB']:
|
||||
if nsize.compare(Decimal("1024.0")) == Decimal("-1"):
|
||||
nice_string = "%3.1f %s" % (nsize, x)
|
||||
niced = True
|
||||
break
|
||||
nsize /= Decimal("1024.0")
|
||||
if not niced:
|
||||
nice_string = "%3.1f %s" % (nsize, 'TB')
|
||||
niced = True
|
||||
if include_bytes and x != 'bytes':
|
||||
nice_string = "%s (%s bytes)" % (nice_string, size)
|
||||
except:
|
||||
return '??? bytes'
|
||||
for ind, word in enumerate( words ):
|
||||
step = 1024 ** ( ind + 1 )
|
||||
if step > size:
|
||||
size = size / float( 1024 ** ind )
|
||||
out = "%.1f %s" % ( size, word )
|
||||
return out
|
||||
return '??? bytes'
|
||||
pass
|
||||
return nice_string
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import logging
|
||||
from galaxy.web.framework.helpers import time_ago
|
||||
from galaxy import eggs
|
||||
from galaxy import web
|
||||
from galaxy import util
|
||||
from galaxy.web.base.controller import BaseAPIController
|
||||
import tool_shed.util.shed_util_common as suc
|
||||
from tool_shed.galaxy_install import repository_util
|
||||
|
||||
from galaxy import eggs
|
||||
import pkg_resources
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
pkg_resources.require( 'mercurial' )
|
||||
from mercurial import hg, ui, commands
|
||||
from mercurial import commands
|
||||
from mercurial import hg
|
||||
from mercurial import ui
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
@@ -73,6 +74,7 @@ class RepositoriesController( BaseAPIController ):
|
||||
"changeset_revision": "3a08cc21466f",
|
||||
"downloadable": true,
|
||||
"has_repository_dependencies": false,
|
||||
"has_repository_dependencies_only_if_compiling_contained_td": false,
|
||||
"id": "f9cad7b01a472135",
|
||||
"includes_datatypes": false,
|
||||
"includes_tool_dependencies": false,
|
||||
@@ -125,7 +127,8 @@ class RepositoriesController( BaseAPIController ):
|
||||
action='show',
|
||||
id=encoded_repository_metadata_id )
|
||||
# Get the repo_info_dict for installing the repository.
|
||||
repo_info_dict, includes_tools, includes_tool_dependencies, includes_tools_for_display_in_tool_panel, has_repository_dependencies = \
|
||||
repo_info_dict, includes_tools, includes_tool_dependencies, includes_tools_for_display_in_tool_panel, \
|
||||
has_repository_dependencies, has_repository_dependencies_only_if_compiling_contained_td = \
|
||||
repository_util.get_repo_info_dict( trans, encoded_repository_id, changeset_revision )
|
||||
return repository_dict, repository_metadata_dict, repo_info_dict
|
||||
else:
|
||||
|
||||
@@ -69,8 +69,12 @@ def app_factory( global_conf, **kwargs ):
|
||||
webapp.add_route( '/view/{owner}', controller='repository', action='sharable_owner' )
|
||||
webapp.add_route( '/view/{owner}/{name}', controller='repository', action='sharable_repository' )
|
||||
webapp.add_route( '/view/{owner}/{name}/{changeset_revision}', controller='repository', action='sharable_repository_revision' )
|
||||
# The following route will handle displaying tool shelp images for tools contained in repositories.
|
||||
webapp.add_route( '/repository/static/images/:repository_id/:image_file', controller='repository', action='display_tool_help_image_in_repository', repository_id=None, image_file=None )
|
||||
# Handle displaying tool help images and README file images for tools contained in repositories.
|
||||
webapp.add_route( '/repository/static/images/:repository_id/:image_file',
|
||||
controller='repository',
|
||||
action='display_image_in_repository',
|
||||
repository_id=None,
|
||||
image_file=None )
|
||||
webapp.add_route( '/:controller/:action', action='index' )
|
||||
webapp.add_route( '/:action', controller='repository', action='index' )
|
||||
webapp.add_route( '/repos/*path_info', controller='hg', action='handle_request', path_info='/' )
|
||||
|
||||
@@ -62,6 +62,13 @@ class Configuration( object ):
|
||||
self.use_tool_dependencies = False
|
||||
self.update_integrated_tool_panel = False
|
||||
self.use_remote_user = string_as_bool( kwargs.get( "use_remote_user", "False" ) )
|
||||
self.user_activation_on = kwargs.get( 'user_activation_on', None )
|
||||
self.activation_grace_period = kwargs.get( 'activation_grace_period', None )
|
||||
self.inactivity_box_content = kwargs.get( 'inactivity_box_content', None )
|
||||
self.registration_warning_message = kwargs.get( 'registration_warning_message', None )
|
||||
self.terms_url = kwargs.get( 'terms_url', None )
|
||||
self.blacklist_location = kwargs.get( 'blacklist_file', None )
|
||||
self.blacklist_content = None
|
||||
self.remote_user_maildomain = kwargs.get( "remote_user_maildomain", None )
|
||||
self.remote_user_logout_href = kwargs.get( "remote_user_logout_href", None )
|
||||
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
|
||||
|
||||
@@ -38,7 +38,7 @@ import tool_shed.grids.util as grids_util
|
||||
import tool_shed.repository_types.util as rt_util
|
||||
|
||||
from galaxy import eggs
|
||||
eggs.require('mercurial')
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
from mercurial import commands
|
||||
from mercurial import hg
|
||||
@@ -416,7 +416,8 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
repository_id = kwd.get( 'id', None )
|
||||
if repository_id:
|
||||
repository = suc.get_repository_in_tool_shed( trans, repository_id )
|
||||
kwd[ 'user_id' ] = trans.security.encode_id( repository.user.id )
|
||||
user_id = trans.security.encode_id( repository.user.id )
|
||||
kwd[ 'user_id' ] = user_id
|
||||
else:
|
||||
# The user selected a repository revision which results in a refresh_on_change.
|
||||
selected_changeset_revision, repository = suc.get_repository_from_refresh_on_change( trans, **kwd )
|
||||
@@ -665,7 +666,7 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
# Update repository files for browsing.
|
||||
suc.update_repository( repo )
|
||||
changeset_revision = repository.tip( trans.app )
|
||||
metadata = self.get_metadata( trans, id, changeset_revision )
|
||||
metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision, metadata_only=True )
|
||||
repository_type_select_field = rt_util.build_repository_type_select_field( trans, repository=repository )
|
||||
return trans.fill_template( '/webapps/tool_shed/repository/browse_repository.mako',
|
||||
repository=repository,
|
||||
@@ -896,7 +897,10 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
message = kwd.get( 'message', '' )
|
||||
status = kwd.get( 'status', 'done' )
|
||||
repository = suc.get_repository_in_tool_shed( trans, id )
|
||||
metadata = self.get_metadata( trans, id, repository.tip( trans.app ) )
|
||||
metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
|
||||
id,
|
||||
repository.tip( trans.app ),
|
||||
metadata_only=True )
|
||||
if trans.user and trans.user.email:
|
||||
return trans.fill_template( "/webapps/tool_shed/repository/contact_owner.mako",
|
||||
repository=repository,
|
||||
@@ -973,9 +977,9 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
flush_needed = True
|
||||
if flush_needed:
|
||||
trans.sa_session.flush()
|
||||
message = "Repository '%s' has been created." % repository.name
|
||||
message = "Repository <b>%s</b> has been created." % str( repository.name )
|
||||
trans.response.send_redirect( web.url_for( controller='repository',
|
||||
action='view_repository',
|
||||
action='manage_repository',
|
||||
message=message,
|
||||
id=trans.security.encode_id( repository.id ) ) )
|
||||
repository_type_select_field = rt_util.build_repository_type_select_field( trans )
|
||||
@@ -1013,6 +1017,38 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
message=message,
|
||||
status=status ) )
|
||||
|
||||
@web.expose
|
||||
def display_image_in_repository( self, trans, **kwd ):
|
||||
"""
|
||||
Open an image file that is contained in repository or that is referenced by a URL for display. The image can be defined in
|
||||
either a README.rst file contained in the repository or the help section of a Galaxy tool config that is contained in the repository.
|
||||
The following image definitions are all supported. The former $PATH_TO_IMAGES is no longer required, and is now ignored.
|
||||
.. image:: https://raw.github.com/galaxy/some_image.png
|
||||
.. image:: $PATH_TO_IMAGES/some_image.png
|
||||
.. image:: /static/images/some_image.gif
|
||||
.. image:: some_image.jpg
|
||||
.. image:: /deep/some_image.png
|
||||
"""
|
||||
repository_id = kwd.get( 'repository_id', None )
|
||||
relative_path_to_image_file = kwd.get( 'image_file', None )
|
||||
if repository_id and relative_path_to_image_file:
|
||||
repository = suc.get_repository_in_tool_shed( trans, repository_id )
|
||||
if repository:
|
||||
repo_files_dir = repository.repo_path( trans.app )
|
||||
path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
|
||||
if os.path.exists( path_to_file ):
|
||||
file_name = os.path.basename( relative_path_to_image_file )
|
||||
try:
|
||||
extension = file_name.split( '.' )[ -1 ]
|
||||
except Exception, e:
|
||||
extension = None
|
||||
if extension:
|
||||
mimetype = trans.app.datatypes_registry.get_mimetype_by_extension( extension )
|
||||
if mimetype:
|
||||
trans.response.set_content_type( mimetype )
|
||||
return open( path_to_file, 'r' )
|
||||
return None
|
||||
|
||||
@web.expose
|
||||
def display_tool( self, trans, repository_id, tool_config, changeset_revision, **kwd ):
|
||||
message = kwd.get( 'message', '' )
|
||||
@@ -1022,7 +1058,10 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
if message:
|
||||
status = 'error'
|
||||
tool_state = tool_util.new_state( trans, tool, invalid=False )
|
||||
metadata = self.get_metadata( trans, repository_id, changeset_revision )
|
||||
metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
|
||||
repository_id,
|
||||
changeset_revision,
|
||||
metadata_only=True )
|
||||
try:
|
||||
return trans.fill_template( "/webapps/tool_shed/repository/tool_form.mako",
|
||||
repository=repository,
|
||||
@@ -1050,22 +1089,6 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
message=message,
|
||||
status='error' ) )
|
||||
|
||||
@web.expose
|
||||
def display_tool_help_image_in_repository( self, trans, **kwd ):
|
||||
repository_id = kwd.get( 'repository_id', None )
|
||||
image_file = kwd.get( 'image_file', None )
|
||||
if repository_id and image_file:
|
||||
repository = suc.get_repository_in_tool_shed( trans, repository_id )
|
||||
repo_files_dir = os.path.join( repository.repo_path( trans.app ), repository.name )
|
||||
default_path = os.path.abspath( os.path.join( repo_files_dir, 'static', 'images', image_file ) )
|
||||
if os.path.exists( default_path ):
|
||||
return open( default_path, 'r' )
|
||||
else:
|
||||
path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, image_file )
|
||||
if os.path.exists( path_to_file ):
|
||||
return open( path_to_file, 'r' )
|
||||
return None
|
||||
|
||||
@web.expose
|
||||
def download( self, trans, repository_id, changeset_revision, file_type, **kwd ):
|
||||
"""Download an archive of the repository files compressed as zip, gz or bz2."""
|
||||
@@ -1343,32 +1366,36 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
def get_changeset_revision_and_ctx_rev( self, trans, **kwd ):
|
||||
"""Handle a request from a local Galaxy instance to retrieve the changeset revision hash to which an installed repository can be updated."""
|
||||
def has_galaxy_utilities( repository_metadata ):
|
||||
includes_data_managers = False
|
||||
includes_datatypes = False
|
||||
includes_tools = False
|
||||
includes_tools_for_display_in_tool_panel = False
|
||||
has_repository_dependencies = False
|
||||
includes_tool_dependencies = False
|
||||
includes_workflows = False
|
||||
has_galaxy_utilities_dict = dict( includes_data_managers=False,
|
||||
includes_datatypes=False,
|
||||
includes_tools=False,
|
||||
includes_tools_for_display_in_tool_panel=False,
|
||||
has_repository_dependencies=False,
|
||||
has_repository_dependencies_only_if_compiling_contained_td=False,
|
||||
includes_tool_dependencies=False,
|
||||
includes_workflows=False )
|
||||
if repository_metadata:
|
||||
includes_tools_for_display_in_tool_panel = repository_metadata.includes_tools_for_display_in_tool_panel
|
||||
metadata = repository_metadata.metadata
|
||||
if metadata:
|
||||
if 'data_manager' in metadata:
|
||||
includes_data_managers = True
|
||||
has_galaxy_utilities_dict[ 'includes_data_managers' ] = True
|
||||
if 'datatypes' in metadata:
|
||||
includes_datatypes = True
|
||||
has_galaxy_utilities_dict[ 'includes_datatypes' ] = True
|
||||
if 'tools' in metadata:
|
||||
includes_tools = True
|
||||
has_galaxy_utilities_dict[ 'includes_tools' ] = True
|
||||
if 'tool_dependencies' in metadata:
|
||||
includes_tool_dependencies = True
|
||||
if 'repository_dependencies' in metadata:
|
||||
has_repository_dependencies = True
|
||||
has_galaxy_utilities_dict[ 'includes_tool_dependencies' ] = True
|
||||
repository_dependencies_dict = metadata.get( 'repository_dependencies', {} )
|
||||
repository_dependencies = repository_dependencies_dict.get( 'repository_dependencies', [] )
|
||||
has_repository_dependencies, has_repository_dependencies_only_if_compiling_contained_td = \
|
||||
suc.get_repository_dependency_types( repository_dependencies )
|
||||
has_galaxy_utilities_dict[ 'has_repository_dependencies' ] = has_repository_dependencies
|
||||
has_galaxy_utilities_dict[ 'has_repository_dependencies_only_if_compiling_contained_td' ] = \
|
||||
has_repository_dependencies_only_if_compiling_contained_td
|
||||
if 'workflows' in metadata:
|
||||
includes_workflows = True
|
||||
return includes_data_managers, includes_datatypes, includes_tools, includes_tools_for_display_in_tool_panel, includes_tool_dependencies, has_repository_dependencies, includes_workflows
|
||||
message = kwd.get( 'message', '' )
|
||||
status = kwd.get( 'status', 'done' )
|
||||
has_galaxy_utilities_dict[ 'includes_workflows' ] = True
|
||||
return has_galaxy_utilities_dict
|
||||
name = kwd.get( 'name', None )
|
||||
owner = kwd.get( 'owner', None )
|
||||
changeset_revision = kwd.get( 'changeset_revision', None )
|
||||
@@ -1376,8 +1403,15 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans,
|
||||
trans.security.encode_id( repository.id ),
|
||||
changeset_revision )
|
||||
includes_data_managers, includes_datatypes, includes_tools, includes_tools_for_display_in_tool_panel, includes_tool_dependencies, has_repository_dependencies, includes_workflows = \
|
||||
has_galaxy_utilities( repository_metadata )
|
||||
has_galaxy_utilities_dict = has_galaxy_utilities( repository_metadata )
|
||||
includes_data_managers = has_galaxy_utilities_dict[ 'includes_data_managers' ]
|
||||
includes_datatypes = has_galaxy_utilities_dict[ 'includes_datatypes' ]
|
||||
includes_tools = has_galaxy_utilities_dict[ 'includes_tools' ]
|
||||
includes_tools_for_display_in_tool_panel = has_galaxy_utilities_dict[ 'includes_tools_for_display_in_tool_panel' ]
|
||||
includes_tool_dependencies = has_galaxy_utilities_dict[ 'includes_tool_dependencies' ]
|
||||
has_repository_dependencies = has_galaxy_utilities_dict[ 'has_repository_dependencies' ]
|
||||
has_repository_dependencies_only_if_compiling_contained_td = has_galaxy_utilities_dict[ 'has_repository_dependencies_only_if_compiling_contained_td' ]
|
||||
includes_workflows = has_galaxy_utilities_dict[ 'includes_workflows' ]
|
||||
repo_dir = repository.repo_path( trans.app )
|
||||
repo = hg.repository( suc.get_configured_ui(), repo_dir )
|
||||
# Default to the received changeset revision and ctx_rev.
|
||||
@@ -1392,6 +1426,7 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel,
|
||||
includes_tool_dependencies=includes_tool_dependencies,
|
||||
has_repository_dependencies=has_repository_dependencies,
|
||||
has_repository_dependencies_only_if_compiling_contained_td=has_repository_dependencies_only_if_compiling_contained_td,
|
||||
includes_workflows=includes_workflows )
|
||||
if changeset_revision == repository.tip( trans.app ):
|
||||
# If changeset_revision is the repository tip, there are no additional updates.
|
||||
@@ -1407,6 +1442,7 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
for changeset in repo.changelog:
|
||||
includes_tools = False
|
||||
has_repository_dependencies = False
|
||||
has_repository_dependencies_only_if_compiling_contained_td = False
|
||||
changeset_hash = str( repo.changectx( changeset ) )
|
||||
ctx = suc.get_changectx_for_changeset( repo, changeset_hash )
|
||||
if update_to_changeset_hash:
|
||||
@@ -1414,8 +1450,15 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
trans.security.encode_id( repository.id ),
|
||||
changeset_hash )
|
||||
if update_to_repository_metadata:
|
||||
includes_data_managers, includes_datatypes, includes_tools, includes_tools_for_display_in_tool_panel, includes_tool_dependencies, has_repository_dependencies, includes_workflows = \
|
||||
has_galaxy_utilities( update_to_repository_metadata )
|
||||
has_galaxy_utilities_dict = has_galaxy_utilities( repository_metadata )
|
||||
includes_data_managers = has_galaxy_utilities_dict[ 'includes_data_managers' ]
|
||||
includes_datatypes = has_galaxy_utilities_dict[ 'includes_datatypes' ]
|
||||
includes_tools = has_galaxy_utilities_dict[ 'includes_tools' ]
|
||||
includes_tools_for_display_in_tool_panel = has_galaxy_utilities_dict[ 'includes_tools_for_display_in_tool_panel' ]
|
||||
includes_tool_dependencies = has_galaxy_utilities_dict[ 'includes_tool_dependencies' ]
|
||||
has_repository_dependencies = has_galaxy_utilities_dict[ 'has_repository_dependencies' ]
|
||||
has_repository_dependencies_only_if_compiling_contained_td = has_galaxy_utilities_dict[ 'has_repository_dependencies_only_if_compiling_contained_td' ]
|
||||
includes_workflows = has_galaxy_utilities_dict[ 'includes_workflows' ]
|
||||
# We found a RepositoryMetadata record.
|
||||
if changeset_hash == repository.tip( trans.app ):
|
||||
# The current ctx is the repository tip, so use it.
|
||||
@@ -1435,6 +1478,7 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
update_dict[ 'includes_tool_dependencies' ] = includes_tool_dependencies
|
||||
update_dict[ 'includes_workflows' ] = includes_workflows
|
||||
update_dict[ 'has_repository_dependencies' ] = has_repository_dependencies
|
||||
update_dict[ 'has_repository_dependencies_only_if_compiling_contained_td' ] = has_repository_dependencies_only_if_compiling_contained_td
|
||||
update_dict[ 'changeset_revision' ] = str( latest_changeset_revision )
|
||||
update_dict[ 'ctx_rev' ] = str( update_to_ctx.rev() )
|
||||
return encoding_util.tool_shed_encode( update_dict )
|
||||
@@ -1549,12 +1593,6 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
pubdate=strftime( '%a, %d %b %Y %H:%M:%S UT', gmtime() ),
|
||||
items=functional_test_results )
|
||||
|
||||
def get_metadata( self, trans, repository_id, changeset_revision ):
|
||||
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, repository_id, changeset_revision )
|
||||
if repository_metadata and repository_metadata.metadata:
|
||||
return repository_metadata.metadata
|
||||
return None
|
||||
|
||||
@web.json
|
||||
def get_readme_files( self, trans, **kwd ):
|
||||
"""
|
||||
@@ -1572,7 +1610,7 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
if repository_metadata:
|
||||
metadata = repository_metadata.metadata
|
||||
if metadata:
|
||||
return readme_util.build_readme_files_dict( repository_metadata.metadata )
|
||||
return readme_util.build_readme_files_dict( trans, repository, changeset_revision, repository_metadata.metadata )
|
||||
return {}
|
||||
|
||||
@web.json
|
||||
@@ -1611,14 +1649,18 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
includes_tools = False
|
||||
includes_tools_for_display_in_tool_panel = False
|
||||
has_repository_dependencies = False
|
||||
has_repository_dependencies_only_if_compiling_contained_td = False
|
||||
includes_tool_dependencies = False
|
||||
repo_info_dicts = []
|
||||
for tup in zip( util.listify( repository_ids ), util.listify( changeset_revisions ) ):
|
||||
repository_id, changeset_revision = tup
|
||||
repo_info_dict, cur_includes_tools, cur_includes_tool_dependencies, cur_includes_tools_for_display_in_tool_panel, cur_has_repository_dependencies = \
|
||||
repo_info_dict, cur_includes_tools, cur_includes_tool_dependencies, cur_includes_tools_for_display_in_tool_panel, \
|
||||
cur_has_repository_dependencies, cur_has_repository_dependencies_only_if_compiling_contained_td = \
|
||||
repository_util.get_repo_info_dict( trans, repository_id, changeset_revision )
|
||||
if cur_has_repository_dependencies and not has_repository_dependencies:
|
||||
has_repository_dependencies = True
|
||||
if cur_has_repository_dependencies_only_if_compiling_contained_td and not has_repository_dependencies_only_if_compiling_contained_td:
|
||||
has_repository_dependencies_only_if_compiling_contained_td = True
|
||||
if cur_includes_tools and not includes_tools:
|
||||
includes_tools = True
|
||||
if cur_includes_tool_dependencies and not includes_tool_dependencies:
|
||||
@@ -1629,6 +1671,7 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
return dict( includes_tools=includes_tools,
|
||||
includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel,
|
||||
has_repository_dependencies=has_repository_dependencies,
|
||||
has_repository_dependencies_only_if_compiling_contained_td=has_repository_dependencies_only_if_compiling_contained_td,
|
||||
includes_tool_dependencies=includes_tool_dependencies,
|
||||
repo_info_dicts=repo_info_dicts )
|
||||
|
||||
@@ -1708,7 +1751,9 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
tool_version_dicts = []
|
||||
for changeset in repo.changelog:
|
||||
current_changeset_revision = str( repo.changectx( changeset ) )
|
||||
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, trans.security.encode_id( repository.id ), current_changeset_revision )
|
||||
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans,
|
||||
trans.security.encode_id( repository.id ),
|
||||
current_changeset_revision )
|
||||
if repository_metadata and repository_metadata.tool_versions:
|
||||
tool_version_dicts.append( repository_metadata.tool_versions )
|
||||
if current_changeset_revision == changeset_revision:
|
||||
@@ -1764,24 +1809,32 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
break
|
||||
if 'workflows' in metadata:
|
||||
includes_workflows = True
|
||||
readme_files_dict = readme_util.build_readme_files_dict( metadata )
|
||||
readme_files_dict = readme_util.build_readme_files_dict( trans, repository, changeset_revision, metadata )
|
||||
# See if the repo_info_dict was populated with repository_dependencies or tool_dependencies.
|
||||
has_repository_dependencies = False
|
||||
has_repository_dependencies_only_if_compiling_contained_td = False
|
||||
includes_tool_dependencies = False
|
||||
for name, repo_info_tuple in repo_info_dict.items():
|
||||
description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, repository_dependencies, tool_dependencies = \
|
||||
suc.get_repo_info_tuple_contents( repo_info_tuple )
|
||||
if repository_dependencies:
|
||||
has_repository_dependencies = True
|
||||
else:
|
||||
has_repository_dependencies = False
|
||||
if tool_dependencies:
|
||||
includes_tool_dependencies = True
|
||||
else:
|
||||
includes_tool_dependencies = False
|
||||
if not has_repository_dependencies or not has_repository_dependencies_only_if_compiling_contained_td or not includes_tool_dependencies:
|
||||
description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, repository_dependencies, tool_dependencies = \
|
||||
suc.get_repo_info_tuple_contents( repo_info_tuple )
|
||||
for rd_key, rd_tups in repository_dependencies.items():
|
||||
if rd_key in [ 'root_key', 'description' ]:
|
||||
continue
|
||||
curr_has_repository_dependencies, curr_has_repository_dependencies_only_if_compiling_contained_td = \
|
||||
suc.get_repository_dependency_types( rd_tups )
|
||||
if curr_has_repository_dependencies and not has_repository_dependencies:
|
||||
has_repository_dependencies = True
|
||||
if curr_has_repository_dependencies_only_if_compiling_contained_td and not has_repository_dependencies_only_if_compiling_contained_td:
|
||||
has_repository_dependencies_only_if_compiling_contained_td = True
|
||||
if tool_dependencies and not includes_tool_dependencies:
|
||||
includes_tool_dependencies = True
|
||||
return dict( includes_data_managers=includes_data_managers,
|
||||
includes_datatypes=includes_datatypes,
|
||||
includes_tools=includes_tools,
|
||||
includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel,
|
||||
has_repository_dependencies=has_repository_dependencies,
|
||||
has_repository_dependencies_only_if_compiling_contained_td=has_repository_dependencies_only_if_compiling_contained_td,
|
||||
includes_tool_dependencies=includes_tool_dependencies,
|
||||
includes_workflows=includes_workflows,
|
||||
readme_files_dict=readme_files_dict,
|
||||
@@ -2372,7 +2425,10 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
avg_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, repository, webapp_model=trans.model )
|
||||
display_reviews = util.string_as_bool( kwd.get( 'display_reviews', False ) )
|
||||
rra = self.get_user_item_rating( trans.sa_session, trans.user, repository, webapp_model=trans.model )
|
||||
metadata = self.get_metadata( trans, id, repository.tip( trans.app ) )
|
||||
metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
|
||||
id,
|
||||
repository.tip( trans.app ),
|
||||
metadata_only=True )
|
||||
repository_type_select_field = rt_util.build_repository_type_select_field( trans, repository=repository )
|
||||
return trans.fill_template( '/webapps/tool_shed/repository/rate_repository.mako',
|
||||
repository=repository,
|
||||
@@ -2434,7 +2490,9 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
try:
|
||||
commands.remove( repo.ui, repo, selected_file, force=True )
|
||||
except Exception, e:
|
||||
log.debug( "Error removing files using the mercurial API, so trying a different approach, the error was: %s" % str( e ))
|
||||
log.debug( "Error removing the following file using the mercurial API:\n %s" % str( selected_file ) )
|
||||
log.debug( "The error was: %s" % str( e ))
|
||||
log.debug( "Attempting to remove the file using a different approach." )
|
||||
relative_selected_file = selected_file.split( 'repo_%d' % repository.id )[1].lstrip( '/' )
|
||||
repo.dirstate.remove( relative_selected_file )
|
||||
repo.dirstate.write()
|
||||
@@ -2767,7 +2825,10 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
'has_metadata' : has_metadata }
|
||||
# Make sure we'll view latest changeset first.
|
||||
changesets.insert( 0, change_dict )
|
||||
metadata = self.get_metadata( trans, id, repository.tip( trans.app ) )
|
||||
metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
|
||||
id,
|
||||
repository.tip( trans.app ),
|
||||
metadata_only=True )
|
||||
return trans.fill_template( '/webapps/tool_shed/repository/view_changelog.mako',
|
||||
repository=repository,
|
||||
metadata=metadata,
|
||||
@@ -2800,7 +2861,7 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
diffs = []
|
||||
for diff in patch.diff( repo, node1=ctx_parent.node(), node2=ctx.node() ):
|
||||
diffs.append( suc.to_html_string( diff ) )
|
||||
metadata = self.get_metadata( trans, id, ctx_str )
|
||||
metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, id, ctx_str, metadata_only=True )
|
||||
# For rendering the prev button.
|
||||
if ctx_parent:
|
||||
ctx_parent_rev = ctx_parent.rev()
|
||||
|
||||
@@ -154,6 +154,10 @@ class Repository( object, Dictifiable ):
|
||||
self.times_downloaded = times_downloaded
|
||||
self.deprecated = deprecated
|
||||
|
||||
def allow_push( self, app ):
|
||||
repo = hg.repository( ui.ui(), self.repo_path( app ) )
|
||||
return repo.ui.config( 'web', 'allow_push' )
|
||||
|
||||
def can_change_type( self, app ):
|
||||
# Allow changing the type only if the repository has no contents, has never been installed, or has never been changed from
|
||||
# the default type.
|
||||
@@ -172,12 +176,6 @@ class Repository( object, Dictifiable ):
|
||||
return True
|
||||
return False
|
||||
|
||||
def to_dict( self, view='collection', value_mapper=None ):
|
||||
rval = super( Repository, self ).to_dict( view=view, value_mapper=value_mapper )
|
||||
if 'user_id' in rval:
|
||||
rval[ 'owner' ] = self.user.username
|
||||
return rval
|
||||
|
||||
def get_changesets_for_setting_metadata( self, app ):
|
||||
type_class = self.get_type_class( app )
|
||||
return type_class.get_changesets_for_setting_metadata( app, self )
|
||||
@@ -185,6 +183,11 @@ class Repository( object, Dictifiable ):
|
||||
def get_type_class( self, app ):
|
||||
return app.repository_types_registry.get_class_by_label( self.type )
|
||||
|
||||
def is_new( self, app ):
|
||||
repo = hg.repository( ui.ui(), self.repo_path( app ) )
|
||||
tip_ctx = repo.changectx( repo.changelog.tip() )
|
||||
return tip_ctx.rev() < 0
|
||||
|
||||
def repo_path( self, app ):
|
||||
return app.hgweb_config_manager.get_entry( os.path.join( "repos", self.user.username, self.name ) )
|
||||
|
||||
@@ -193,19 +196,6 @@ class Repository( object, Dictifiable ):
|
||||
tip_ctx = repo.changectx( repo.changelog.tip() )
|
||||
return "%s:%s" % ( str( tip_ctx.rev() ), str( repo.changectx( repo.changelog.tip() ) ) )
|
||||
|
||||
def tip( self, app ):
|
||||
repo = hg.repository( ui.ui(), self.repo_path( app ) )
|
||||
return str( repo.changectx( repo.changelog.tip() ) )
|
||||
|
||||
def is_new( self, app ):
|
||||
repo = hg.repository( ui.ui(), self.repo_path( app ) )
|
||||
tip_ctx = repo.changectx( repo.changelog.tip() )
|
||||
return tip_ctx.rev() < 0
|
||||
|
||||
def allow_push( self, app ):
|
||||
repo = hg.repository( ui.ui(), self.repo_path( app ) )
|
||||
return repo.ui.config( 'web', 'allow_push' )
|
||||
|
||||
def set_allow_push( self, app, usernames, remove_auth='' ):
|
||||
allow_push = util.listify( self.allow_push( app ) )
|
||||
if remove_auth:
|
||||
@@ -227,6 +217,16 @@ class Repository( object, Dictifiable ):
|
||||
fp.write( line )
|
||||
fp.close()
|
||||
|
||||
def tip( self, app ):
|
||||
repo = hg.repository( ui.ui(), self.repo_path( app ) )
|
||||
return str( repo.changectx( repo.changelog.tip() ) )
|
||||
|
||||
def to_dict( self, view='collection', value_mapper=None ):
|
||||
rval = super( Repository, self ).to_dict( view=view, value_mapper=value_mapper )
|
||||
if 'user_id' in rval:
|
||||
rval[ 'owner' ] = self.user.username
|
||||
return rval
|
||||
|
||||
|
||||
class RepositoryMetadata( object, Dictifiable ):
|
||||
dict_collection_visible_keys = ( 'id', 'repository_id', 'changeset_revision', 'malicious', 'downloadable', 'has_repository_dependencies', 'includes_datatypes',
|
||||
@@ -253,6 +253,7 @@ class RepositoryMetadata( object, Dictifiable ):
|
||||
self.time_last_tested = time_last_tested
|
||||
self.tool_test_results = tool_test_results
|
||||
self.has_repository_dependencies = has_repository_dependencies
|
||||
# We don't consider the special case has_repository_dependencies_only_if_compiling_contained_td here.
|
||||
self.includes_datatypes = includes_datatypes
|
||||
self.includes_tools = includes_tools
|
||||
self.includes_tool_dependencies = includes_tool_dependencies
|
||||
@@ -377,6 +378,23 @@ class ItemTagAssociation( object ):
|
||||
self.user_value = None
|
||||
|
||||
|
||||
class PostJobAction( object ):
|
||||
|
||||
def __init__( self, action_type, workflow_step, output_name = None, action_arguments = None):
|
||||
self.action_type = action_type
|
||||
self.output_name = output_name
|
||||
self.action_arguments = action_arguments
|
||||
self.workflow_step = workflow_step
|
||||
|
||||
|
||||
class StoredWorkflowAnnotationAssociation( object ):
|
||||
pass
|
||||
|
||||
|
||||
class WorkflowStepAnnotationAssociation( object ):
|
||||
pass
|
||||
|
||||
|
||||
class Workflow( object ):
|
||||
|
||||
def __init__( self ):
|
||||
|
||||
@@ -10,35 +10,42 @@ log = logging.getLogger( __name__ )
|
||||
|
||||
def generate_deprecated_repository_img_str( include_mouse_over=False ):
|
||||
if include_mouse_over:
|
||||
deprecated_tip_str = 'class="icon-button tooltip" title="This repository is deprecated in the Tool Shed"'
|
||||
deprecated_tip_str = 'class="icon-button" title="This repository is deprecated in the Tool Shed"'
|
||||
else:
|
||||
deprecated_tip_str = ''
|
||||
return '<img src="/static/images/icon_error_sml.gif" %s/>' % deprecated_tip_str
|
||||
|
||||
def generate_includes_workflows_img_str( include_mouse_over=False ):
|
||||
if include_mouse_over:
|
||||
deprecated_tip_str = 'class="icon-button" title="This repository contains exported workflows"'
|
||||
else:
|
||||
deprecated_tip_str = ''
|
||||
return '<img src="/static/images/fugue/gear.png" %s/>' % deprecated_tip_str
|
||||
|
||||
def generate_latest_revision_img_str( include_mouse_over=False ):
|
||||
if include_mouse_over:
|
||||
latest_revision_tip_str = 'class="icon-button tooltip" title="This is the latest installable revision of this repository"'
|
||||
latest_revision_tip_str = 'class="icon-button" title="This is the latest installable revision of this repository"'
|
||||
else:
|
||||
latest_revision_tip_str = ''
|
||||
return '<img src="/static/june_2007_style/blue/ok_small.png" %s/>' % latest_revision_tip_str
|
||||
|
||||
def generate_revision_updates_img_str( include_mouse_over=False ):
|
||||
if include_mouse_over:
|
||||
revision_updates_tip_str = 'class="icon-button tooltip" title="Updates are available in the Tool Shed for this revision"'
|
||||
revision_updates_tip_str = 'class="icon-button" title="Updates are available in the Tool Shed for this revision"'
|
||||
else:
|
||||
revision_updates_tip_str = ''
|
||||
return '<img src="/static/images/icon_warning_sml.gif" %s/>' % revision_updates_tip_str
|
||||
|
||||
def generate_revision_upgrades_img_str( include_mouse_over=False ):
|
||||
if include_mouse_over:
|
||||
revision_upgrades_tip_str = 'class="icon-button tooltip" title="A newer installable revision is available for this repository"'
|
||||
revision_upgrades_tip_str = 'class="icon-button" title="A newer installable revision is available for this repository"'
|
||||
else:
|
||||
revision_upgrades_tip_str = ''
|
||||
return '<img src="/static/images/up.gif" %s/>' % revision_upgrades_tip_str
|
||||
|
||||
def generate_unknown_img_str( include_mouse_over=False ):
|
||||
if include_mouse_over:
|
||||
unknown_tip_str = 'class="icon-button tooltip" title="Unable to get information from the Tool Shed"'
|
||||
unknown_tip_str = 'class="icon-button" title="Unable to get information from the Tool Shed"'
|
||||
else:
|
||||
unknown_tip_str = ''
|
||||
return '<img src="/static/june_2007_style/blue/question-octagon-frame.png" %s/>' % unknown_tip_str
|
||||
@@ -60,6 +67,8 @@ class InstalledRepositoryGrid( grids.Grid ):
|
||||
tool_shed_status_str += generate_revision_updates_img_str( include_mouse_over=True )
|
||||
if tool_shed_repository.upgrade_available:
|
||||
tool_shed_status_str += generate_revision_upgrades_img_str( include_mouse_over=True )
|
||||
if tool_shed_repository.includes_workflows:
|
||||
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 )
|
||||
return tool_shed_status_str
|
||||
@@ -211,7 +220,11 @@ class InstalledRepositoryGrid( grids.Grid ):
|
||||
use_paging = False
|
||||
|
||||
def build_initial_query( self, trans, **kwd ):
|
||||
return trans.sa_session.query( self.model_class )
|
||||
return trans.sa_session.query( self.model_class ) \
|
||||
.order_by( self.model_class.table.c.tool_shed,
|
||||
self.model_class.table.c.name,
|
||||
self.model_class.table.c.owner,
|
||||
self.model_class.table.c.ctx_rev )
|
||||
|
||||
@property
|
||||
def legend( self ):
|
||||
@@ -219,6 +232,7 @@ class InstalledRepositoryGrid( grids.Grid ):
|
||||
legend_str += '%s A newer installable revision is available for this repository<br/>' % generate_revision_upgrades_img_str()
|
||||
legend_str += '%s This is the latest installable revision of this repository<br/>' % generate_latest_revision_img_str()
|
||||
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()
|
||||
return legend_str
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import threading
|
||||
import logging
|
||||
from galaxy import util
|
||||
from galaxy.tools import ToolSection
|
||||
from galaxy.util.json import from_json_string
|
||||
@@ -20,6 +21,8 @@ from tool_shed.util import tool_util
|
||||
from tool_shed.util import xml_util
|
||||
from galaxy.util.odict import odict
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
|
||||
class InstallManager( object ):
|
||||
|
||||
@@ -83,6 +86,9 @@ class InstallManager( object ):
|
||||
plural = 's'
|
||||
file_names = ', '.join( self.proprietary_tool_confs )
|
||||
if missing_tool_configs_dict:
|
||||
for proprietary_tool_conf in self.proprietary_tool_confs:
|
||||
# Create a backup of the tool configuration in the un-migrated state.
|
||||
shutil.copy( proprietary_tool_conf, '%s-pre-stage-%04d' % ( proprietary_tool_conf, latest_migration_script_number ) )
|
||||
for repository_elem in root:
|
||||
# Make sure we have a valid repository tag.
|
||||
if self.__is_valid_repository_tag( repository_elem ):
|
||||
@@ -127,7 +133,7 @@ class InstallManager( object ):
|
||||
# Install path is of the form: <tool path>/<tool shed>/repos/<repository owner>/<repository name>/<installed changeset revision>
|
||||
relative_clone_dir = os.path.join( self.tool_shed, 'repos', owner, name, changeset_revision )
|
||||
clone_dir = os.path.join( self.tool_path, relative_clone_dir )
|
||||
if not self.__isinstalled( clone_dir ):
|
||||
if not self.__iscloned( clone_dir ):
|
||||
repository_clone_url = os.path.join( self.tool_shed_url, 'repos', owner, name )
|
||||
relative_install_dir = os.path.join( relative_clone_dir, name )
|
||||
install_dir = os.path.join( clone_dir, name )
|
||||
@@ -160,8 +166,8 @@ class InstallManager( object ):
|
||||
if rd_key in [ 'root_key', 'description' ]:
|
||||
continue
|
||||
for rd_tup in rd_tups:
|
||||
rd_tool_shed, rd_name, rd_owner, rd_changeset_revision, rd_prior_installation_required = \
|
||||
common_util.parse_repository_dependency_tuple( rd_tup )
|
||||
parsed_rd_tup = common_util.parse_repository_dependency_tuple( rd_tup )
|
||||
rd_tool_shed, rd_name, rd_owner, rd_changeset_revision = parsed_rd_tup[ 0:4 ]
|
||||
# TODO: Make sure the repository description is applied to the new repository record during installation.
|
||||
tool_shed_repository = self.create_or_update_tool_shed_repository_record( rd_name, rd_owner, rd_changeset_revision, description=None )
|
||||
if tool_shed_repository:
|
||||
@@ -200,7 +206,7 @@ class InstallManager( object ):
|
||||
tmp_filename = fh.name
|
||||
fh.close()
|
||||
fh = open( tmp_filename, 'wb' )
|
||||
tree.write( tmp_filename )
|
||||
tree.write( tmp_filename, encoding='utf-8', xml_declaration=True )
|
||||
fh.close()
|
||||
shutil.move( tmp_filename, os.path.abspath( proprietary_tool_conf ) )
|
||||
os.chmod( proprietary_tool_conf, 0644 )
|
||||
@@ -472,7 +478,15 @@ class InstallManager( object ):
|
||||
tool_shed_repository.name,
|
||||
tool_shed_repository.installed_changeset_revision )
|
||||
clone_dir = os.path.join( self.tool_path, relative_clone_dir )
|
||||
if self.__isinstalled( clone_dir ):
|
||||
cloned_ok = self.__iscloned( clone_dir )
|
||||
is_installed = False
|
||||
# Any of the following states should count as installed in this context.
|
||||
if tool_shed_repository.status in [ self.app.model.ToolShedRepository.installation_status.INSTALLED,
|
||||
self.app.model.ToolShedRepository.installation_status.ERROR,
|
||||
self.app.model.ToolShedRepository.installation_status.UNINSTALLED,
|
||||
self.app.model.ToolShedRepository.installation_status.DEACTIVATED ]:
|
||||
is_installed = True
|
||||
if cloned_ok and is_installed:
|
||||
print "Skipping automatic install of repository '", tool_shed_repository.name, "' because it has already been installed in location ", clone_dir
|
||||
else:
|
||||
repository_clone_url = os.path.join( self.tool_shed_url, 'repos', tool_shed_repository.owner, tool_shed_repository.name )
|
||||
@@ -483,9 +497,10 @@ class InstallManager( object ):
|
||||
tool_shed_repository.name,
|
||||
tool_shed_repository.owner,
|
||||
tool_shed_repository.installed_changeset_revision )
|
||||
suc.update_tool_shed_repository_status( self.app, tool_shed_repository, self.app.model.ToolShedRepository.installation_status.CLONING )
|
||||
cloned_ok, error_message = suc.clone_repository( repository_clone_url, os.path.abspath( install_dir ), ctx_rev )
|
||||
if cloned_ok:
|
||||
if not cloned_ok:
|
||||
suc.update_tool_shed_repository_status( self.app, tool_shed_repository, self.app.model.ToolShedRepository.installation_status.CLONING )
|
||||
cloned_ok, error_message = suc.clone_repository( repository_clone_url, os.path.abspath( install_dir ), ctx_rev )
|
||||
if cloned_ok and not is_installed:
|
||||
self.handle_repository_contents( tool_shed_repository=tool_shed_repository,
|
||||
repository_clone_url=repository_clone_url,
|
||||
relative_install_dir=relative_install_dir,
|
||||
@@ -586,7 +601,7 @@ class InstallManager( object ):
|
||||
return True
|
||||
return False
|
||||
|
||||
def __isinstalled( self, clone_dir ):
|
||||
def __iscloned( self, clone_dir ):
|
||||
full_path = os.path.abspath( clone_dir )
|
||||
if os.path.exists( full_path ):
|
||||
for root, dirs, files in os.walk( full_path ):
|
||||
|
||||
@@ -29,7 +29,7 @@ def verify_tools( app, url, galaxy_config_file, engine_options={} ):
|
||||
try:
|
||||
egg = dialect_to_egg[ dialect ]
|
||||
try:
|
||||
pkg_resources.require( egg )
|
||||
eggs.require( egg )
|
||||
log.debug( "%s egg successfully loaded for %s dialect" % ( egg, dialect ) )
|
||||
except:
|
||||
# If the module is in the path elsewhere (i.e. non-egg), it'll still load.
|
||||
@@ -113,7 +113,7 @@ def verify_tools( app, url, galaxy_config_file, engine_options={} ):
|
||||
msg += "setting in your main Galaxy configuration file (e.g., uninverse_wsgi.ini).\n"
|
||||
processed_tool_dependencies = []
|
||||
for missing_tool_config, tool_dependencies in missing_tool_configs_dict.items():
|
||||
for tool_dependencies_tup in tool_dependencies:
|
||||
for tool_dependencies_tup in missing_tool_configs_dict[ missing_tool_config ][ 'tool_dependencies' ]:
|
||||
if tool_dependencies_tup not in processed_tool_dependencies:
|
||||
msg += "------------------------------------\n"
|
||||
msg += "Tool Dependency\n"
|
||||
@@ -121,7 +121,7 @@ def verify_tools( app, url, galaxy_config_file, engine_options={} ):
|
||||
msg += "Name: %s, Version: %s, Type: %s\n" % ( tool_dependencies_tup[ 0 ],
|
||||
tool_dependencies_tup[ 1 ],
|
||||
tool_dependencies_tup[ 2 ] )
|
||||
if tool_dependencies_tup[ 3 ]:
|
||||
if len( tool_dependencies_tup ) >= 4:
|
||||
msg += "Requirements and installation information:\n"
|
||||
msg += "%s\n" % tool_dependencies_tup[ 3 ]
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
"""
|
||||
The following tools have been eliminated from the distribution:
|
||||
|
||||
1: BAM-to-SAM converts BAM format to SAM format
|
||||
2: Categorize Elements satisfying criteria
|
||||
3: Compute Motif Frequencies For All Motifs motif by motif
|
||||
4: Compute Motif Frequencies in indel flanking regions
|
||||
5: CTD analysis of chemicals, diseases, or genes
|
||||
6: Cuffcompare
|
||||
7: Cuffdiff
|
||||
8: Cufflinks
|
||||
9: Cuffmerge
|
||||
10: Delete Overlapping Indels from a chromosome indels file
|
||||
11: Separate pgSnp alleles into columns
|
||||
12: Draw Stacked Bar Plots for different categories and different criteria
|
||||
13: Length Distribution chart
|
||||
14: FASTA Width formatter
|
||||
15: RNA/DNA converter
|
||||
16: Draw quality score boxplot
|
||||
17: Quality format converter (ASCII-Numeric)
|
||||
18: Filter by quality
|
||||
19: FASTQ to FASTA converter
|
||||
20: Remove sequencing artifacts
|
||||
21: Barcode Splitter
|
||||
22: Clip adapter sequences
|
||||
23: Collapse sequences
|
||||
24: Draw nucleotides distribution chart
|
||||
25: Compute quality statistics
|
||||
26: Rename sequences
|
||||
27: Reverse- Complement
|
||||
28: Trim sequences
|
||||
29: FunDO human genes associated with disease terms
|
||||
30: HVIS visualization of genomic data with the Hilbert curve
|
||||
31: Fetch Indels from 3-way alignments
|
||||
32: Identify microsatellite births and deaths
|
||||
33: Extract orthologous microsatellites for multiple (>2) species alignments
|
||||
34: Mutate Codons with SNPs
|
||||
35: Pileup-to-Interval condenses pileup format into ranges of bases
|
||||
36: Filter pileup on coverage and SNPs
|
||||
37: Filter SAM on bitwise flag values
|
||||
38: Merge BAM Files merges BAM files together
|
||||
39: Generate pileup from BAM dataset
|
||||
40: SAM-to-BAM converts SAM format to BAM format
|
||||
41: Convert SAM to interval
|
||||
42: flagstat provides simple stats on BAM files
|
||||
43: MPileup SNP and indel caller
|
||||
44: rmdup remove PCR duplicates
|
||||
45: Slice BAM by provided regions
|
||||
46: Split paired end reads
|
||||
47: T Test for Two Samples
|
||||
48: Plotting tool for multiple series and graph types.
|
||||
|
||||
The tools are now available in the repositories respectively:
|
||||
|
||||
1: bam_to_sam
|
||||
2: categorize_elements_satisfying_criteria
|
||||
3: compute_motif_frequencies_for_all_motifs
|
||||
4: compute_motifs_frequency
|
||||
5: ctd_batch
|
||||
6: cuffcompare
|
||||
7: cuffdiff
|
||||
8: cufflinks
|
||||
9: cuffmerge
|
||||
10: delete_overlapping_indels
|
||||
11: divide_pg_snp
|
||||
12: draw_stacked_barplots
|
||||
13: fasta_clipping_histogram
|
||||
14: fasta_formatter
|
||||
15: fasta_nucleotide_changer
|
||||
16: fastq_quality_boxplot
|
||||
17: fastq_quality_converter
|
||||
18: fastq_quality_filter
|
||||
19: fastq_to_fasta
|
||||
20: fastx_artifacts_filter
|
||||
21: fastx_barcode_splitter
|
||||
22: fastx_clipper
|
||||
23: fastx_collapser
|
||||
24: fastx_nucleotides_distribution
|
||||
25: fastx_quality_statistics
|
||||
26: fastx_renamer
|
||||
27: fastx_reverse_complement
|
||||
28: fastx_trimmer
|
||||
29: hgv_fundo
|
||||
30: hgv_hilbertvis
|
||||
31: indels_3way
|
||||
32: microsatellite_birthdeath
|
||||
33: multispecies_orthologous_microsats
|
||||
34: mutate_snp_codon
|
||||
35: pileup_interval
|
||||
36: pileup_parser
|
||||
37: sam_bitwise_flag_filter
|
||||
38: sam_merge
|
||||
39: sam_pileup
|
||||
40: sam_to_bam
|
||||
41: sam2interval
|
||||
42: samtools_flagstat
|
||||
43: samtools_mpileup
|
||||
44: samtools_rmdup
|
||||
45: samtools_slice_bam
|
||||
46: split_paired_reads
|
||||
47: t_test_two_samples
|
||||
48: xy_plot
|
||||
|
||||
from the main Galaxy tool shed at http://toolshed.g2.bx.psu.edu
|
||||
and will be installed into your local Galaxy instance at the
|
||||
location discussed above by running the following command.
|
||||
|
||||
"""
|
||||
|
||||
def upgrade( migrate_engine ):
|
||||
print __doc__
|
||||
|
||||
def downgrade( migrate_engine ):
|
||||
pass
|
||||
@@ -22,9 +22,8 @@ from tool_shed.util import tool_util
|
||||
from xml.etree import ElementTree as XmlET
|
||||
|
||||
from galaxy import eggs
|
||||
import pkg_resources
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
pkg_resources.require( 'mercurial' )
|
||||
from mercurial import commands
|
||||
from mercurial import hg
|
||||
from mercurial import ui
|
||||
@@ -100,19 +99,14 @@ def get_installed_repositories_from_repository_dependencies( trans, repository_d
|
||||
continue
|
||||
# rd_key is something like: 'http://localhost:9009__ESEP__package_rdkit_2012_12__ESEP__test__ESEP__d635ffb9c665__ESEP__True'
|
||||
# rd_val is something like: [['http://localhost:9009', 'package_numpy_1_7', 'test', 'cddd64ecd985', 'True']]
|
||||
try:
|
||||
tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
|
||||
container_util.get_components_from_key( rd_key )
|
||||
except:
|
||||
tool_shed, name, owner, changeset_revision = container_util.get_components_from_key( rd_val )
|
||||
repository_components_tuple = container_util.get_components_from_key( rd_key )
|
||||
components_list = suc.extract_components_from_tuple( repository_components_tuple )
|
||||
tool_shed, name, owner, changeset_revision = components_list[ 0:4 ]
|
||||
installed_repository = suc.get_tool_shed_repository_by_shed_name_owner_changeset_revision( trans.app, tool_shed, name, owner, changeset_revision )
|
||||
if installed_repository not in installed_repositories:
|
||||
installed_repositories.append( installed_repository )
|
||||
for rd_val in rd_vals:
|
||||
try:
|
||||
tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = rd_val
|
||||
except:
|
||||
tool_shed, name, owner, changeset_revision = rd_val
|
||||
tool_shed, name, owner, changeset_revision = rd_val[ 0:4 ]
|
||||
installed_repository = suc.get_tool_shed_repository_by_shed_name_owner_changeset_revision( trans.app, tool_shed, name, owner, changeset_revision )
|
||||
if installed_repository not in installed_repositories:
|
||||
installed_repositories.append( installed_repository )
|
||||
@@ -234,10 +228,10 @@ def get_repo_info_dict( trans, repository_id, changeset_revision ):
|
||||
else:
|
||||
includes_tools = False
|
||||
includes_tools_for_display_in_tool_panel = repository_metadata.includes_tools_for_display_in_tool_panel
|
||||
if 'repository_dependencies' in metadata:
|
||||
has_repository_dependencies = True
|
||||
else:
|
||||
has_repository_dependencies = False
|
||||
repository_dependencies_dict = metadata.get( 'repository_dependencies', {} )
|
||||
repository_dependencies = repository_dependencies_dict.get( 'repository_dependencies', [] )
|
||||
has_repository_dependencies, has_repository_dependencies_only_if_compiling_contained_td = \
|
||||
suc.get_repository_dependency_types( repository_dependencies )
|
||||
if 'tool_dependencies' in metadata:
|
||||
includes_tool_dependencies = True
|
||||
else:
|
||||
@@ -246,6 +240,7 @@ def get_repo_info_dict( trans, repository_id, changeset_revision ):
|
||||
# Here's where we may have to handle enhancements to the callers. See above comment.
|
||||
includes_tools = False
|
||||
has_repository_dependencies = False
|
||||
has_repository_dependencies_only_if_compiling_contained_td = False
|
||||
includes_tool_dependencies = False
|
||||
includes_tools_for_display_in_tool_panel = False
|
||||
ctx = suc.get_changectx_for_changeset( repo, changeset_revision )
|
||||
@@ -259,7 +254,8 @@ def get_repo_info_dict( trans, repository_id, changeset_revision ):
|
||||
repository_metadata=repository_metadata,
|
||||
tool_dependencies=None,
|
||||
repository_dependencies=None )
|
||||
return repo_info_dict, includes_tools, includes_tool_dependencies, includes_tools_for_display_in_tool_panel, has_repository_dependencies
|
||||
return repo_info_dict, includes_tools, includes_tool_dependencies, includes_tools_for_display_in_tool_panel, \
|
||||
has_repository_dependencies, has_repository_dependencies_only_if_compiling_contained_td
|
||||
|
||||
def get_repository_components_for_installation( encoded_tsr_id, encoded_tsr_ids, repo_info_dicts, tool_panel_section_keys ):
|
||||
"""
|
||||
@@ -311,6 +307,7 @@ def get_update_to_changeset_revision_and_ctx_rev( trans, repository ):
|
||||
includes_tool_dependencies = update_dict.get( 'includes_tool_dependencies', False )
|
||||
includes_workflows = update_dict.get( 'includes_workflows', False )
|
||||
has_repository_dependencies = update_dict.get( 'has_repository_dependencies', False )
|
||||
has_repository_dependencies_only_if_compiling_contained_td = update_dict.get( 'has_repository_dependencies_only_if_compiling_contained_td', False )
|
||||
changeset_revision = update_dict.get( 'changeset_revision', None )
|
||||
ctx_rev = update_dict.get( 'ctx_rev', None )
|
||||
changeset_revision_dict[ 'includes_data_managers' ] = includes_data_managers
|
||||
@@ -320,6 +317,7 @@ def get_update_to_changeset_revision_and_ctx_rev( trans, repository ):
|
||||
changeset_revision_dict[ 'includes_tool_dependencies' ] = includes_tool_dependencies
|
||||
changeset_revision_dict[ 'includes_workflows' ] = includes_workflows
|
||||
changeset_revision_dict[ 'has_repository_dependencies' ] = has_repository_dependencies
|
||||
changeset_revision_dict[ 'has_repository_dependencies_only_if_compiling_contained_td' ] = has_repository_dependencies_only_if_compiling_contained_td
|
||||
changeset_revision_dict[ 'changeset_revision' ] = changeset_revision
|
||||
changeset_revision_dict[ 'ctx_rev' ] = ctx_rev
|
||||
except Exception, e:
|
||||
@@ -331,6 +329,7 @@ def get_update_to_changeset_revision_and_ctx_rev( trans, repository ):
|
||||
changeset_revision_dict[ 'includes_tool_dependencies' ] = False
|
||||
changeset_revision_dict[ 'includes_workflows' ] = False
|
||||
changeset_revision_dict[ 'has_repository_dependencies' ] = False
|
||||
changeset_revision_dict[ 'has_repository_dependencies_only_if_compiling_contained_td' ] = False
|
||||
changeset_revision_dict[ 'changeset_revision' ] = None
|
||||
changeset_revision_dict[ 'ctx_rev' ] = None
|
||||
return changeset_revision_dict
|
||||
@@ -353,6 +352,10 @@ def handle_repository_contents( trans, tool_shed_repository, tool_path, reposito
|
||||
updating_installed_repository=False,
|
||||
persist=True )
|
||||
tool_shed_repository.metadata = metadata_dict
|
||||
# Update the tool_shed_repository.tool_shed_status column in the database.
|
||||
tool_shed_status_dict = suc.get_tool_shed_status_for_installed_repository( trans.app, tool_shed_repository )
|
||||
if tool_shed_status_dict:
|
||||
tool_shed_repository.tool_shed_status = tool_shed_status_dict
|
||||
trans.sa_session.add( tool_shed_repository )
|
||||
trans.sa_session.flush()
|
||||
if 'tool_dependencies' in metadata_dict and not reinstalling:
|
||||
@@ -427,7 +430,6 @@ def handle_tool_shed_repositories( trans, installation_dict, using_api=False ):
|
||||
install_repository_dependencies = installation_dict[ 'install_repository_dependencies' ]
|
||||
new_tool_panel_section = installation_dict[ 'new_tool_panel_section' ]
|
||||
no_changes_checked = installation_dict[ 'no_changes_checked' ]
|
||||
reinstalling = installation_dict[ 'reinstalling' ]
|
||||
repo_info_dicts = installation_dict[ 'repo_info_dicts' ]
|
||||
tool_panel_section = installation_dict[ 'tool_panel_section' ]
|
||||
tool_path = installation_dict[ 'tool_path' ]
|
||||
@@ -437,7 +439,6 @@ def handle_tool_shed_repositories( trans, installation_dict, using_api=False ):
|
||||
tool_path=tool_path,
|
||||
tool_shed_url=tool_shed_url,
|
||||
repo_info_dicts=repo_info_dicts,
|
||||
reinstalling=reinstalling,
|
||||
install_repository_dependencies=install_repository_dependencies,
|
||||
no_changes_checked=no_changes_checked,
|
||||
tool_panel_section=tool_panel_section,
|
||||
@@ -626,13 +627,10 @@ def merge_containers_dicts_for_new_install( containers_dicts ):
|
||||
# Change the folder id so it won't confict with others being merged.
|
||||
old_container_repository_dependencies_folder.id = folder_id
|
||||
folder_id += 1
|
||||
repository_components_tuple = container_util.get_components_from_key( old_container_repository_dependencies_folder.key )
|
||||
components_list = suc.extract_components_from_tuple( repository_components_tuple )
|
||||
name = components_list[ 1 ]
|
||||
# Generate the label by retrieving the repository name.
|
||||
try:
|
||||
toolshed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
|
||||
container_util.get_components_from_key( old_container_repository_dependencies_folder.key )
|
||||
except ValueError:
|
||||
# For backward compatibility to the 12/20/12 Galaxy release.
|
||||
toolshed, name, owner, changeset_revision = container_util.get_components_from_key( old_container_repository_dependencies_folder.key )
|
||||
old_container_repository_dependencies_folder.label = str( name )
|
||||
repository_dependencies_folder.folders.append( old_container_repository_dependencies_folder )
|
||||
# Merge tool_dependencies.
|
||||
|
||||
@@ -8,15 +8,16 @@ import tempfile
|
||||
import shutil
|
||||
import td_common_util
|
||||
from contextlib import contextmanager
|
||||
from galaxy.util import unicodify
|
||||
from galaxy.util.template import fill_template
|
||||
from galaxy import eggs
|
||||
|
||||
import pkg_resources
|
||||
|
||||
pkg_resources.require('ssh' )
|
||||
pkg_resources.require( 'Fabric' )
|
||||
eggs.require( 'ssh' )
|
||||
eggs.require( 'paramiko' )
|
||||
eggs.require( 'Fabric' )
|
||||
|
||||
from fabric.api import env
|
||||
from fabric.api import hide
|
||||
from fabric.api import lcd
|
||||
from fabric.api import local
|
||||
from fabric.api import settings
|
||||
@@ -31,6 +32,40 @@ def check_fabric_version():
|
||||
if int( version.split( "." )[ 0 ] ) < 1:
|
||||
raise NotImplementedError( "Install Fabric version 1.0 or later." )
|
||||
|
||||
def file_append( text, file_path, skip_if_contained=True, make_executable=True ):
|
||||
'''
|
||||
Append a line to a file unless skip_if_contained is True and the line already exists in the file. This method creates the file
|
||||
if it doesn't exist. If make_executable is True, the permissions on the file are set to executable by the owner. This method
|
||||
is similar to a local version of fabric.contrib.files.append.
|
||||
'''
|
||||
if not os.path.exists( file_path ):
|
||||
local( 'touch %s' % file_path )
|
||||
if make_executable:
|
||||
# Explicitly set the file to the received mode if valid.
|
||||
with settings( hide( 'everything' ), warn_only=True ):
|
||||
local( 'chmod +x %s' % file_path )
|
||||
return_code = 0
|
||||
# Convert the received text to a list, in order to support adding one or more lines to the file.
|
||||
if isinstance( text, basestring ):
|
||||
text = [ text ]
|
||||
for line in text:
|
||||
# Build a regex to search for the relevant line in env.sh.
|
||||
regex = td_common_util.egrep_escape( line )
|
||||
if skip_if_contained:
|
||||
# If the line exists in the file, egrep will return a success.
|
||||
with settings( hide( 'everything' ), warn_only=True ):
|
||||
egrep_cmd = 'egrep "^%s$" %s' % ( regex, file_path )
|
||||
contains_line = local( egrep_cmd ).succeeded
|
||||
if contains_line:
|
||||
continue
|
||||
# Append the current line to the file, escaping any single quotes in the line.
|
||||
line = line.replace( "'", r"'\\''" )
|
||||
return_code = local( "echo '%s' >> %s" % ( line, file_path ) ).return_code
|
||||
if return_code:
|
||||
# Return upon the first error encountered.
|
||||
return return_code
|
||||
return return_code
|
||||
|
||||
def filter_actions_after_binary_installation( actions ):
|
||||
'''Filter out actions that should not be processed if a binary download succeeded.'''
|
||||
filtered_actions = []
|
||||
@@ -48,9 +83,9 @@ def handle_command( app, tool_dependency, install_dir, cmd, return_output=False
|
||||
if output.return_code:
|
||||
tool_dependency.status = app.model.ToolDependency.installation_status.ERROR
|
||||
if output.stderr:
|
||||
tool_dependency.error_message = str( output.stderr )[ :32768 ]
|
||||
tool_dependency.error_message = unicodify( str( output.stderr )[ :32768 ] )
|
||||
elif output.stdout:
|
||||
tool_dependency.error_message = str( output.stdout )[ :32768 ]
|
||||
tool_dependency.error_message = unicodify( str( output.stdout )[ :32768 ] )
|
||||
else:
|
||||
tool_dependency.error_message = "Unknown error occurred executing shell command %s, return_code: %s" % ( str( cmd ), str( output.return_code ) )
|
||||
sa_session.add( tool_dependency )
|
||||
@@ -303,6 +338,43 @@ def install_and_build_package( app, tool_dependency, actions_dict ):
|
||||
filename = url.split( '/' )[ -1 ]
|
||||
td_common_util.url_download( work_dir, filename, url )
|
||||
dir = os.path.curdir
|
||||
elif action_type == 'setup_r_environment':
|
||||
# setup an R environment
|
||||
# <action type="setup_r_environment">
|
||||
# <r_base name="package_r_3_0_1" owner="bgruening" />
|
||||
# </action>
|
||||
# allow downloading and installing an R package
|
||||
# <package>https://github.com/bgruening/download_store/raw/master/DESeq2-1_0_18/BiocGenerics_0.6.0.tar.gz</package>
|
||||
if action_dict.get( 'env_shell_file_paths', False ):
|
||||
install_environment.add_env_shell_file_paths( action_dict[ 'env_shell_file_paths' ] )
|
||||
else:
|
||||
log.warning( 'Missing R environment. Please check your specified R installation exists.' )
|
||||
return
|
||||
tarball_names = list()
|
||||
for url in action_dict[ 'r_packages' ]:
|
||||
filename = url.split( '/' )[ -1 ]
|
||||
tarball_names.append( filename )
|
||||
td_common_util.url_download( work_dir, filename, url, extract=False )
|
||||
dir = os.path.curdir
|
||||
current_dir = os.path.abspath( os.path.join( work_dir, dir ) )
|
||||
with lcd( current_dir ):
|
||||
with settings( warn_only=True ):
|
||||
for tarball_name in tarball_names:
|
||||
cmd = '''export PATH=$PATH:$R_HOME/bin && export R_LIBS=$INSTALL_DIR &&
|
||||
Rscript -e "install.packages(c('%s'),lib='$INSTALL_DIR', repos=NULL, dependencies=FALSE)"''' % (tarball_name)
|
||||
|
||||
cmd = install_environment.build_command( td_common_util.evaluate_template( cmd, install_dir ) )
|
||||
return_code = handle_command( app, tool_dependency, install_dir, cmd )
|
||||
if return_code:
|
||||
return
|
||||
|
||||
# R libraries are installed to $INSTALL_DIR (install_dir), we now set the R_LIBS path to that directory
|
||||
# TODO: That code is used a lot for the different environments and should be refactored, once the environments are integrated
|
||||
modify_env_command_dict = dict( name="R_LIBS", action="prepend_to", value=install_dir )
|
||||
modify_env_command = td_common_util.create_or_update_env_shell_file( install_dir, modify_env_command_dict )
|
||||
return_code = handle_command( app, tool_dependency, install_dir, modify_env_command )
|
||||
if return_code:
|
||||
return
|
||||
else:
|
||||
# We're handling a complex repository dependency where we only have a set_environment tag set.
|
||||
# <action type="set_environment">
|
||||
@@ -321,7 +393,11 @@ def install_and_build_package( app, tool_dependency, actions_dict ):
|
||||
with lcd( current_dir ):
|
||||
action_type, action_dict = action_tup
|
||||
if action_type == 'make_directory':
|
||||
td_common_util.make_directory( full_path=action_dict[ 'full_path' ] )
|
||||
if os.path.isabs( action_dict[ 'full_path' ] ):
|
||||
full_path = action_dict[ 'full_path' ]
|
||||
else:
|
||||
full_path = os.path.join( current_dir, action_dict[ 'full_path' ] )
|
||||
td_common_util.make_directory( full_path=full_path )
|
||||
elif action_type == 'move_directory_files':
|
||||
td_common_util.move_directory_files( current_dir=current_dir,
|
||||
source_dir=os.path.join( action_dict[ 'source_directory' ] ),
|
||||
@@ -329,7 +405,8 @@ def install_and_build_package( app, tool_dependency, actions_dict ):
|
||||
elif action_type == 'move_file':
|
||||
td_common_util.move_file( current_dir=current_dir,
|
||||
source=os.path.join( action_dict[ 'source' ] ),
|
||||
destination_dir=os.path.join( action_dict[ 'destination' ] ) )
|
||||
destination=os.path.join( action_dict[ 'destination' ] ),
|
||||
rename_to=action_dict[ 'rename_to' ] )
|
||||
elif action_type == 'set_environment':
|
||||
# Currently the only action supported in this category is "environment_variable".
|
||||
# Build a command line from the prior_installation_required, in case an environment variable is referenced
|
||||
@@ -339,8 +416,8 @@ def install_and_build_package( app, tool_dependency, actions_dict ):
|
||||
for env_var_dict in env_var_dicts:
|
||||
# Check for the presence of the $ENV[] key string and populate it if possible.
|
||||
env_var_dict = handle_environment_variables( app, tool_dependency, install_dir, env_var_dict, cmds )
|
||||
env_command = td_common_util.create_or_update_env_shell_file( install_dir, env_var_dict )
|
||||
return_code = handle_command( app, tool_dependency, install_dir, env_command )
|
||||
env_entry, env_file = td_common_util.create_or_update_env_shell_file( install_dir, env_var_dict )
|
||||
return_code = file_append( env_entry, env_file, skip_if_contained=True, make_executable=True )
|
||||
if return_code:
|
||||
return
|
||||
elif action_type == 'set_environment_for_install':
|
||||
@@ -382,13 +459,13 @@ def install_and_build_package( app, tool_dependency, actions_dict ):
|
||||
log.error( "virtualenv's site-packages directory '%s' does not exist", output.stdout )
|
||||
return
|
||||
modify_env_command_dict = dict( name="PYTHONPATH", action="prepend_to", value=output.stdout )
|
||||
modify_env_command = td_common_util.create_or_update_env_shell_file( install_dir, modify_env_command_dict )
|
||||
return_code = handle_command( app, tool_dependency, install_dir, modify_env_command )
|
||||
env_entry, env_file = td_common_util.create_or_update_env_shell_file( install_dir, modify_env_command_dict )
|
||||
return_code = file_append( env_entry, env_file, skip_if_contained=True, make_executable=True )
|
||||
if return_code:
|
||||
return
|
||||
modify_env_command_dict = dict( name="PATH", action="prepend_to", value=os.path.join( venv_directory, "bin" ) )
|
||||
modify_env_command = td_common_util.create_or_update_env_shell_file( install_dir, modify_env_command_dict )
|
||||
return_code = handle_command( app, tool_dependency, install_dir, modify_env_command )
|
||||
env_entry, env_file = td_common_util.create_or_update_env_shell_file( install_dir, modify_env_command_dict )
|
||||
return_code = file_append( env_entry, env_file, skip_if_contained=True, make_executable=True )
|
||||
if return_code:
|
||||
return
|
||||
elif action_type == 'shell_command':
|
||||
@@ -409,6 +486,14 @@ def install_and_build_package( app, tool_dependency, actions_dict ):
|
||||
return_code = handle_command( app, tool_dependency, install_dir, cmd )
|
||||
if return_code:
|
||||
return
|
||||
elif action_type == 'make_install':
|
||||
# make; make install; allow providing make options
|
||||
with settings( warn_only=True ):
|
||||
make_opts = action_dict.get( 'make_opts', '' )
|
||||
cmd = install_environment.build_command( 'make %s && make install' % make_opts )
|
||||
return_code = handle_command( app, tool_dependency, install_dir, cmd )
|
||||
if return_code:
|
||||
return
|
||||
elif action_type == 'autoconf':
|
||||
# Handle configure, make and make install allow providing configuration options
|
||||
with settings( warn_only=True ):
|
||||
|
||||
@@ -14,8 +14,8 @@ from tool_shed.util import tool_dependency_util
|
||||
from tool_shed.util import xml_util
|
||||
from tool_shed.galaxy_install.tool_dependencies import td_common_util
|
||||
from galaxy.model.orm import and_
|
||||
from galaxy.web import url_for
|
||||
from galaxy.util import asbool
|
||||
from galaxy.util import listify
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
@@ -394,7 +394,7 @@ def install_package( app, elem, tool_shed_repository, tool_dependencies=None ):
|
||||
if not binary_installed:
|
||||
print 'Binary installation did not occur, so proceeding with install and compile recipe.'
|
||||
# Make sure to reset for installation if attempt at binary installation resulted in an error.
|
||||
if tool_dependency.status != app.model.ToolDependency.installation_status.NEW:
|
||||
if tool_dependency.status != app.model.ToolDependency.installation_status.NEVER_INSTALLED:
|
||||
removed, error_message = tool_dependency_util.remove_tool_dependency( app, tool_dependency )
|
||||
install_via_fabric( app,
|
||||
tool_dependency,
|
||||
@@ -460,7 +460,7 @@ def install_via_fabric( app, tool_dependency, install_dir, package_name=None, pr
|
||||
is_binary_download = True
|
||||
else:
|
||||
is_binary_download = False
|
||||
elif action_elem:
|
||||
elif action_elem is not None:
|
||||
# We were provided with a single <action> element to perform certain actions after a platform-specific tarball was downloaded.
|
||||
elems = [ action_elem ]
|
||||
else:
|
||||
@@ -546,11 +546,7 @@ def install_via_fabric( app, tool_dependency, install_dir, package_name=None, pr
|
||||
action_dict[ 'directory' ] = action_elem.text
|
||||
else:
|
||||
continue
|
||||
elif action_type in [ 'move_directory_files', 'move_file' ]:
|
||||
# <action type="move_file">
|
||||
# <source>misc/some_file</source>
|
||||
# <destination>$INSTALL_DIR/bin</destination>
|
||||
# </action>
|
||||
elif action_type == 'move_directory_files':
|
||||
# <action type="move_directory_files">
|
||||
# <source_directory>bin</source_directory>
|
||||
# <destination_directory>$INSTALL_DIR/bin</destination_directory>
|
||||
@@ -559,6 +555,14 @@ def install_via_fabric( app, tool_dependency, install_dir, package_name=None, pr
|
||||
move_elem_text = td_common_util.evaluate_template( move_elem.text, install_dir )
|
||||
if move_elem_text:
|
||||
action_dict[ move_elem.tag ] = move_elem_text
|
||||
elif action_type == 'move_file':
|
||||
# <action type="move_file" rename_to="new_file_name">
|
||||
# <source>misc/some_file</source>
|
||||
# <destination>$INSTALL_DIR/bin</destination>
|
||||
# </action>
|
||||
action_dict[ 'source' ] = evaluate_template( action_elem.find( 'source' ).text )
|
||||
action_dict[ 'destination' ] = evaluate_template( action_elem.find( 'destination' ).text )
|
||||
action_dict[ 'rename_to' ] = action_elem.get( 'rename_to' )
|
||||
elif action_type == 'set_environment':
|
||||
# <action type="set_environment">
|
||||
# <environment_variable name="PYTHONPATH" action="append_to">$INSTALL_DIR/lib/python</environment_variable>
|
||||
@@ -607,6 +611,31 @@ def install_via_fabric( app, tool_dependency, install_dir, package_name=None, pr
|
||||
if action_elem.text:
|
||||
configure_opts = td_common_util.evaluate_template( action_elem.text, install_dir )
|
||||
action_dict[ 'configure_opts' ] = configure_opts
|
||||
elif action_type == 'setup_r_environment':
|
||||
# setup an R environment
|
||||
# <action type="setup_r_environment" name="package_r_3_0_1" owner="bgruening">
|
||||
# <package>https://github.com/bgruening/download_store/raw/master/DESeq2-1_0_18/BiocGenerics_0.6.0.tar.gz</package>
|
||||
# </action>
|
||||
|
||||
env_shell_file_paths = td_common_util.get_env_shell_file_paths( app, action_elem.find('repository') )
|
||||
|
||||
all_env_shell_file_paths.extend( env_shell_file_paths )
|
||||
if all_env_shell_file_paths:
|
||||
action_dict[ 'env_shell_file_paths' ] = all_env_shell_file_paths
|
||||
r_packages = list()
|
||||
for env_elem in action_elem:
|
||||
if env_elem.tag == 'package':
|
||||
r_packages.append( env_elem.text.strip() )
|
||||
|
||||
if r_packages:
|
||||
action_dict[ 'r_packages' ] = r_packages
|
||||
else:
|
||||
continue
|
||||
elif action_type == 'make_install':
|
||||
# make; make install; allow providing make options
|
||||
if action_elem.text:
|
||||
make_opts = td_common_util.evaluate_template( action_elem.text, install_dir )
|
||||
action_dict[ 'make_opts' ] = make_opts
|
||||
elif action_type == 'chmod':
|
||||
# Change the read, write, and execute bits on a file.
|
||||
# <action type="chmod">
|
||||
@@ -649,20 +678,6 @@ def install_via_fabric( app, tool_dependency, install_dir, package_name=None, pr
|
||||
else:
|
||||
install_and_build_package_via_fabric( app, tool_dependency, actions_dict )
|
||||
|
||||
def listify( item ):
|
||||
"""
|
||||
Make a single item a single item list, or return a list if passed a
|
||||
list. Passing a None returns an empty list.
|
||||
"""
|
||||
if not item:
|
||||
return []
|
||||
elif isinstance( item, list ):
|
||||
return item
|
||||
elif isinstance( item, basestring ) and item.count( ',' ):
|
||||
return item.split( ',' )
|
||||
else:
|
||||
return [ item ]
|
||||
|
||||
def parse_env_shell_entry( action, name, value, line ):
|
||||
new_value = value
|
||||
var_name = '$%s' % name
|
||||
@@ -820,10 +835,10 @@ def set_environment( app, elem, tool_shed_repository ):
|
||||
type='set_environment',
|
||||
status=app.model.ToolDependency.installation_status.INSTALLING,
|
||||
set_status=True )
|
||||
cmd = td_common_util.create_or_update_env_shell_file( install_dir, env_var_dict )
|
||||
env_entry, env_file = td_common_util.create_or_update_env_shell_file( install_dir, env_var_dict )
|
||||
if env_var_version == '1.0':
|
||||
# Handle setting environment variables using a fabric method.
|
||||
fabric_util.handle_command( app, tool_dependency, install_dir, cmd )
|
||||
fabric_util.file_append( env_entry, env_file, skip_if_contained=True, make_executable=True )
|
||||
sa_session.refresh( tool_dependency )
|
||||
if tool_dependency.status != app.model.ToolDependency.installation_status.ERROR:
|
||||
tool_dependency.status = app.model.ToolDependency.installation_status.INSTALLED
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import tarfile
|
||||
@@ -9,10 +10,116 @@ import zipfile
|
||||
from string import Template
|
||||
import tool_shed.util.shed_util_common as suc
|
||||
from galaxy.datatypes import checkers
|
||||
from urllib2 import HTTPError
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
|
||||
class CompressedFile( object ):
|
||||
|
||||
def __init__( self, file_path, mode='r' ):
|
||||
if istar( file_path ):
|
||||
self.file_type = 'tar'
|
||||
elif iszip( file_path ) and not isjar( file_path ):
|
||||
self.file_type = 'zip'
|
||||
self.file_name = os.path.splitext( os.path.basename( file_path ) )[ 0 ]
|
||||
if self.file_name.endswith( '.tar' ):
|
||||
self.file_name = os.path.splitext( self.file_name )[ 0 ]
|
||||
self.type = self.file_type
|
||||
method = 'open_%s' % self.file_type
|
||||
if hasattr( self, method ):
|
||||
self.archive = getattr( self, method )( file_path, mode )
|
||||
else:
|
||||
raise NameError( 'File type %s specified, no open method found.' % self.file_type )
|
||||
|
||||
def extract( self, path ):
|
||||
'''Determine the path to which the archive should be extracted.'''
|
||||
contents = self.getmembers()
|
||||
extraction_path = path
|
||||
if len( contents ) == 1:
|
||||
# The archive contains a single file, return the extraction path.
|
||||
if self.isfile( contents[ 0 ] ):
|
||||
extraction_path = os.path.join( path, self.file_name )
|
||||
if not os.path.exists( extraction_path ):
|
||||
os.makedirs( extraction_path )
|
||||
self.archive.extractall( extraction_path )
|
||||
else:
|
||||
# Sort filenames within the archive by length, because if the shortest path entry in the archive is a directory,
|
||||
# and the next entry has the directory prepended, then everything following that entry must be within that directory.
|
||||
# For example, consider tarball for BWA 0.5.9:
|
||||
# bwa-0.5.9.tar.bz2:
|
||||
# bwa-0.5.9/
|
||||
# bwa-0.5.9/bwt.c
|
||||
# bwa-0.5.9/bwt.h
|
||||
# bwa-0.5.9/Makefile
|
||||
# bwa-0.5.9/bwt_gen/
|
||||
# bwa-0.5.9/bwt_gen/Makefile
|
||||
# bwa-0.5.9/bwt_gen/bwt_gen.c
|
||||
# bwa-0.5.9/bwt_gen/bwt_gen.h
|
||||
# When sorted by length, one sees a directory at the root of the tarball, and all other tarball contents as
|
||||
# children of that directory.
|
||||
filenames = sorted( [ self.getname( item ) for item in contents ], cmp=lambda a,b: cmp( len( a ), len( b ) ) )
|
||||
parent_name = filenames[ 0 ]
|
||||
parent = self.getmember( parent_name )
|
||||
first_child = filenames[ 1 ]
|
||||
if first_child.startswith( parent_name ) and parent is not None and self.isdir( parent ):
|
||||
if self.getname( parent ) == self.file_name:
|
||||
self.archive.extractall( os.path.join( path ) )
|
||||
extraction_path = os.path.join( path, self.file_name )
|
||||
else:
|
||||
self.archive.extractall( os.path.join( path ) )
|
||||
extraction_path = os.path.join( path, self.getname( parent ) )
|
||||
else:
|
||||
extraction_path = os.path.join( path, self.file_name )
|
||||
if not os.path.exists( extraction_path ):
|
||||
os.makedirs( extraction_path )
|
||||
self.archive.extractall( os.path.join( extraction_path ) )
|
||||
return os.path.abspath( extraction_path )
|
||||
|
||||
def getmembers_tar( self ):
|
||||
return self.archive.getmembers()
|
||||
|
||||
def getmembers_zip( self ):
|
||||
return self.archive.infolist()
|
||||
|
||||
def getname_tar( self, item ):
|
||||
return item.name
|
||||
|
||||
def getname_zip( self, item ):
|
||||
return item.filename
|
||||
|
||||
def getmember( self, name ):
|
||||
for member in self.getmembers():
|
||||
if self.getname( member ) == name:
|
||||
return member
|
||||
|
||||
def getmembers( self ):
|
||||
return getattr( self, 'getmembers_%s' % self.type )()
|
||||
|
||||
def getname( self, member ):
|
||||
return getattr( self, 'getname_%s' % self.type )( member )
|
||||
|
||||
def isdir( self, member ):
|
||||
return getattr( self, 'isdir_%s' % self.type )( member )
|
||||
|
||||
def isdir_tar( self, member ):
|
||||
return member.isdir()
|
||||
|
||||
def isdir_zip( self, member ):
|
||||
if member.filename.endswith( os.sep ):
|
||||
return True
|
||||
return False
|
||||
|
||||
def isfile( self, member ):
|
||||
if not self.isdir( member ):
|
||||
return True
|
||||
return False
|
||||
|
||||
def open_tar( self, filepath, mode ):
|
||||
return tarfile.open( filepath, mode, errorlevel=0 )
|
||||
|
||||
def open_zip( self, filepath, mode ):
|
||||
return zipfile.ZipFile( filepath, mode )
|
||||
|
||||
def clean_tool_shed_url( base_url ):
|
||||
if base_url:
|
||||
if base_url.find( '://' ) > -1:
|
||||
@@ -64,26 +171,9 @@ def create_or_update_env_shell_file( install_dir, env_var_dict ):
|
||||
changed_value = '%s' % env_var_value
|
||||
elif env_var_action == 'append_to':
|
||||
changed_value = '$%s:%s' % ( env_var_name, env_var_value )
|
||||
line = "%s=%s; export %s" % (env_var_name, changed_value, env_var_name)
|
||||
return create_or_update_env_shell_file_with_command(install_dir, line)
|
||||
|
||||
|
||||
def create_or_update_env_shell_file_with_command( install_dir, command ):
|
||||
"""
|
||||
Return a shell expression which when executed will create or update
|
||||
a Galaxy env.sh dependency file in the specified install_dir containing
|
||||
the supplied command.
|
||||
"""
|
||||
env_shell_file_path = '%s/env.sh' % install_dir
|
||||
if os.path.exists( env_shell_file_path ):
|
||||
write_action = '>>'
|
||||
else:
|
||||
write_action = '>'
|
||||
cmd = "echo %s %s %s;chmod +x %s" % ( __shellquote(command),
|
||||
write_action,
|
||||
__shellquote(env_shell_file_path),
|
||||
__shellquote(env_shell_file_path))
|
||||
return cmd
|
||||
line = "%s=%s; export %s" % ( env_var_name, changed_value, env_var_name )
|
||||
env_shell_file_path = os.path.join( install_dir, 'env.sh' )
|
||||
return line, env_shell_file_path
|
||||
|
||||
def download_binary( url, work_dir ):
|
||||
'''
|
||||
@@ -93,29 +183,16 @@ def download_binary( url, work_dir ):
|
||||
dir = url_download( work_dir, downloaded_filename, url, extract=False )
|
||||
return downloaded_filename
|
||||
|
||||
def extract_tar( file_name, file_path ):
|
||||
if isgzip( file_name ) or isbz2( file_name ):
|
||||
# Open for reading with transparent compression.
|
||||
tar = tarfile.open( file_name, 'r:*', errorlevel=0 )
|
||||
else:
|
||||
tar = tarfile.open( file_name, errorlevel=0 )
|
||||
tar.extractall( path=file_path )
|
||||
tar.close()
|
||||
|
||||
def extract_zip( archive_path, extraction_path ):
|
||||
# TODO: change this method to use zipfile.Zipfile.extractall() when we stop supporting Python 2.5.
|
||||
if not zipfile_ok( archive_path ):
|
||||
return False
|
||||
zip_archive = zipfile.ZipFile( archive_path, 'r' )
|
||||
for name in zip_archive.namelist():
|
||||
uncompressed_path = os.path.join( extraction_path, name )
|
||||
if uncompressed_path.endswith( '/' ):
|
||||
if not os.path.isdir( uncompressed_path ):
|
||||
os.makedirs( uncompressed_path )
|
||||
else:
|
||||
file( uncompressed_path, 'wb' ).write( zip_archive.read( name ) )
|
||||
zip_archive.close()
|
||||
return True
|
||||
def egrep_escape( text ):
|
||||
"""Escape ``text`` to allow literal matching using egrep."""
|
||||
regex = re.escape( text )
|
||||
# Seems like double escaping is needed for \
|
||||
regex = regex.replace( '\\\\', '\\\\\\' )
|
||||
# Triple-escaping seems to be required for $ signs
|
||||
regex = regex.replace( r'\$', r'\\\$' )
|
||||
# Whereas single quotes should not be escaped
|
||||
regex = regex.replace( r"\'", "'" )
|
||||
return regex
|
||||
|
||||
def format_traceback():
|
||||
ex_type, ex, tb = sys.exc_info()
|
||||
@@ -233,12 +310,19 @@ def move_directory_files( current_dir, source_dir, destination_dir ):
|
||||
destination_file = os.path.join( destination_directory, file_name )
|
||||
shutil.move( source_file, destination_file )
|
||||
|
||||
def move_file( current_dir, source, destination_dir ):
|
||||
source_file = os.path.abspath( os.path.join( current_dir, source ) )
|
||||
destination_directory = os.path.join( destination_dir )
|
||||
if not os.path.isdir( destination_directory ):
|
||||
def move_file( current_dir, source, destination, rename_to=None ):
|
||||
source_path = os.path.abspath( os.path.join( current_dir, source ) )
|
||||
source_file = os.path.basename( source_path )
|
||||
if rename_to is not None:
|
||||
destination_file = rename_to
|
||||
destination_directory = os.path.join( destination )
|
||||
destination_path = os.path.join( destination_directory, destination_file )
|
||||
else:
|
||||
destination_directory = os.path.join( destination )
|
||||
destination_path = os.path.join( destination_directory, source_file )
|
||||
if not os.path.exists( destination_directory ):
|
||||
os.makedirs( destination_directory )
|
||||
shutil.move( source_file, destination_directory )
|
||||
shutil.move( source_path, destination_path )
|
||||
|
||||
def parse_package_elem( package_elem, platform_info_dict=None, include_after_install_actions=True ):
|
||||
"""
|
||||
@@ -265,11 +349,13 @@ def parse_package_elem( package_elem, platform_info_dict=None, include_after_ins
|
||||
in_actions_group = True
|
||||
# Record the number of <actions> elements so we can filter out any <action> elements that precede <actions> elements.
|
||||
actions_elem_count = len( elem.findall( 'actions' ) )
|
||||
# Record the number of <actions> elements that have architecture and os specified, in order to filter out any platform-independent
|
||||
# <actions> elements that come before platform-specific <actions> elements. This call to elem.findall is filtered by tags that have
|
||||
# both the os and architecture specified. For more details, see http://docs.python.org/2/library/xml.etree.elementtree.html Section
|
||||
# 19.7.2.1.
|
||||
platform_actions_element_count = len( elem.findall( 'actions[@architecture][@os]' ) )
|
||||
# Record the number of <actions> elements that have both architecture and os specified, in order to filter out any
|
||||
# platform-independent <actions> elements that come before platform-specific <actions> elements.
|
||||
platform_actions_elements = []
|
||||
for actions_elem in elem.findall( 'actions' ):
|
||||
if actions_elem.get( 'architecture' ) is not None and actions_elem.get( 'os' ) is not None:
|
||||
platform_actions_elements.append( actions_elem )
|
||||
platform_actions_element_count = len( platform_actions_elements )
|
||||
platform_actions_elements_processed = 0
|
||||
actions_elems_processed = 0
|
||||
# The tag sets that will go into the after_install_actions list are <action> tags instead of <actions> tags. These will be processed
|
||||
@@ -339,19 +425,6 @@ def parse_package_elem( package_elem, platform_info_dict=None, include_after_ins
|
||||
continue
|
||||
return actions_elem_tuples
|
||||
|
||||
def tar_extraction_directory( file_path, file_name ):
|
||||
"""Try to return the correct extraction directory."""
|
||||
file_name = file_name.strip()
|
||||
extensions = [ '.tar.gz', '.tgz', '.tar.bz2', '.tar', '.zip' ]
|
||||
for extension in extensions:
|
||||
if file_name.find( extension ) > 0:
|
||||
dir_name = file_name[ :-len( extension ) ]
|
||||
if os.path.exists( os.path.abspath( os.path.join( file_path, dir_name ) ) ):
|
||||
return dir_name
|
||||
if os.path.exists( os.path.abspath( os.path.join( file_path, file_name ) ) ):
|
||||
return os.path.abspath( file_path )
|
||||
raise ValueError( 'Could not find path to file %s' % os.path.abspath( os.path.join( file_path, file_name ) ) )
|
||||
|
||||
def url_download( install_dir, downloaded_file_name, download_url, extract=True ):
|
||||
file_path = os.path.join( install_dir, downloaded_file_name )
|
||||
src = None
|
||||
@@ -373,32 +446,14 @@ def url_download( install_dir, downloaded_file_name, download_url, extract=True
|
||||
if dst:
|
||||
dst.close()
|
||||
if extract:
|
||||
if istar( file_path ):
|
||||
# <action type="download_by_url">http://sourceforge.net/projects/samtools/files/samtools/0.1.18/samtools-0.1.18.tar.bz2</action>
|
||||
extract_tar( file_path, install_dir )
|
||||
dir = tar_extraction_directory( install_dir, downloaded_file_name )
|
||||
elif isjar( file_path ):
|
||||
dir = os.path.curdir
|
||||
elif iszip( file_path ):
|
||||
# <action type="download_by_url">http://downloads.sourceforge.net/project/picard/picard-tools/1.56/picard-tools-1.56.zip</action>
|
||||
zip_archive_extracted = extract_zip( file_path, install_dir )
|
||||
dir = zip_extraction_directory( install_dir, downloaded_file_name )
|
||||
if istar( file_path ) or iszip( file_path ):
|
||||
archive = CompressedFile( file_path )
|
||||
extraction_path = archive.extract( install_dir )
|
||||
else:
|
||||
dir = os.path.abspath( install_dir )
|
||||
extraction_path = os.path.abspath( install_dir )
|
||||
else:
|
||||
dir = os.path.abspath( install_dir )
|
||||
return dir
|
||||
|
||||
def zip_extraction_directory( file_path, file_name ):
|
||||
"""Try to return the correct extraction directory."""
|
||||
files = [ filename for filename in os.listdir( file_path ) if not filename.endswith( '.zip' ) ]
|
||||
if len( files ) > 1:
|
||||
return os.path.abspath( file_path )
|
||||
elif len( files ) == 1:
|
||||
# If there is only on file it should be a directory.
|
||||
if os.path.isdir( os.path.join( file_path, files[ 0 ] ) ):
|
||||
return os.path.abspath( os.path.join( file_path, files[ 0 ] ) )
|
||||
raise ValueError( 'Could not find directory for the extracted file %s' % os.path.abspath( os.path.join( file_path, file_name ) ) )
|
||||
extraction_path = os.path.abspath( install_dir )
|
||||
return extraction_path
|
||||
|
||||
def zipfile_ok( path_to_archive ):
|
||||
"""
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import os, logging
|
||||
from galaxy.web.framework.helpers import time_ago, grids
|
||||
import logging
|
||||
import os
|
||||
from galaxy.web.framework.helpers import grids
|
||||
from galaxy.web.framework.helpers import time_ago
|
||||
from galaxy.webapps.tool_shed import model
|
||||
from galaxy.model.orm import and_
|
||||
import tool_shed.util.shed_util_common as suc
|
||||
from tool_shed.grids.repository_grids import RepositoryGrid, CategoryGrid
|
||||
from tool_shed.grids.repository_grids import CategoryGrid
|
||||
from tool_shed.grids.repository_grids import RepositoryGrid
|
||||
|
||||
from galaxy import eggs
|
||||
import pkg_resources
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
pkg_resources.require( 'mercurial' )
|
||||
from mercurial import hg, ui, commands
|
||||
from mercurial import commands
|
||||
from mercurial import hg
|
||||
from mercurial import ui
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from tool_shed.util import container_util
|
||||
from tool_shed.util import encoding_util
|
||||
from tool_shed.util import data_manager_util
|
||||
from tool_shed.util import datatype_util
|
||||
from tool_shed.util import tool_dependency_util
|
||||
from tool_shed.util import tool_util
|
||||
from tool_shed.util import xml_util
|
||||
from tool_shed.galaxy_install.tool_dependencies.install_util import install_package
|
||||
@@ -69,16 +70,28 @@ def get_dependencies_for_repository( trans, tool_shed_url, repo_info_dict, inclu
|
||||
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 = {}
|
||||
installed_td = {}
|
||||
missing_rd = {}
|
||||
missing_td = {}
|
||||
name = repo_info_dict.keys()[ 0 ]
|
||||
repo_info_tuple = repo_info_dict[ name ]
|
||||
description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, repository_dependencies, installed_td = \
|
||||
description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, repository_dependencies, tool_dependencies = \
|
||||
suc.get_repo_info_tuple_contents( repo_info_tuple )
|
||||
if tool_dependencies:
|
||||
if not includes_tool_dependencies:
|
||||
includes_tool_dependencies = True
|
||||
# Inspect the tool_dependencies dictionary to separate the installed and missing tool dependencies. We don't add to installed_td
|
||||
# and missing_td here because at this point they are empty.
|
||||
installed_td, missing_td = get_installed_and_missing_tool_dependencies( trans, tool_shed_url, tool_dependencies )
|
||||
# In cases where a repository dependency is required only for compiling a dependent repository's tool dependency, the value of
|
||||
# repository_dependencies will be an empty dictionary here.
|
||||
if repository_dependencies:
|
||||
# We have a repository with one or more defined repository dependencies.
|
||||
missing_td = {}
|
||||
# Handle the scenario where a repository was installed, then uninstalled and an error occurred during the re-installation process.
|
||||
# In this case, a record for the repository will exist in the database with the status of 'New'.
|
||||
repository = suc.get_repository_for_dependency_relationship( trans.app, tool_shed_url, name, repository_owner, changeset_revision )
|
||||
if not repository:
|
||||
repository = suc.get_repository_for_dependency_relationship( trans.app, tool_shed_url, name, repository_owner, changeset_revision )
|
||||
if repository and repository.metadata:
|
||||
installed_rd, missing_rd = get_installed_and_missing_repository_dependencies( trans, repository )
|
||||
else:
|
||||
@@ -86,73 +99,70 @@ def get_dependencies_for_repository( trans, tool_shed_url, repo_info_dict, inclu
|
||||
# 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 )
|
||||
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 )
|
||||
required_repo_info_dicts = all_repo_info_dict.get( 'all_repo_info_dicts', [] )
|
||||
# Display tool dependencies defined for each of the repository dependencies.
|
||||
if required_repo_info_dicts:
|
||||
all_tool_dependencies = {}
|
||||
required_tool_dependencies = {}
|
||||
for rid in required_repo_info_dicts:
|
||||
for name, repo_info_tuple in rid.items():
|
||||
description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, repository_dependencies, rid_installed_td = \
|
||||
description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, rid_repository_dependencies, rid_tool_dependencies = \
|
||||
suc.get_repo_info_tuple_contents( repo_info_tuple )
|
||||
if rid_installed_td:
|
||||
for td_key, td_dict in rid_installed_td.items():
|
||||
if td_key not in all_tool_dependencies:
|
||||
all_tool_dependencies[ td_key ] = td_dict
|
||||
if all_tool_dependencies:
|
||||
if installed_td is None:
|
||||
installed_td = {}
|
||||
else:
|
||||
# Move all tool dependencies to the missing_tool_dependencies container.
|
||||
for td_key, td_dict in installed_td.items():
|
||||
if td_key not in missing_td:
|
||||
missing_td[ td_key ] = td_dict
|
||||
installed_td = {}
|
||||
if rid_tool_dependencies:
|
||||
for td_key, td_dict in rid_tool_dependencies.items():
|
||||
if td_key not in required_tool_dependencies:
|
||||
required_tool_dependencies[ td_key ] = td_dict
|
||||
if required_tool_dependencies:
|
||||
# Discover and categorize all tool dependencies defined for this repository's repository dependencies.
|
||||
required_tool_dependencies, required_missing_tool_dependencies = \
|
||||
get_installed_and_missing_tool_dependencies_for_new_install( trans, all_tool_dependencies )
|
||||
if required_tool_dependencies:
|
||||
required_installed_td, required_missing_td = get_installed_and_missing_tool_dependencies( trans,
|
||||
tool_shed_url,
|
||||
required_tool_dependencies )
|
||||
if required_installed_td:
|
||||
if not includes_tool_dependencies:
|
||||
includes_tool_dependencies = True
|
||||
for td_key, td_dict in required_tool_dependencies.items():
|
||||
for td_key, td_dict in required_installed_td.items():
|
||||
if td_key not in installed_td:
|
||||
installed_td[ td_key ] = td_dict
|
||||
if required_missing_tool_dependencies:
|
||||
if required_missing_td:
|
||||
if not includes_tool_dependencies:
|
||||
includes_tool_dependencies = True
|
||||
for td_key, td_dict in required_missing_tool_dependencies.items():
|
||||
for td_key, td_dict in required_missing_td.items():
|
||||
if td_key not in missing_td:
|
||||
missing_td[ td_key ] = td_dict
|
||||
else:
|
||||
# We have a single repository with no defined repository dependencies.
|
||||
# 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 )
|
||||
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 )
|
||||
required_repo_info_dicts = all_repo_info_dict.get( 'all_repo_info_dicts', [] )
|
||||
installed_rd = None
|
||||
missing_rd = None
|
||||
missing_td = None
|
||||
dependencies_for_repository_dict = dict( changeset_revision=changeset_revision,
|
||||
has_repository_dependencies=has_repository_dependencies,
|
||||
includes_tool_dependencies=includes_tool_dependencies,
|
||||
includes_tools=includes_tools,
|
||||
includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel,
|
||||
installed_repository_dependencies=installed_rd,
|
||||
installed_tool_dependencies=installed_td,
|
||||
missing_repository_dependencies=missing_rd,
|
||||
missing_tool_dependencies=missing_td,
|
||||
name=name,
|
||||
repository_owner=repository_owner )
|
||||
dependencies_for_repository_dict = \
|
||||
dict( changeset_revision=changeset_revision,
|
||||
has_repository_dependencies=has_repository_dependencies,
|
||||
has_repository_dependencies_only_if_compiling_contained_td=has_repository_dependencies_only_if_compiling_contained_td,
|
||||
includes_tool_dependencies=includes_tool_dependencies,
|
||||
includes_tools=includes_tools,
|
||||
includes_tools_for_display_in_tool_panel=includes_tools_for_display_in_tool_panel,
|
||||
installed_repository_dependencies=installed_rd,
|
||||
installed_tool_dependencies=installed_td,
|
||||
missing_repository_dependencies=missing_rd,
|
||||
missing_tool_dependencies=missing_td,
|
||||
name=name,
|
||||
repository_owner=repository_owner )
|
||||
return dependencies_for_repository_dict
|
||||
|
||||
def get_installed_and_missing_repository_dependencies( trans, repository ):
|
||||
"""
|
||||
Return the installed and missing repository dependencies for a tool shed repository that has a record in the Galaxy database, but
|
||||
may or may not be installed. In this case, the repository dependencies are associated with the repository in the database.
|
||||
may or may not be installed. In this case, the repository dependencies are associated with the repository in the database. Do not
|
||||
include a repository dependency if it is required only to compile a tool dependency defined for the dependent repository since these
|
||||
special kinds of repository dependencies are really a dependency of the dependent repository's contained tool dependency, and only if
|
||||
that tool dependency requires compilation.
|
||||
"""
|
||||
missing_repository_dependencies = {}
|
||||
installed_repository_dependencies = {}
|
||||
@@ -166,7 +176,14 @@ def get_installed_and_missing_repository_dependencies( trans, repository ):
|
||||
for tsr in repository.repository_dependencies:
|
||||
prior_installation_required = suc.set_prior_installation_required( repository, tsr )
|
||||
only_if_compiling_contained_td = suc.set_only_if_compiling_contained_td( repository, tsr )
|
||||
rd_tup = [ tsr.tool_shed, tsr.name, tsr.owner, tsr.changeset_revision, prior_installation_required, only_if_compiling_contained_td, tsr.id, tsr.status ]
|
||||
rd_tup = [ tsr.tool_shed,
|
||||
tsr.name,
|
||||
tsr.owner,
|
||||
tsr.changeset_revision,
|
||||
prior_installation_required,
|
||||
only_if_compiling_contained_td,
|
||||
tsr.id,
|
||||
tsr.status ]
|
||||
if tsr.status == trans.model.ToolShedRepository.installation_status.INSTALLED:
|
||||
installed_rd_tups.append( rd_tup )
|
||||
else:
|
||||
@@ -184,7 +201,7 @@ def get_installed_and_missing_repository_dependencies( trans, repository ):
|
||||
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
|
||||
# dictionary for proper display parsing.
|
||||
# dictionaries for proper display parsing.
|
||||
root_key = container_util.generate_repository_dependencies_key_for_repository( repository.tool_shed,
|
||||
repository.name,
|
||||
repository.owner,
|
||||
@@ -210,7 +227,7 @@ def get_installed_and_missing_repository_dependencies_for_new_install( trans, re
|
||||
installed_repository_dependencies = {}
|
||||
missing_rd_tups = []
|
||||
installed_rd_tups = []
|
||||
description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, repository_dependencies, installed_td = \
|
||||
description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, repository_dependencies, tool_dependencies = \
|
||||
suc.get_repo_info_tuple_contents( repo_info_tuple )
|
||||
if repository_dependencies:
|
||||
description = repository_dependencies[ 'description' ]
|
||||
@@ -228,7 +245,7 @@ def get_installed_and_missing_repository_dependencies_for_new_install( trans, re
|
||||
# tuple looks like: ( description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, repository_dependencies, installed_td )
|
||||
tmp_clone_url = suc.generate_clone_url_from_repo_info_tup( rd_tup )
|
||||
tmp_repo_info_tuple = ( None, tmp_clone_url, changeset_revision, None, owner, None, None )
|
||||
repository, current_changeset_revision = suc.repository_was_previously_installed( trans, tool_shed, name, tmp_repo_info_tuple )
|
||||
repository, installed_changeset_revision = suc.repository_was_previously_installed( trans, tool_shed, name, tmp_repo_info_tuple )
|
||||
if repository:
|
||||
new_rd_tup = [ tool_shed,
|
||||
name,
|
||||
@@ -273,29 +290,46 @@ def get_installed_and_missing_repository_dependencies_for_new_install( trans, re
|
||||
missing_repository_dependencies[ 'description' ] = description
|
||||
return installed_repository_dependencies, missing_repository_dependencies
|
||||
|
||||
def get_installed_and_missing_tool_dependencies_for_new_install( trans, all_tool_dependencies ):
|
||||
def get_installed_and_missing_tool_dependencies( trans, tool_shed_url, tool_dependencies_dict ):
|
||||
"""Return the lists of installed tool dependencies and missing tool dependencies for a set of repositories being installed into Galaxy."""
|
||||
# FIXME: confirm that this method currently populates and returns only missing tool dependencies. If so, this method should be enhanced to search for
|
||||
# installed tool dependencies defined as complex repository dependency relationships.
|
||||
if all_tool_dependencies:
|
||||
tool_dependencies = {}
|
||||
missing_tool_dependencies = {}
|
||||
for td_key, val in all_tool_dependencies.items():
|
||||
# Set environment tool dependencies are a list, set each member to never installed.
|
||||
installed_tool_dependencies = {}
|
||||
missing_tool_dependencies = {}
|
||||
if tool_dependencies_dict:
|
||||
for td_key, val in tool_dependencies_dict.items():
|
||||
# Default the status to NEVER_INSTALLED.
|
||||
tool_dependency_status = trans.model.ToolDependency.installation_status.NEVER_INSTALLED
|
||||
# Set environment tool dependencies are a list.
|
||||
if td_key == 'set_environment':
|
||||
new_val = []
|
||||
for requirement_dict in val:
|
||||
requirement_dict[ 'status' ] = trans.model.ToolDependency.installation_status.NEVER_INSTALLED
|
||||
# {'repository_name': 'xx', 'name': 'bwa', 'version': '0.5.9', 'repository_owner': 'yy', 'changeset_revision': 'zz', 'type': 'package'}
|
||||
tool_dependency = tool_dependency_util.get_tool_dependency_by_name_version_type( trans.app,
|
||||
requirement_dict.get( 'name', None ),
|
||||
requirement_dict.get( 'version', None ),
|
||||
requirement_dict.get( 'type', 'package' ) )
|
||||
if tool_dependency:
|
||||
tool_dependency_status = tool_dependency.status
|
||||
requirement_dict[ 'status' ] = tool_dependency_status
|
||||
new_val.append( requirement_dict )
|
||||
missing_tool_dependencies[ td_key ] = new_val
|
||||
if tool_dependency_status in [ trans.model.ToolDependency.installation_status.INSTALLED ]:
|
||||
installed_tool_dependencies[ td_key ] = new_val
|
||||
else:
|
||||
missing_tool_dependencies[ td_key ] = new_val
|
||||
else:
|
||||
# The val dictionary looks something like this:
|
||||
# {'repository_name': 'xx', 'name': 'bwa', 'version': '0.5.9', 'repository_owner': 'yy', 'changeset_revision': 'zz', 'type': 'package'}
|
||||
tool_dependency = tool_dependency_util.get_tool_dependency_by_name_version_type( trans.app,
|
||||
val.get( 'name', None ),
|
||||
val.get( 'version', None ),
|
||||
val.get( 'type', 'package' ) )
|
||||
if tool_dependency:
|
||||
tool_dependency_status = tool_dependency.status
|
||||
val[ 'status' ] = tool_dependency_status
|
||||
if tool_dependency_status in [ trans.model.ToolDependency.installation_status.INSTALLED ]:
|
||||
installed_tool_dependencies[ td_key ] = val
|
||||
else:
|
||||
# Since we have a new install, missing tool dependencies have never been installed.
|
||||
val[ 'status' ] = trans.model.ToolDependency.installation_status.NEVER_INSTALLED
|
||||
missing_tool_dependencies[ td_key ] = val
|
||||
else:
|
||||
tool_dependencies = None
|
||||
missing_tool_dependencies = None
|
||||
return tool_dependencies, missing_tool_dependencies
|
||||
return installed_tool_dependencies, missing_tool_dependencies
|
||||
|
||||
def get_required_repo_info_dicts( trans, tool_shed_url, repo_info_dicts ):
|
||||
"""
|
||||
@@ -318,17 +352,20 @@ def get_required_repo_info_dicts( trans, tool_shed_url, repo_info_dicts ):
|
||||
for key, val in repository_dependencies.items():
|
||||
if key in [ 'root_key', 'description' ]:
|
||||
continue
|
||||
try:
|
||||
toolshed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
|
||||
container_util.get_components_from_key( key )
|
||||
components_list = [ toolshed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td ]
|
||||
except ValueError:
|
||||
# For backward compatibility to the 12/20/12 Galaxy release, default prior_installation_required and only_if_compiling_contained_td
|
||||
# to False in the caller.
|
||||
toolshed, name, owner, changeset_revision = container_util.get_components_from_key( key )
|
||||
components_list = [ toolshed, name, owner, changeset_revision ]
|
||||
repository_components_tuple = container_util.get_components_from_key( key )
|
||||
components_list = suc.extract_components_from_tuple( repository_components_tuple )
|
||||
# Skip listing a repository dependency if it is required only to compile a tool dependency defined for the dependent repository since
|
||||
# in this case, the repository dependency is really a dependency of the dependent repository's contained tool dependency, and only if
|
||||
# that tool dependency requires compilation.
|
||||
# For backward compatibility to the 12/20/12 Galaxy release.
|
||||
prior_installation_required = 'False'
|
||||
only_if_compiling_contained_td = 'False'
|
||||
if len( components_list ) == 4:
|
||||
prior_installation_required = 'False'
|
||||
only_if_compiling_contained_td = 'False'
|
||||
elif len( components_list ) == 5:
|
||||
prior_installation_required = components_list[ 4 ]
|
||||
only_if_compiling_contained_td = 'False'
|
||||
# Skip listing a repository dependency if it is required only to compile a tool dependency defined for the dependent repository.
|
||||
if not util.asbool( only_if_compiling_contained_td ):
|
||||
if components_list not in required_repository_tups:
|
||||
required_repository_tups.append( components_list )
|
||||
@@ -337,8 +374,8 @@ def get_required_repo_info_dicts( trans, tool_shed_url, repo_info_dicts ):
|
||||
only_if_compiling_contained_td = components_list[ 5 ]
|
||||
except:
|
||||
only_if_compiling_contained_td = 'False'
|
||||
# TODO: Fix this to display the tool dependency if only_if_compiling_contained_td is True, but clarify that installation may not
|
||||
# happen.
|
||||
# Skip listing a repository dependency if it is required only to compile a tool dependency defined for the dependent repository
|
||||
# (see above comment).
|
||||
if not util.asbool( only_if_compiling_contained_td ):
|
||||
if components_list not in required_repository_tups:
|
||||
required_repository_tups.append( components_list )
|
||||
@@ -392,6 +429,10 @@ def handle_tool_dependencies( app, tool_shed_repository, tool_dependencies_confi
|
||||
will be installed in:
|
||||
~/<app.config.tool_dependency_dir>/<package_name>/<package_version>/<repo_owner>/<repo_name>/<repo_installed_changeset_revision>
|
||||
"""
|
||||
# The received list of tool_dependencies are the database records for those dependencies defined in the received tool_dependencies_config
|
||||
# that should be installed. This allows for filtering out dependencies that have not been checked for installation on the 'Manage tool
|
||||
# dependencies' page for an installed tool shed repository.
|
||||
attr_tups_of_dependencies_for_install = [ ( td.name, td.version, td.type ) for td in tool_dependencies ]
|
||||
sa_session = app.model.context.current
|
||||
installed_tool_dependencies = []
|
||||
# Parse the tool_dependencies.xml config.
|
||||
@@ -402,19 +443,43 @@ def handle_tool_dependencies( app, tool_shed_repository, tool_dependencies_confi
|
||||
fabric_version_checked = False
|
||||
for elem in root:
|
||||
if elem.tag == 'package':
|
||||
# Only install the tool_dependency if it is not already installed.
|
||||
# Only install the tool_dependency if it is not already installed and it is associated with a database record in the received
|
||||
# tool_dependencies.
|
||||
package_name = elem.get( 'name', None )
|
||||
package_version = elem.get( 'version', None )
|
||||
if package_name and package_version:
|
||||
for tool_dependency in tool_dependencies:
|
||||
if tool_dependency.name==package_name and tool_dependency.version==package_version:
|
||||
break
|
||||
if tool_dependency.can_install:
|
||||
attr_tup = ( package_name, package_version, 'package' )
|
||||
try:
|
||||
index = attr_tups_of_dependencies_for_install.index( attr_tup )
|
||||
except Exception, e:
|
||||
index = None
|
||||
if index is not None:
|
||||
tool_dependency = tool_dependencies[ index ]
|
||||
if tool_dependency.can_install:
|
||||
try:
|
||||
tool_dependency = install_package( app, elem, tool_shed_repository, tool_dependencies=tool_dependencies )
|
||||
except Exception, e:
|
||||
error_message = "Error installing tool dependency %s version %s: %s" % ( str( package_name ), str( package_version ), str( e ) )
|
||||
log.exception( error_message )
|
||||
if tool_dependency:
|
||||
tool_dependency.status = app.model.ToolDependency.installation_status.ERROR
|
||||
tool_dependency.error_message = error_message
|
||||
sa_session.add( tool_dependency )
|
||||
sa_session.flush()
|
||||
if tool_dependency and tool_dependency.status in [ app.model.ToolDependency.installation_status.INSTALLED,
|
||||
app.model.ToolDependency.installation_status.ERROR ]:
|
||||
installed_tool_dependencies.append( tool_dependency )
|
||||
elif elem.tag == 'set_environment':
|
||||
env_var_name = env_var_elem.get( 'name', None )
|
||||
if env_var_name:
|
||||
# Tool dependencies of type "set_environmnet" always have the version attribute set to None.
|
||||
attr_tup = ( env_var_name, None, 'set_environment' )
|
||||
if attr_tup in attr_tups_of_dependencies_for_install:
|
||||
try:
|
||||
tool_dependency = install_package( app, elem, tool_shed_repository, tool_dependencies=tool_dependencies )
|
||||
tool_dependency = set_environment( app, elem, tool_shed_repository )
|
||||
except Exception, e:
|
||||
error_message = "Error installing tool dependency %s version %s: %s" % ( str( package_name ), str( package_version ), str( e ) )
|
||||
log.exception( error_message )
|
||||
error_message = "Error setting environment for tool dependency: %s" % str( e )
|
||||
log.debug( error_message )
|
||||
if tool_dependency:
|
||||
tool_dependency.status = app.model.ToolDependency.installation_status.ERROR
|
||||
tool_dependency.error_message = error_message
|
||||
@@ -423,20 +488,6 @@ def handle_tool_dependencies( app, tool_shed_repository, tool_dependencies_confi
|
||||
if tool_dependency and tool_dependency.status in [ app.model.ToolDependency.installation_status.INSTALLED,
|
||||
app.model.ToolDependency.installation_status.ERROR ]:
|
||||
installed_tool_dependencies.append( tool_dependency )
|
||||
elif elem.tag == 'set_environment':
|
||||
try:
|
||||
tool_dependency = set_environment( app, elem, tool_shed_repository )
|
||||
except Exception, e:
|
||||
error_message = "Error setting environment for tool dependency: %s" % str( e )
|
||||
log.debug( error_message )
|
||||
if tool_dependency:
|
||||
tool_dependency.status = app.model.ToolDependency.installation_status.ERROR
|
||||
tool_dependency.error_message = error_message
|
||||
sa_session.add( tool_dependency )
|
||||
sa_session.flush()
|
||||
if tool_dependency and tool_dependency.status in [ app.model.ToolDependency.installation_status.INSTALLED,
|
||||
app.model.ToolDependency.installation_status.ERROR ]:
|
||||
installed_tool_dependencies.append( tool_dependency )
|
||||
return installed_tool_dependencies
|
||||
|
||||
def repository_dependency_needed_only_for_compiling_tool_dependency( repository, repository_dependency ):
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import logging
|
||||
import os
|
||||
import urllib2
|
||||
from galaxy.util import json
|
||||
@@ -5,6 +6,8 @@ from galaxy.util.odict import odict
|
||||
from tool_shed.util import encoding_util
|
||||
from tool_shed.util import xml_util
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
REPOSITORY_OWNER = 'devteam'
|
||||
|
||||
def accumulate_tool_dependencies( tool_shed_accessible, tool_dependencies, all_tool_dependencies ):
|
||||
|
||||
@@ -573,27 +573,23 @@ def build_invalid_tools_folder( trans, folder_id, invalid_tool_configs, changese
|
||||
def build_readme_files_folder( trans, folder_id, readme_files_dict, label='Readme files' ):
|
||||
"""Return a folder hierarchy containing readme text files."""
|
||||
if readme_files_dict:
|
||||
multiple_readme_files = len( readme_files_dict ) > 1
|
||||
readme_id = 0
|
||||
folder_id += 1
|
||||
readme_files_root_folder = Folder( id=folder_id, key='root', label='root', parent=None )
|
||||
if multiple_readme_files:
|
||||
folder_id += 1
|
||||
readme_files_folder = Folder( id=folder_id, key='readme_files', label=label, parent=readme_files_root_folder )
|
||||
readme_files_root_folder.folders.append( readme_files_folder )
|
||||
folder_id += 1
|
||||
readme_files_folder = Folder( id=folder_id, key='readme_files', label=label, parent=readme_files_root_folder )
|
||||
multiple_readme_files = len( readme_files_dict ) > 1
|
||||
readme_files_root_folder.folders.append( readme_files_folder )
|
||||
for readme_file_name, readme_file_text in readme_files_dict.items():
|
||||
readme_id += 1
|
||||
readme = ReadMe( id=readme_id, name=readme_file_name, text=readme_file_text )
|
||||
if multiple_readme_files:
|
||||
folder_id += 1
|
||||
folder = Folder( id=folder_id, key=readme.name, label=readme.name, parent=readme_files_folder )
|
||||
folder = Folder( id=folder_id, key=readme_file_name, label=readme_file_name, parent=readme_files_folder )
|
||||
folder.readme_files.append( readme )
|
||||
readme_files_folder.folders.append( folder )
|
||||
else:
|
||||
folder_id += 1
|
||||
readme_files_folder = Folder( id=folder_id, key='readme_files', label=readme.name, parent=readme_files_root_folder )
|
||||
readme_files_folder.readme_files.append( readme )
|
||||
readme_files_root_folder.folders.append( readme_files_folder )
|
||||
else:
|
||||
readme_files_root_folder = None
|
||||
return folder_id, readme_files_root_folder
|
||||
@@ -788,7 +784,7 @@ def build_repository_containers_for_tool_shed( trans, repository, changeset_revi
|
||||
# Readme files container.
|
||||
if metadata:
|
||||
if 'readme_files' not in exclude and 'readme_files' in metadata:
|
||||
readme_files_dict = readme_util.build_readme_files_dict( metadata )
|
||||
readme_files_dict = readme_util.build_readme_files_dict( trans, repository, changeset_revision, metadata )
|
||||
folder_id, readme_files_root_folder = build_readme_files_folder( trans, folder_id, readme_files_dict )
|
||||
containers_dict[ 'readme_files' ] = readme_files_root_folder
|
||||
if 'repository_dependencies' not in exclude:
|
||||
@@ -855,7 +851,7 @@ def build_repository_containers_for_tool_shed( trans, repository, changeset_revi
|
||||
folder_id, data_managers_root_folder = build_invalid_data_managers_folder( trans, folder_id, data_managers, error_messages, label="Invalid Data Managers" )
|
||||
containers_dict[ 'invalid_data_managers' ] = data_managers_root_folder
|
||||
except Exception, e:
|
||||
log.debug( "Exception in build_repository_containers_for_tool_shed: %s" % str( e ) )
|
||||
log.exception( "Exception in build_repository_containers_for_tool_shed: %s" % str( e ) )
|
||||
finally:
|
||||
lock.release()
|
||||
return containers_dict
|
||||
@@ -1325,29 +1321,31 @@ def get_components_from_key( key ):
|
||||
repository_name = items[ 1 ]
|
||||
repository_owner = items[ 2 ]
|
||||
changeset_revision = items[ 3 ]
|
||||
if len( items ) >= 5:
|
||||
try:
|
||||
prior_installation_required = items[ 4 ]
|
||||
except:
|
||||
prior_installation_required = 'False'
|
||||
try:
|
||||
only_if_compiling_contained_td = items[ 5 ]
|
||||
except:
|
||||
only_if_compiling_contained_td = 'False'
|
||||
if len( items ) == 5:
|
||||
prior_installation_required = items[ 4 ]
|
||||
return toolshed_base_url, repository_name, repository_owner, changeset_revision, prior_installation_required
|
||||
elif len( items ) == 6:
|
||||
prior_installation_required = items[ 4 ]
|
||||
only_if_compiling_contained_td = items[ 5 ]
|
||||
return toolshed_base_url, repository_name, repository_owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td
|
||||
else:
|
||||
# For backward compatibility to the 12/20/12 Galaxy release we have to return the following, and callers must handle exceptions.
|
||||
return toolshed_base_url, repository_name, repository_owner, changeset_revision
|
||||
|
||||
def handle_repository_dependencies_container_entry( trans, repository_dependencies_folder, rd_key, rd_value, folder_id, repository_dependency_id, folder_keys ):
|
||||
try:
|
||||
toolshed, repository_name, repository_owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
|
||||
get_components_from_key( rd_key )
|
||||
except ValueError:
|
||||
# For backward compatibility to the 12/20/12 Galaxy release, default prior_installation_required and only_if_compiling_contained_td to 'False'.
|
||||
toolshed, repository_name, repository_owner, changeset_revision = get_components_from_key( rd_key )
|
||||
repository_components_tuple = get_components_from_key( rd_key )
|
||||
components_list = suc.extract_components_from_tuple( repository_components_tuple )
|
||||
toolshed, repository_name, repository_owner, changeset_revision = components_list[ 0:4 ]
|
||||
# For backward compatibility to the 12/20/12 Galaxy release.
|
||||
if len( components_list ) == 4:
|
||||
prior_installation_required = 'False'
|
||||
only_if_compiling_contained_td = 'False'
|
||||
elif len( components_list ) == 5:
|
||||
prior_installation_required = components_list[ 4 ]
|
||||
only_if_compiling_contained_td = 'False'
|
||||
elif len( components_list ) == 6:
|
||||
prior_installation_required = components_list[ 4 ]
|
||||
only_if_compiling_contained_td = components_list[ 5 ]
|
||||
folder = get_folder( repository_dependencies_folder, rd_key )
|
||||
label = generate_repository_dependencies_folder_label_from_key( repository_name,
|
||||
repository_owner,
|
||||
@@ -1416,14 +1414,19 @@ def is_subfolder_of( folder, repository_dependency ):
|
||||
return False
|
||||
|
||||
def key_is_current_repositorys_key( repository_name, repository_owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td, key ):
|
||||
try:
|
||||
toolshed_base_url, key_name, key_owner, key_changeset_revision, key_prior_installation_required, key_only_if_compiling_contained_td = \
|
||||
get_components_from_key( key )
|
||||
except ValueError:
|
||||
# For backward compatibility to the 12/20/12 Galaxy release, default key_prior_installation_required to False.
|
||||
toolshed_base_url, key_name, key_owner, key_changeset_revision = get_components_from_key( key )
|
||||
repository_components_tuple = get_components_from_key( key )
|
||||
components_list = suc.extract_components_from_tuple( repository_components_tuple )
|
||||
toolshed, key_name, key_owner, key_changeset_revision = components_list[ 0:4 ]
|
||||
# For backward compatibility to the 12/20/12 Galaxy release.
|
||||
if len( components_list ) == 4:
|
||||
key_prior_installation_required = 'False'
|
||||
key_only_if_compiling_contained_td = 'False'
|
||||
elif len( components_list ) == 5:
|
||||
key_prior_installation_required = components_list[ 4 ]
|
||||
key_only_if_compiling_contained_td = 'False'
|
||||
elif len( components_list ) == 6:
|
||||
key_prior_installation_required = components_list[ 4 ]
|
||||
key_only_if_compiling_contained_td = components_list[ 5 ]
|
||||
if repository_name == key_name and \
|
||||
repository_owner == key_owner and \
|
||||
changeset_revision == key_changeset_revision and \
|
||||
|
||||
@@ -23,9 +23,8 @@ from tool_shed.galaxy_install.tool_dependencies import install_util
|
||||
from tool_shed.galaxy_install.tool_dependencies import td_common_util
|
||||
import tool_shed.repository_types.util as rt_util
|
||||
|
||||
import pkg_resources
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
pkg_resources.require( 'mercurial' )
|
||||
from mercurial import commands
|
||||
from mercurial import hg
|
||||
from mercurial import ui
|
||||
@@ -280,13 +279,16 @@ def compare_workflows( trans, ancestor_workflows, current_workflows ):
|
||||
def create_or_update_repository_metadata( trans, id, repository, changeset_revision, metadata_dict ):
|
||||
"""Create or update a repository_metadatqa record in the tool shed."""
|
||||
has_repository_dependencies = False
|
||||
has_repository_dependencies_only_if_compiling_contained_td = False
|
||||
includes_datatypes = False
|
||||
includes_tools = False
|
||||
includes_tool_dependencies = False
|
||||
includes_workflows = False
|
||||
if metadata_dict:
|
||||
if 'repository_dependencies' in metadata_dict:
|
||||
has_repository_dependencies = True
|
||||
repository_dependencies_dict = metadata_dict.get( 'repository_dependencies', {} )
|
||||
repository_dependencies = repository_dependencies_dict.get( 'repository_dependencies', [] )
|
||||
has_repository_dependencies, has_repository_dependencies_only_if_compiling_contained_td = \
|
||||
suc.get_repository_dependency_types( repository_dependencies )
|
||||
if 'datatypes' in metadata_dict:
|
||||
includes_datatypes = True
|
||||
if 'tools' in metadata_dict:
|
||||
@@ -295,7 +297,11 @@ def create_or_update_repository_metadata( trans, id, repository, changeset_revis
|
||||
includes_tool_dependencies = True
|
||||
if 'workflows' in metadata_dict:
|
||||
includes_workflows = True
|
||||
downloadable = has_repository_dependencies or includes_datatypes or includes_tools or includes_tool_dependencies or includes_workflows
|
||||
if has_repository_dependencies or has_repository_dependencies_only_if_compiling_contained_td or includes_datatypes or \
|
||||
includes_tools or includes_tool_dependencies or includes_workflows:
|
||||
downloadable = True
|
||||
else:
|
||||
downloadable = False
|
||||
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, id, changeset_revision )
|
||||
if repository_metadata:
|
||||
# A repository metadata record already exists with the received changeset_revision, so we don't need to check the skip_tool_test table.
|
||||
@@ -583,7 +589,7 @@ def generate_metadata_for_changeset_revision( app, repository, changeset_revisio
|
||||
original_repository_metadata = repository.metadata
|
||||
else:
|
||||
original_repository_metadata = None
|
||||
readme_file_names = get_readme_file_names( repository.name )
|
||||
readme_file_names = readme_util.get_readme_file_names( str( repository.name ) )
|
||||
if app.name == 'galaxy':
|
||||
# Shed related tool panel configs are only relevant to Galaxy.
|
||||
metadata_dict = { 'shed_config_filename' : shed_config_dict.get( 'config_filename' ) }
|
||||
@@ -704,8 +710,16 @@ def generate_metadata_for_changeset_revision( app, repository, changeset_revisio
|
||||
fp = open( relative_path, 'rb' )
|
||||
workflow_text = fp.read()
|
||||
fp.close()
|
||||
exported_workflow_dict = json.from_json_string( workflow_text )
|
||||
if 'a_galaxy_workflow' in exported_workflow_dict and exported_workflow_dict[ 'a_galaxy_workflow' ] == 'true':
|
||||
if workflow_text:
|
||||
valid_exported_galaxy_workflow = True
|
||||
try:
|
||||
exported_workflow_dict = json.from_json_string( workflow_text )
|
||||
except Exception, e:
|
||||
log.exception( "Skipping file %s since it does not seem to be a valid exported Galaxy workflow: %s" \
|
||||
% str( relative_path ), str( e ) )
|
||||
valid_exported_galaxy_workflow = False
|
||||
if valid_exported_galaxy_workflow and \
|
||||
'a_galaxy_workflow' in exported_workflow_dict and exported_workflow_dict[ 'a_galaxy_workflow' ] == 'true':
|
||||
metadata_dict = generate_workflow_metadata( relative_path, exported_workflow_dict, metadata_dict )
|
||||
# Handle any data manager entries
|
||||
metadata_dict = generate_data_manager_metadata( app,
|
||||
@@ -1040,16 +1054,6 @@ def get_parent_id( trans, id, old_id, version, guid, changeset_revisions ):
|
||||
# The tool did not change through all of the changeset revisions.
|
||||
return old_id
|
||||
|
||||
def get_readme_file_names( repository_name ):
|
||||
"""Creates a list of valid filenames."""
|
||||
readme_files = [ 'readme', 'read_me', 'install' ]
|
||||
valid_filenames = map( lambda f: '%s.txt' % f, readme_files )
|
||||
valid_filenames.extend( map( lambda f: '%s.rst' % f, readme_files ) )
|
||||
valid_filenames.extend( readme_files )
|
||||
valid_filenames.append( '%s.txt' % repository_name )
|
||||
valid_filenames.append( '%s.rst' % repository_name )
|
||||
return valid_filenames
|
||||
|
||||
def get_relative_path_to_repository_file( root, name, relative_install_dir, work_dir, shed_config_dict, resetting_all_metadata_on_repository ):
|
||||
if resetting_all_metadata_on_repository:
|
||||
full_path_to_file = os.path.join( root, name )
|
||||
@@ -1068,13 +1072,15 @@ def get_repository_metadata_by_id( trans, id ):
|
||||
"""Get repository metadata from the database"""
|
||||
return trans.sa_session.query( trans.model.RepositoryMetadata ).get( trans.security.decode_id( id ) )
|
||||
|
||||
def get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision ):
|
||||
"""Get a specified metadata record for a specified repository."""
|
||||
return trans.sa_session.query( trans.model.RepositoryMetadata ) \
|
||||
.filter( and_( trans.model.RepositoryMetadata.table.c.repository_id == trans.security.decode_id( id ),
|
||||
trans.model.RepositoryMetadata.table.c.changeset_revision == changeset_revision ) ) \
|
||||
.first()
|
||||
|
||||
def get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision, metadata_only=False ):
|
||||
"""Get a specified metadata record for a specified repository in the tool shed."""
|
||||
if metadata_only:
|
||||
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, id, changeset_revision )
|
||||
if repository_metadata and repository_metadata.metadata:
|
||||
return repository_metadata.metadata
|
||||
return None
|
||||
return suc.get_repository_metadata_by_changeset_revision( trans, id, changeset_revision )
|
||||
|
||||
def get_repository_metadata_revisions_for_review( repository, reviewed=True ):
|
||||
repository_metadata_revisions = []
|
||||
metadata_changeset_revision_hashes = []
|
||||
@@ -1232,21 +1238,31 @@ def handle_repository_elem( app, repository_elem, only_if_compiling_contained_td
|
||||
log.debug( error_message )
|
||||
is_valid = False
|
||||
return repository_dependency_tup, is_valid, error_message
|
||||
# Find the specified changeset revision in the repository's changelog to see if it's valid.
|
||||
found = False
|
||||
repo = hg.repository( suc.get_configured_ui(), repository.repo_path( app ) )
|
||||
for changeset in repo.changelog:
|
||||
changeset_hash = str( repo.changectx( changeset ) )
|
||||
if changeset_hash == changeset_revision:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
error_message = "Ignoring repository dependency definition for tool shed %s, name %s, owner %s, changeset revision %s "% \
|
||||
( toolshed, name, owner, changeset_revision )
|
||||
error_message += "because the changeset revision is invalid. "
|
||||
log.debug( error_message )
|
||||
is_valid = False
|
||||
# The received changeset_revision may be None since defining it in the dependency definition is optional. If this is the case,
|
||||
# the default will be to set it's value to the repository dependency tip revision. This probably occurs only when handling
|
||||
# circular dependency definitions.
|
||||
tip_ctx = repo.changectx( repo.changelog.tip() )
|
||||
# Make sure the repo.changlog includes at least 1 revision.
|
||||
if changeset_revision is None and tip_ctx.rev() >= 0:
|
||||
changeset_revision = str( tip_ctx )
|
||||
repository_dependency_tup = [ toolshed, name, owner, changeset_revision, prior_installation_required, str( only_if_compiling_contained_td ) ]
|
||||
return repository_dependency_tup, is_valid, error_message
|
||||
else:
|
||||
# Find the specified changeset revision in the repository's changelog to see if it's valid.
|
||||
found = False
|
||||
for changeset in repo.changelog:
|
||||
changeset_hash = str( repo.changectx( changeset ) )
|
||||
if changeset_hash == changeset_revision:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
error_message = "Ignoring repository dependency definition for tool shed %s, name %s, owner %s, changeset revision %s "% \
|
||||
( toolshed, name, owner, changeset_revision )
|
||||
error_message += "because the changeset revision is invalid. "
|
||||
log.debug( error_message )
|
||||
is_valid = False
|
||||
return repository_dependency_tup, is_valid, error_message
|
||||
else:
|
||||
# Repository dependencies are currently supported within a single tool shed.
|
||||
error_message = "Repository dependencies are currently supported only within the same tool shed. Ignoring repository dependency definition "
|
||||
@@ -1524,7 +1540,7 @@ def populate_containers_dict_from_repository_metadata( trans, tool_shed_url, too
|
||||
raw_text = common_util.tool_shed_get( trans.app, tool_shed_url, url )
|
||||
readme_files_dict = json.from_json_string( raw_text )
|
||||
else:
|
||||
readme_files_dict = readme_util.build_readme_files_dict( repository.metadata, tool_path )
|
||||
readme_files_dict = readme_util.build_readme_files_dict( trans, repository, repository.changeset_revision, repository.metadata, tool_path )
|
||||
else:
|
||||
readme_files_dict = None
|
||||
# Handle repository dependencies.
|
||||
@@ -1851,10 +1867,13 @@ def set_repository_metadata( trans, repository, content_alert_str='', **kwd ):
|
||||
repository_metadata.includes_datatypes = True
|
||||
else:
|
||||
repository_metadata.includes_datatypes = False
|
||||
if 'repository_dependencies' in metadata_dict:
|
||||
repository_metadata.has_repository_dependencies = True
|
||||
else:
|
||||
repository_metadata.has_repository_dependencies = False
|
||||
# We don't store information about the special type of repository dependency that is needed only for compiling a tool dependency
|
||||
# defined for the dependent repository.
|
||||
repository_dependencies_dict = metadata_dict.get( 'repository_dependencies', {} )
|
||||
repository_dependencies = repository_dependencies_dict.get( 'repository_dependencies', [] )
|
||||
has_repository_dependencies, has_repository_dependencies_only_if_compiling_contained_td = \
|
||||
suc.get_repository_dependency_types( repository_dependencies )
|
||||
repository_metadata.has_repository_dependencies = has_repository_dependencies
|
||||
if 'tool_dependencies' in metadata_dict:
|
||||
repository_metadata.includes_tool_dependencies = True
|
||||
else:
|
||||
@@ -1886,7 +1905,7 @@ def set_repository_metadata( trans, repository, content_alert_str='', **kwd ):
|
||||
changeset_revisions.append( changeset_revision )
|
||||
add_tool_versions( trans, encoded_id, repository_metadata, changeset_revisions )
|
||||
elif len( repo ) == 1 and not invalid_file_tups:
|
||||
message = "Revision '%s' includes no tools, datatypes or exported workflows for which metadata can " % str( repository.tip( trans.app ) )
|
||||
message = "Revision <b>%s</b> includes no Galaxy utilities for which metadata can " % str( repository.tip( trans.app ) )
|
||||
message += "be defined so this revision cannot be automatically installed into a local Galaxy instance."
|
||||
status = "error"
|
||||
if invalid_file_tups:
|
||||
|
||||
@@ -1,30 +1,89 @@
|
||||
import logging
|
||||
import os
|
||||
import threading
|
||||
from mako.template import Template
|
||||
from galaxy import eggs
|
||||
from galaxy import web
|
||||
from galaxy.util import json
|
||||
from galaxy.util import rst_to_html
|
||||
from galaxy.util import unicodify
|
||||
import tool_shed.util.shed_util_common as suc
|
||||
from tool_shed.util import common_util
|
||||
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
from mercurial import hg
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
def build_readme_files_dict( metadata, tool_path=None ):
|
||||
"""Return a dictionary of valid readme file name <-> readme file content pairs for all readme files contained in the received metadata."""
|
||||
def build_readme_files_dict( trans, repository, changeset_revision, metadata, tool_path=None ):
|
||||
"""
|
||||
Return a dictionary of valid readme file name <-> readme file content pairs for all readme files defined in the received metadata. Since the
|
||||
received changeset_revision (which is associated with the received metadata) may not be the latest installable changeset revision, the README
|
||||
file contents may not be available on disk. This method is used by both Galaxy and the Tool Shed.
|
||||
"""
|
||||
if trans.webapp.name == 'galaxy':
|
||||
can_use_disk_files = True
|
||||
else:
|
||||
repo = hg.repository( suc.get_configured_ui(), repository.repo_path( trans.app ) )
|
||||
latest_downloadable_changeset_revision = suc.get_latest_downloadable_changeset_revision( trans, repository, repo )
|
||||
can_use_disk_files = changeset_revision == latest_downloadable_changeset_revision
|
||||
readme_files_dict = {}
|
||||
if metadata:
|
||||
if 'readme_files' in metadata:
|
||||
for relative_path_to_readme_file in metadata[ 'readme_files' ]:
|
||||
readme_file_name = os.path.split( relative_path_to_readme_file )[ 1 ]
|
||||
if tool_path:
|
||||
full_path_to_readme_file = os.path.abspath( os.path.join( tool_path, relative_path_to_readme_file ) )
|
||||
if can_use_disk_files:
|
||||
if tool_path:
|
||||
full_path_to_readme_file = os.path.abspath( os.path.join( tool_path, relative_path_to_readme_file ) )
|
||||
else:
|
||||
full_path_to_readme_file = os.path.abspath( relative_path_to_readme_file )
|
||||
text = None
|
||||
try:
|
||||
f = open( full_path_to_readme_file, 'r' )
|
||||
text = unicodify( f.read() )
|
||||
f.close()
|
||||
except Exception, e:
|
||||
log.exception( "Error reading README file '%s' from disk: %s" % ( str( relative_path_to_readme_file ), str( e ) ) )
|
||||
text = None
|
||||
if text:
|
||||
text_of_reasonable_length = suc.size_string( text )
|
||||
if text_of_reasonable_length.find( '.. image:: ' ) >= 0:
|
||||
# Handle image display for README files that are contained in repositories in the tool shed or installed into Galaxy.
|
||||
lock = threading.Lock()
|
||||
lock.acquire( True )
|
||||
try:
|
||||
text_of_reasonable_length = suc.set_image_paths( trans.app,
|
||||
trans.security.encode_id( repository.id ),
|
||||
text_of_reasonable_length )
|
||||
except Exception, e:
|
||||
log.exception( "Exception in build_readme_files_dict, so images may not be properly displayed:\n%s" % str( e ) )
|
||||
finally:
|
||||
lock.release()
|
||||
if readme_file_name.endswith( '.rst' ):
|
||||
text_of_reasonable_length = Template( rst_to_html( text_of_reasonable_length ),
|
||||
input_encoding='utf-8',
|
||||
output_encoding='utf-8',
|
||||
default_filters=[ 'decode.utf8' ],
|
||||
encoding_errors='replace' )
|
||||
text_of_reasonable_length = text_of_reasonable_length.render( static_path=web.url_for( '/static' ),
|
||||
host_url=web.url_for( '/', qualified=True ) )
|
||||
text_of_reasonable_length = unicodify( text_of_reasonable_length )
|
||||
else:
|
||||
text_of_reasonable_length = suc.to_html_string( text_of_reasonable_length )
|
||||
readme_files_dict[ readme_file_name ] = text_of_reasonable_length
|
||||
else:
|
||||
full_path_to_readme_file = os.path.abspath( relative_path_to_readme_file )
|
||||
try:
|
||||
f = open( full_path_to_readme_file, 'r' )
|
||||
text = unicodify( f.read() )
|
||||
f.close()
|
||||
readme_files_dict[ readme_file_name ] = suc.size_string( text )
|
||||
except Exception, e:
|
||||
log.debug( "Error reading README file '%s' defined in metadata: %s" % ( str( relative_path_to_readme_file ), str( e ) ) )
|
||||
# We must be in the tool shed and have an old changeset_revision, so we need to retrieve the file contents from the repository manifest.
|
||||
ctx = suc.get_changectx_for_changeset( repo, changeset_revision )
|
||||
if ctx:
|
||||
fctx = suc.get_file_context_from_ctx( ctx, readme_file_name )
|
||||
if fctx and fctx not in [ 'DELETED' ]:
|
||||
try:
|
||||
text = unicodify( fctx.data() )
|
||||
readme_files_dict[ readme_file_name ] = suc.size_string( text )
|
||||
except Exception, e:
|
||||
log.exception( "Error reading README file '%s' from repository manifest: %s" % \
|
||||
( str( relative_path_to_readme_file ), str( e ) ) )
|
||||
return readme_files_dict
|
||||
|
||||
def get_readme_files_dict_for_display( trans, tool_shed_url, repo_info_dict ):
|
||||
@@ -42,3 +101,13 @@ def get_readme_files_dict_for_display( trans, tool_shed_url, repo_info_dict ):
|
||||
raw_text = common_util.tool_shed_get( trans.app, tool_shed_url, url )
|
||||
readme_files_dict = json.from_json_string( raw_text )
|
||||
return readme_files_dict
|
||||
|
||||
def get_readme_file_names( repository_name ):
|
||||
"""Return a list of file names that will be categorized as README files for the received repository_name."""
|
||||
readme_files = [ 'readme', 'read_me', 'install' ]
|
||||
valid_filenames = map( lambda f: '%s.txt' % f, readme_files )
|
||||
valid_filenames.extend( map( lambda f: '%s.rst' % f, readme_files ) )
|
||||
valid_filenames.extend( readme_files )
|
||||
valid_filenames.append( '%s.txt' % repository_name )
|
||||
valid_filenames.append( '%s.rst' % repository_name )
|
||||
return valid_filenames
|
||||
|
||||
@@ -12,9 +12,8 @@ from tool_shed.util import encoding_util
|
||||
from tool_shed.util import metadata_util
|
||||
from tool_shed.util import tool_util
|
||||
|
||||
import pkg_resources
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
pkg_resources.require( 'mercurial' )
|
||||
from mercurial import commands
|
||||
from mercurial import hg
|
||||
from mercurial import ui
|
||||
@@ -36,12 +35,9 @@ def build_repository_dependency_relationships( trans, repo_info_dicts, tool_shed
|
||||
if key in [ 'root_key', 'description' ]:
|
||||
continue
|
||||
d_repository = None
|
||||
try:
|
||||
d_toolshed, d_name, d_owner, d_changeset_revision, d_prior_installation_required, d_only_if_compiling_contained_td = \
|
||||
container_util.get_components_from_key( key )
|
||||
except ValueError:
|
||||
# For backward compatibility to the 12/20/12 Galaxy release.
|
||||
d_toolshed, d_name, d_owner, d_changeset_revision = container_util.get_components_from_key( key )
|
||||
repository_components_tuple = container_util.get_components_from_key( key )
|
||||
components_list = suc.extract_components_from_tuple( repository_components_tuple )
|
||||
d_toolshed, d_name, d_owner, d_changeset_revision = components_list[ 0:4 ]
|
||||
for tsr in tool_shed_repositories:
|
||||
# Get the the tool_shed_repository defined by name, owner and changeset_revision. This is the repository that will be
|
||||
# dependent upon each of the tool shed repositories contained in val. We'll need to check tool_shed_repository.tool_shed
|
||||
@@ -99,154 +95,116 @@ def can_add_to_key_rd_dicts( key_rd_dict, key_rd_dicts ):
|
||||
return False
|
||||
return True
|
||||
|
||||
def create_repository_dependency_objects( trans, tool_path, tool_shed_url, repo_info_dicts, reinstalling=False, install_repository_dependencies=False,
|
||||
def create_repository_dependency_objects( trans, tool_path, tool_shed_url, repo_info_dicts, install_repository_dependencies=False,
|
||||
no_changes_checked=False, tool_panel_section=None, new_tool_panel_section=None ):
|
||||
"""
|
||||
Discover all repository dependencies and make sure all tool_shed_repository and associated repository_dependency records exist as well as
|
||||
the dependency relationships between installed repositories. This method is called when new repositories are being installed into a Galaxy
|
||||
instance and when uninstalled repositories are being reinstalled.
|
||||
the dependency relationships between installed repositories. This method is called when uninstalled repositories are being reinstalled.
|
||||
If the user elected to install repository dependencies, all items in the all_repo_info_dicts list will be processed. However, if repository
|
||||
dependencies are not to be installed, only those items contained in the received repo_info_dicts list will be processed.
|
||||
"""
|
||||
# The following list will be maintained within this method to contain all created or updated tool shed repositories, including repository dependencies
|
||||
# that may not be installed.
|
||||
# The following list will be maintained within this method to contain all created or updated tool shed repositories, including repository
|
||||
# dependencies that may not be installed.
|
||||
all_created_or_updated_tool_shed_repositories = []
|
||||
# There will be a one-to-one mapping between items in 3 lists: created_or_updated_tool_shed_repositories, tool_panel_section_keys and filtered_repo_info_dicts.
|
||||
# The 3 lists will filter out repository dependencies that are not to be installed.
|
||||
# There will be a one-to-one mapping between items in 3 lists: created_or_updated_tool_shed_repositories, tool_panel_section_keys and
|
||||
# filtered_repo_info_dicts. The 3 lists will filter out repository dependencies that are not to be installed.
|
||||
created_or_updated_tool_shed_repositories = []
|
||||
tool_panel_section_keys = []
|
||||
# Repositories will be filtered (e.g., if already installed, if elected to not be installed, etc), so filter the associated repo_info_dicts accordingly.
|
||||
# Repositories will be filtered (e.g., if already installed, if elected to not be installed, etc), so filter the associated repo_info_dicts
|
||||
# accordingly.
|
||||
filtered_repo_info_dicts = []
|
||||
# Discover all repository dependencies and retrieve information for installing them. Even if the user elected to not install repository dependencies we have
|
||||
# to make sure all repository dependency objects exist so that the appropriate repository dependency relationships can be built.
|
||||
# Discover all repository dependencies and retrieve information for installing them. Even if the user elected to not install repository
|
||||
# dependencies we have to make sure all repository dependency objects exist so that the appropriate repository dependency relationships can
|
||||
# be built.
|
||||
all_required_repo_info_dict = common_install_util.get_required_repo_info_dicts( trans, tool_shed_url, repo_info_dicts )
|
||||
all_repo_info_dicts = all_required_repo_info_dict.get( 'all_repo_info_dicts', [] )
|
||||
if not all_repo_info_dicts:
|
||||
# No repository dependencies were discovered so process the received repositories.
|
||||
all_repo_info_dicts = [ rid for rid in repo_info_dicts ]
|
||||
for repo_info_dict in all_repo_info_dicts:
|
||||
for name, repo_info_tuple in repo_info_dict.items():
|
||||
description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, repository_dependencies, tool_dependencies = \
|
||||
suc.get_repo_info_tuple_contents( repo_info_tuple )
|
||||
# Make sure the repository was not already installed.
|
||||
installed_tool_shed_repository, installed_changeset_revision = suc.repository_was_previously_installed( trans, tool_shed_url, name, repo_info_tuple )
|
||||
if installed_tool_shed_repository:
|
||||
tool_section, new_tool_panel_section, tool_panel_section_key = tool_util.handle_tool_panel_selection( trans=trans,
|
||||
metadata=installed_tool_shed_repository.metadata,
|
||||
no_changes_checked=no_changes_checked,
|
||||
tool_panel_section=tool_panel_section,
|
||||
new_tool_panel_section=new_tool_panel_section )
|
||||
if reinstalling or install_repository_dependencies:
|
||||
# If the user elected to install repository dependencies, all items in the all_repo_info_dicts list will be processed. However, if
|
||||
# repository dependencies are not to be installed, only those items contained in the received repo_info_dicts list will be processed.
|
||||
if is_in_repo_info_dicts( repo_info_dict, repo_info_dicts ) or install_repository_dependencies:
|
||||
if installed_tool_shed_repository.status in [ trans.model.ToolShedRepository.installation_status.ERROR,
|
||||
trans.model.ToolShedRepository.installation_status.UNINSTALLED ]:
|
||||
# The current tool shed repository is not currently installed, so we can update it's record in the database.
|
||||
can_update = True
|
||||
name = installed_tool_shed_repository.name
|
||||
description = installed_tool_shed_repository.description
|
||||
installed_changeset_revision = installed_tool_shed_repository.installed_changeset_revision
|
||||
metadata_dict = installed_tool_shed_repository.metadata
|
||||
dist_to_shed = installed_tool_shed_repository.dist_to_shed
|
||||
elif installed_tool_shed_repository.status in [ trans.model.ToolShedRepository.installation_status.DEACTIVATED ]:
|
||||
# The current tool shed repository is deactivated, so updating it's database record is not necessary - just activate it.
|
||||
log.debug( "Reactivating deactivated tool_shed_repository '%s'." % str( installed_tool_shed_repository.name ) )
|
||||
common_install_util.activate_repository( trans, installed_tool_shed_repository )
|
||||
can_update = False
|
||||
else:
|
||||
# The tool shed repository currently being processed is already installed or is in the process of being installed, so it's record
|
||||
# in the database cannot be updated.
|
||||
if installed_tool_shed_repository.status in [ trans.model.ToolShedRepository.installation_status.INSTALLED ]:
|
||||
log.debug( "Skipping installation of tool_shed_repository '%s' because it is already installed." % \
|
||||
str( installed_tool_shed_repository.name ) )
|
||||
else:
|
||||
log.debug( "Skipping installation of tool_shed_repository '%s' because it's installation status is '%s'." % \
|
||||
( str( installed_tool_shed_repository.name ), str( installed_tool_shed_repository.status ) ) )
|
||||
can_update = False
|
||||
else:
|
||||
# This block will be reached only if reinstalling is True, install_repository_dependencies is False and is_in_repo_info_dicts is False.
|
||||
# The tool shed repository currently being processed must be a repository dependency that the user elected to not install, so it's
|
||||
# record in the database cannot be updated.
|
||||
debug_msg = "Skipping installation of tool_shed_repository '%s' because it is likely a " % str( installed_tool_shed_repository.name )
|
||||
debug_msg += "repository dependency that was elected to not be installed."
|
||||
log.debug( debug_msg )
|
||||
can_update = False
|
||||
else:
|
||||
# This block will be reached only if reinstalling is False and install_repository_dependencies is False. This implies that the tool shed
|
||||
# repository currently being processed has already been installed.
|
||||
if installed_tool_shed_repository.status in [ trans.model.ToolShedRepository.installation_status.INSTALLED ]:
|
||||
# Since the repository currently being processed is already in the INSTALLED state, skip it and process the next repository in the
|
||||
# list if there is one.
|
||||
# If the user elected to install repository dependencies, all items in the all_repo_info_dicts list will be processed. However, if
|
||||
# repository dependencies are not to be installed, only those items contained in the received repo_info_dicts list will be processed
|
||||
# but the the all_repo_info_dicts list will be used to create all defined repository dependency relationships.
|
||||
if is_in_repo_info_dicts( repo_info_dict, repo_info_dicts ) or install_repository_dependencies:
|
||||
for name, repo_info_tuple in repo_info_dict.items():
|
||||
can_update_db_record = False
|
||||
description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, repository_dependencies, tool_dependencies = \
|
||||
suc.get_repo_info_tuple_contents( repo_info_tuple )
|
||||
# See if the repository has an existing record in the database.
|
||||
repository_db_record, installed_changeset_revision = \
|
||||
suc.repository_was_previously_installed( trans, tool_shed_url, name, repo_info_tuple )
|
||||
if repository_db_record:
|
||||
if repository_db_record.status in [ trans.model.ToolShedRepository.installation_status.INSTALLED,
|
||||
trans.model.ToolShedRepository.installation_status.CLONING,
|
||||
trans.model.ToolShedRepository.installation_status.SETTING_TOOL_VERSIONS,
|
||||
trans.model.ToolShedRepository.installation_status.INSTALLING_REPOSITORY_DEPENDENCIES,
|
||||
trans.model.ToolShedRepository.installation_status.INSTALLING_TOOL_DEPENDENCIES,
|
||||
trans.model.ToolShedRepository.installation_status.LOADING_PROPRIETARY_DATATYPES ]:
|
||||
log.debug( "Skipping installation of tool_shed_repository '%s' because it's installation status is '%s'." % \
|
||||
( str( installed_tool_shed_repository.name ), str( installed_tool_shed_repository.status ) ) )
|
||||
can_update = False
|
||||
( str( repository_db_record.name ), str( repository_db_record.status ) ) )
|
||||
else:
|
||||
# The repository currently being processed is in some state other than INSTALLED, so reset it for installation.
|
||||
debug_msg = "Resetting tool_shed_repository '%s' for installation.\n" % str( installed_tool_shed_repository.name )
|
||||
debug_msg += "The current state of the tool_shed_repository is:\n"
|
||||
debug_msg += "deleted: %s\n" % str( installed_tool_shed_repository.deleted )
|
||||
debug_msg += "tool_shed_status: %s\n" % str( installed_tool_shed_repository.tool_shed_status )
|
||||
debug_msg += "uninstalled: %s\n" % str( installed_tool_shed_repository.uninstalled )
|
||||
debug_msg += "status: %s\n" % str( installed_tool_shed_repository.status )
|
||||
debug_msg += "error_message: %s\n" % str( installed_tool_shed_repository.error_message )
|
||||
log.debug( debug_msg )
|
||||
suc.reset_previously_installed_repository( trans, installed_tool_shed_repository )
|
||||
can_update = True
|
||||
reinstalling = True
|
||||
# Set changeset_revision here so suc.create_or_update_tool_shed_repository will find the previously installed
|
||||
# and uninstalled repository instead of creating a new record.
|
||||
changeset_revision = installed_tool_shed_repository.installed_changeset_revision
|
||||
else:
|
||||
# A tool shed repository is being installed into a Galaxy instance for the first time, or we're attempting to install it or reinstall it resulted
|
||||
# in an error. In the latter case, the repository record in the database has no metadata and it's status has been set to 'New'. In either case,
|
||||
# the repository's database record may be updated.
|
||||
can_update = True
|
||||
installed_changeset_revision = changeset_revision
|
||||
metadata_dict = {}
|
||||
dist_to_shed = False
|
||||
if can_update:
|
||||
# The database record for the tool shed repository currently being processed can be updated.
|
||||
if reinstalling or install_repository_dependencies:
|
||||
# Get the repository metadata to see where it was previously located in the tool panel.
|
||||
if installed_tool_shed_repository:
|
||||
# The tool shed repository status is one of 'New', 'Uninstalled', or 'Error'.
|
||||
tool_section, new_tool_panel_section, tool_panel_section_key = tool_util.handle_tool_panel_selection( trans=trans,
|
||||
metadata=installed_tool_shed_repository.metadata,
|
||||
no_changes_checked=no_changes_checked,
|
||||
tool_panel_section=tool_panel_section,
|
||||
new_tool_panel_section=new_tool_panel_section )
|
||||
if repository_db_record.status in [ trans.model.ToolShedRepository.installation_status.ERROR,
|
||||
trans.model.ToolShedRepository.installation_status.NEW,
|
||||
trans.model.ToolShedRepository.installation_status.UNINSTALLED ]:
|
||||
# The current tool shed repository is not currently installed, so we can update it's record in the database.
|
||||
name = repository_db_record.name
|
||||
installed_changeset_revision = repository_db_record.installed_changeset_revision
|
||||
metadata_dict = repository_db_record.metadata
|
||||
dist_to_shed = repository_db_record.dist_to_shed
|
||||
can_update_db_record = True
|
||||
elif repository_db_record.status in [ trans.model.ToolShedRepository.installation_status.DEACTIVATED ]:
|
||||
# The current tool shed repository is deactivated, so updating it's database record is not necessary - just activate it.
|
||||
log.debug( "Reactivating deactivated tool_shed_repository '%s'." % str( repository_db_record.name ) )
|
||||
common_install_util.activate_repository( trans, repository_db_record )
|
||||
# No additional updates to the database record are necessary.
|
||||
can_update_db_record = False
|
||||
elif repository_db_record.status not in [ trans.model.ToolShedRepository.installation_status.NEW ]:
|
||||
# Set changeset_revision here so suc.create_or_update_tool_shed_repository will find the previously installed
|
||||
# and uninstalled repository instead of creating a new record.
|
||||
changeset_revision = repository_db_record.installed_changeset_revision
|
||||
suc.reset_previously_installed_repository( trans, repository_db_record )
|
||||
can_update_db_record = True
|
||||
else:
|
||||
# No record exists in the database for the repository currently being processed.
|
||||
installed_changeset_revision = changeset_revision
|
||||
metadata_dict = {}
|
||||
dist_to_shed = False
|
||||
can_update_db_record = True
|
||||
if can_update_db_record:
|
||||
# The database record for the tool shed repository currently being processed can be updated. Get the repository metadata
|
||||
# to see where it was previously located in the tool panel.
|
||||
if repository_db_record and repository_db_record.metadata:
|
||||
tool_section, new_tool_panel_section, tool_panel_section_key = \
|
||||
tool_util.handle_tool_panel_selection( trans=trans,
|
||||
metadata=repository_db_record.metadata,
|
||||
no_changes_checked=no_changes_checked,
|
||||
tool_panel_section=tool_panel_section,
|
||||
new_tool_panel_section=new_tool_panel_section )
|
||||
else:
|
||||
# We're installing a new tool shed repository that does not yet have a database record. This repository is a repository dependency
|
||||
# of a different repository being installed.
|
||||
# We're installing a new tool shed repository that does not yet have a database record.
|
||||
tool_panel_section_key, tool_section = tool_util.handle_tool_panel_section( trans,
|
||||
tool_panel_section=tool_panel_section,
|
||||
new_tool_panel_section=new_tool_panel_section )
|
||||
|
||||
else:
|
||||
# We're installing a new tool shed repository that does not yet have a database record.
|
||||
tool_panel_section_key, tool_section = tool_util.handle_tool_panel_section( trans,
|
||||
tool_panel_section=tool_panel_section,
|
||||
new_tool_panel_section=new_tool_panel_section )
|
||||
tool_shed_repository = suc.create_or_update_tool_shed_repository( app=trans.app,
|
||||
name=name,
|
||||
description=description,
|
||||
installed_changeset_revision=changeset_revision,
|
||||
ctx_rev=ctx_rev,
|
||||
repository_clone_url=repository_clone_url,
|
||||
metadata_dict={},
|
||||
status=trans.model.ToolShedRepository.installation_status.NEW,
|
||||
current_changeset_revision=changeset_revision,
|
||||
owner=repository_owner,
|
||||
dist_to_shed=False )
|
||||
if tool_shed_repository not in all_created_or_updated_tool_shed_repositories:
|
||||
# Add the processed tool shed repository to the list of all processed repositories maintained within this method.
|
||||
all_created_or_updated_tool_shed_repositories.append( tool_shed_repository )
|
||||
# Only append the tool shed repository to the list of created_or_updated_tool_shed_repositories if it is supposed to be installed.
|
||||
if install_repository_dependencies or is_in_repo_info_dicts( repo_info_dict, repo_info_dicts ):
|
||||
if tool_shed_repository not in created_or_updated_tool_shed_repositories:
|
||||
# Keep the one-to-one mapping between items in 3 lists.
|
||||
created_or_updated_tool_shed_repositories.append( tool_shed_repository )
|
||||
tool_panel_section_keys.append( tool_panel_section_key )
|
||||
filtered_repo_info_dicts.append( repo_info_dict )
|
||||
tool_shed_repository = suc.create_or_update_tool_shed_repository( app=trans.app,
|
||||
name=name,
|
||||
description=description,
|
||||
installed_changeset_revision=changeset_revision,
|
||||
ctx_rev=ctx_rev,
|
||||
repository_clone_url=repository_clone_url,
|
||||
metadata_dict={},
|
||||
status=trans.model.ToolShedRepository.installation_status.NEW,
|
||||
current_changeset_revision=changeset_revision,
|
||||
owner=repository_owner,
|
||||
dist_to_shed=False )
|
||||
if tool_shed_repository not in all_created_or_updated_tool_shed_repositories:
|
||||
all_created_or_updated_tool_shed_repositories.append( tool_shed_repository )
|
||||
# Only append the tool shed repository to the list of created_or_updated_tool_shed_repositories if it is supposed to be installed.
|
||||
if install_repository_dependencies or is_in_repo_info_dicts( repo_info_dict, repo_info_dicts ):
|
||||
if tool_shed_repository not in created_or_updated_tool_shed_repositories:
|
||||
# Keep the one-to-one mapping between items in 3 lists.
|
||||
created_or_updated_tool_shed_repositories.append( tool_shed_repository )
|
||||
tool_panel_section_keys.append( tool_panel_section_key )
|
||||
filtered_repo_info_dicts.append( repo_info_dict )
|
||||
# Build repository dependency relationships even if the user chose to not install repository dependencies.
|
||||
build_repository_dependency_relationships( trans, all_repo_info_dicts, all_created_or_updated_tool_shed_repositories )
|
||||
return created_or_updated_tool_shed_repositories, tool_panel_section_keys, all_repo_info_dicts, filtered_repo_info_dicts
|
||||
@@ -436,22 +394,22 @@ def get_updated_changeset_revisions_for_repository_dependencies( trans, key_rd_d
|
||||
# We have the updated changset revision.
|
||||
updated_key_rd_dicts.append( new_key_rd_dict )
|
||||
else:
|
||||
try:
|
||||
toolshed, repository_name, repository_owner, repository_changeset_revision, prior_installation_required, rd_only_if_compiling_contained_td = \
|
||||
container_util.get_components_from_key( key )
|
||||
except ValueError:
|
||||
# For backward compatibility to the 12/20/12 Galaxy release.
|
||||
toolshed, repository_name, repository_owner, repository_changeset_revision = container_util.get_components_from_key( key )
|
||||
repository_components_tuple = container_util.get_components_from_key( key )
|
||||
components_list = suc.extract_components_from_tuple( repository_components_tuple )
|
||||
toolshed, repository_name, repository_owner, repository_changeset_revision = components_list[ 0:4 ]
|
||||
# For backward compatibility to the 12/20/12 Galaxy release.
|
||||
if len( components_list ) == 4:
|
||||
prior_installation_required = 'False'
|
||||
rd_only_if_compiling_contained_td = 'False'
|
||||
elif len( components_list ) == 5:
|
||||
rd_only_if_compiling_contained_td = 'False'
|
||||
message = "The revision %s defined for repository %s owned by %s is invalid, so repository dependencies defined for repository %s will be ignored." % \
|
||||
( str( rd_changeset_revision ), str( rd_name ), str( rd_owner ), str( repository_name ) )
|
||||
log.debug( message )
|
||||
else:
|
||||
try:
|
||||
toolshed, repository_name, repository_owner, repository_changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
|
||||
container_util.get_components_from_key( key )
|
||||
except ValueError:
|
||||
# For backward compatibility to the 12/20/12 Galaxy release.
|
||||
toolshed, repository_name, repository_owner, repository_changeset_revision = container_util.get_components_from_key( key )
|
||||
repository_components_tuple = container_util.get_components_from_key( key )
|
||||
components_list = suc.extract_components_from_tuple( repository_components_tuple )
|
||||
toolshed, repository_name, repository_owner, repository_changeset_revision = components_list[ 0:4 ]
|
||||
message = "The revision %s defined for repository %s owned by %s is invalid, so repository dependencies defined for repository %s will be ignored." % \
|
||||
( str( rd_changeset_revision ), str( rd_name ), str( rd_owner ), str( repository_name ) )
|
||||
log.debug( message )
|
||||
|
||||
@@ -5,7 +5,7 @@ import tool_shed.util.shed_util_common as suc
|
||||
from galaxy.web.form_builder import build_select_field
|
||||
|
||||
from galaxy import eggs
|
||||
eggs.require('mercurial')
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
from mercurial import commands
|
||||
from mercurial import hg
|
||||
|
||||
@@ -5,9 +5,8 @@ from galaxy.model.orm import and_
|
||||
from galaxy.util.odict import odict
|
||||
import tool_shed.util.shed_util_common as suc
|
||||
|
||||
import pkg_resources
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
pkg_resources.require( 'mercurial' )
|
||||
from mercurial import commands
|
||||
from mercurial import hg
|
||||
from mercurial import ui
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import string
|
||||
import tempfile
|
||||
@@ -20,10 +21,11 @@ from tool_shed.util import common_util
|
||||
from tool_shed.util import encoding_util
|
||||
from tool_shed.util import xml_util
|
||||
from xml.etree import ElementTree as XmlET
|
||||
from urllib2 import HTTPError
|
||||
|
||||
from galaxy import eggs
|
||||
import pkg_resources
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
pkg_resources.require( 'mercurial' )
|
||||
from mercurial import cmdutil
|
||||
from mercurial import commands
|
||||
from mercurial import hg
|
||||
@@ -143,12 +145,18 @@ def build_repository_ids_select_field( trans, name='repository_ids', multiple=Tr
|
||||
repositories_select_field.add_option( option_label, option_value )
|
||||
return repositories_select_field
|
||||
|
||||
def build_tool_dependencies_select_field( trans, tool_shed_repository, name, multiple=True, display='checkboxes', uninstalled=False ):
|
||||
def build_tool_dependencies_select_field( trans, tool_shed_repository, name, multiple=True, display='checkboxes', uninstalled_only=False ):
|
||||
"""Method called from Galaxy to generate the current list of tool dependency ids for an installed tool shed repository."""
|
||||
tool_dependencies_select_field = SelectField( name=name, multiple=multiple, display=display )
|
||||
for tool_dependency in tool_shed_repository.tool_dependencies:
|
||||
if uninstalled and tool_dependency.status != trans.model.ToolDependency.installation_status.UNINSTALLED:
|
||||
continue
|
||||
if uninstalled_only:
|
||||
if tool_dependency.status not in [ trans.model.ToolDependency.installation_status.NEVER_INSTALLED,
|
||||
trans.model.ToolDependency.installation_status.UNINSTALLED ]:
|
||||
continue
|
||||
else:
|
||||
if tool_dependency.status in [ trans.model.ToolDependency.installation_status.NEVER_INSTALLED,
|
||||
trans.model.ToolDependency.installation_status.UNINSTALLED ]:
|
||||
continue
|
||||
option_label = '%s version %s' % ( str( tool_dependency.name ), str( tool_dependency.version ) )
|
||||
option_value = trans.security.encode_id( tool_dependency.id )
|
||||
tool_dependencies_select_field.add_option( option_label, option_value )
|
||||
@@ -307,6 +315,21 @@ def create_or_update_tool_shed_repository( app, name, description, installed_cha
|
||||
sa_session.flush()
|
||||
return tool_shed_repository
|
||||
|
||||
def extract_components_from_tuple( repository_components_tuple ):
|
||||
'''Extract the repository components from the provided tuple in a backward-compatible manner.'''
|
||||
toolshed = repository_components_tuple[ 0 ]
|
||||
name = repository_components_tuple[ 1 ]
|
||||
owner = repository_components_tuple[ 2 ]
|
||||
changeset_revision = repository_components_tuple[ 3 ]
|
||||
components_list = [ toolshed, name, owner, changeset_revision ]
|
||||
if len( repository_components_tuple ) == 5:
|
||||
toolshed, name, owner, changeset_revision, prior_installation_required = repository_components_tuple
|
||||
components_list = [ toolshed, name, owner, changeset_revision, prior_installation_required ]
|
||||
elif len( repository_components_tuple ) == 6:
|
||||
toolshed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = repository_components_tuple
|
||||
components_list = [ toolshed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td ]
|
||||
return components_list
|
||||
|
||||
def generate_clone_url_for_installed_repository( app, repository ):
|
||||
"""Generate the URL for cloning a repository that has been installed into a Galaxy instance."""
|
||||
tool_shed_url = get_url_from_tool_shed( app, repository.tool_shed )
|
||||
@@ -868,6 +891,31 @@ def get_repository_by_name_and_owner( app, name, owner ):
|
||||
.first()
|
||||
return None
|
||||
|
||||
def get_repository_dependency_types( repository_dependencies ):
|
||||
"""
|
||||
Inspect the received list of repository_dependencies tuples and return boolean values for has_repository_dependencies and
|
||||
has_repository_dependencies_only_if_compiling_contained_td.
|
||||
"""
|
||||
# Set has_repository_dependencies, which will be True only if at least one repository_dependency is defined with the value of
|
||||
# only_if_compiling_contained_td as False.
|
||||
has_repository_dependencies = False
|
||||
for rd_tup in repository_dependencies:
|
||||
tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
|
||||
common_util.parse_repository_dependency_tuple( rd_tup )
|
||||
if not asbool( only_if_compiling_contained_td ):
|
||||
has_repository_dependencies = True
|
||||
break
|
||||
# Set has_repository_dependencies_only_if_compiling_contained_td, which will be True only if at least one repository_dependency is
|
||||
# defined with the value of only_if_compiling_contained_td as True.
|
||||
has_repository_dependencies_only_if_compiling_contained_td = False
|
||||
for rd_tup in repository_dependencies:
|
||||
tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
|
||||
common_util.parse_repository_dependency_tuple( rd_tup )
|
||||
if asbool( only_if_compiling_contained_td ):
|
||||
has_repository_dependencies_only_if_compiling_contained_td = True
|
||||
break
|
||||
return has_repository_dependencies, has_repository_dependencies_only_if_compiling_contained_td
|
||||
|
||||
def get_repository_for_dependency_relationship( app, tool_shed, name, owner, changeset_revision ):
|
||||
"""Return an installed tool_shed_repository database record that is defined by either the current changeset revision or the installed_changeset_revision."""
|
||||
# This method is used only in Galaxy, not the tool shed.
|
||||
@@ -1185,10 +1233,25 @@ def get_tool_shed_status_for_installed_repository( app, repository ):
|
||||
try:
|
||||
encoded_tool_shed_status_dict = common_util.tool_shed_get( app, tool_shed_url, url )
|
||||
tool_shed_status_dict = encoding_util.tool_shed_decode( encoded_tool_shed_status_dict )
|
||||
return tool_shed_status_dict
|
||||
except HTTPError, e:
|
||||
# This should handle backward compatility to the Galaxy 12/20/12 release. We used to only handle updates for an installed revision
|
||||
# using a boolean value.
|
||||
log.debug( "Error attempting to get tool shed status for installed repository %s: %s\nAttempting older 'check_for_updates' method.\n" % \
|
||||
( str( repository.name ), str( e ) ) )
|
||||
url = url_join( tool_shed_url,
|
||||
'repository/check_for_updates?name=%s&owner=%s&changeset_revision=%s&from_update_manager=True' % \
|
||||
( repository.name, repository.owner, repository.changeset_revision ) )
|
||||
try:
|
||||
# The value of text will be 'true' or 'false', depending upon whether there is an update available for the installed revision.
|
||||
text = common_util.tool_shed_get( app, tool_shed_url, url )
|
||||
return dict( revision_update=text )
|
||||
except Exception, e:
|
||||
# The required tool shed may be unavailable, so default the revision_update value to 'false'.
|
||||
return dict( revision_update='false' )
|
||||
except Exception, e:
|
||||
log.exception( "Error attemtping to get tool shed status for installed repository %s: %s" % ( str( repository.name ), str( e ) ) )
|
||||
log.exception( "Error attempting to get tool shed status for installed repository %s: %s" % ( str( repository.name ), str( e ) ) )
|
||||
return {}
|
||||
return tool_shed_status_dict
|
||||
|
||||
def get_updated_changeset_revisions( trans, name, owner, changeset_revision ):
|
||||
"""
|
||||
@@ -1389,14 +1452,23 @@ def remove_dir( dir ):
|
||||
|
||||
def repository_was_previously_installed( trans, tool_shed_url, repository_name, repo_info_tuple ):
|
||||
"""
|
||||
Handle the case where the repository was previously installed using an older changeset_revsion, but later the repository was updated
|
||||
in the tool shed and now we're trying to install the latest changeset revision of the same repository instead of updating the one
|
||||
that was previously installed. We'll look in the database instead of on disk since the repository may be uninstalled.
|
||||
Find out if a repository is already installed into Galaxy - there are several scenarios where this is necessary. For example, this method
|
||||
will handle the case where the repository was previously installed using an older changeset_revsion, but later the repository was updated
|
||||
in the tool shed and now we're trying to install the latest changeset revision of the same repository instead of updating the one that was
|
||||
previously installed. We'll look in the database instead of on disk since the repository may be currently uninstalled.
|
||||
"""
|
||||
description, repository_clone_url, changeset_revision, ctx_rev, repository_owner, repository_dependencies, tool_dependencies = \
|
||||
get_repo_info_tuple_contents( repo_info_tuple )
|
||||
tool_shed = get_tool_shed_from_clone_url( repository_clone_url )
|
||||
# Get all previous change set revisions from the tool shed for the repository back to, but excluding, the previous valid changeset
|
||||
# See if we can locate the repository using the value of changeset_revision.
|
||||
tool_shed_repository = get_tool_shed_repository_by_shed_name_owner_installed_changeset_revision( trans.app,
|
||||
tool_shed,
|
||||
repository_name,
|
||||
repository_owner,
|
||||
changeset_revision )
|
||||
if tool_shed_repository:
|
||||
return tool_shed_repository, changeset_revision
|
||||
# Get all previous changeset revisions from the tool shed for the repository back to, but excluding, the previous valid changeset
|
||||
# revision to see if it was previously installed using one of them.
|
||||
url = url_join( tool_shed_url,
|
||||
'repository/previous_changeset_revisions?galaxy_url=%s&name=%s&owner=%s&changeset_revision=%s' % \
|
||||
@@ -1410,15 +1482,23 @@ def repository_was_previously_installed( trans, tool_shed_url, repository_name,
|
||||
repository_name,
|
||||
repository_owner,
|
||||
previous_changeset_revision )
|
||||
if tool_shed_repository and tool_shed_repository.status not in [ trans.model.ToolShedRepository.installation_status.NEW ]:
|
||||
if tool_shed_repository:
|
||||
return tool_shed_repository, previous_changeset_revision
|
||||
return None, None
|
||||
|
||||
def reset_previously_installed_repository( trans, repository ):
|
||||
"""
|
||||
Reset the atrributes of a tool_shed_repository that was previsouly installed. The repository will be in some state other than with a
|
||||
status of INSTALLED, so all atributes will be set to the default (NEW( state. This will enable the repository to be freshly installed.
|
||||
Reset the atrributes of a tool_shed_repository that was previsouly installed. The repository will be in some state other than INSTALLED,
|
||||
so all atributes will be set to the default NEW state. This will enable the repository to be freshly installed.
|
||||
"""
|
||||
debug_msg = "Resetting tool_shed_repository '%s' for installation.\n" % str( repository.name )
|
||||
debug_msg += "The current state of the tool_shed_repository is:\n"
|
||||
debug_msg += "deleted: %s\n" % str( repository.deleted )
|
||||
debug_msg += "tool_shed_status: %s\n" % str( repository.tool_shed_status )
|
||||
debug_msg += "uninstalled: %s\n" % str( repository.uninstalled )
|
||||
debug_msg += "status: %s\n" % str( repository.status )
|
||||
debug_msg += "error_message: %s\n" % str( repository.error_message )
|
||||
log.debug( debug_msg )
|
||||
repository.deleted = False
|
||||
repository.tool_shed_status = None
|
||||
repository.uninstalled = False
|
||||
@@ -1455,6 +1535,31 @@ def reversed_upper_bounded_changelog( repo, included_upper_bounds_changeset_revi
|
||||
"""Return a reversed list of changesets in the repository changelog up to and including the included_upper_bounds_changeset_revision."""
|
||||
return reversed_lower_upper_bounded_changelog( repo, INITIAL_CHANGELOG_HASH, included_upper_bounds_changeset_revision )
|
||||
|
||||
def set_image_paths( app, encoded_repository_id, text ):
|
||||
"""
|
||||
Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy as well as image
|
||||
display in repository README files. This method will determine the location of the image file and return the path to it that will enable
|
||||
the caller to open the file.
|
||||
"""
|
||||
if text:
|
||||
if app.name == 'galaxy':
|
||||
route_to_images = '/admin_toolshed/static/images/%s' % encoded_repository_id
|
||||
else:
|
||||
# We're in the tool shed.
|
||||
route_to_images = '/repository/static/images/%s' % encoded_repository_id
|
||||
# We used to require $PATH_TO_IMAGES, but we now eliminate it if it's used.
|
||||
text = text.replace( '$PATH_TO_IMAGES', '' )
|
||||
# Eliminate the invalid setting of ./static/images since the routes will properly display images contained in that directory.
|
||||
text = text.replace( './static/images', '' )
|
||||
# Eliminate the default setting of /static/images since the routes will properly display images contained in that directory.
|
||||
text = text.replace( '/static/images', '' )
|
||||
# Use regex to instantiate routes into the defined image paths, but replace paths that start with neither http:// nor https://,
|
||||
# which will allow for settings like .. images:: http_files/images/help.png
|
||||
for match in re.findall( '.. image:: (?!http)/?(.+)', text ):
|
||||
text = text.replace( match, match.replace( '/', '%2F' ) )
|
||||
text = re.sub( r'\.\. image:: (?!https?://)/?(.+)', r'.. image:: %s/\1' % route_to_images, text )
|
||||
return text
|
||||
|
||||
def set_only_if_compiling_contained_td( repository, required_repository ):
|
||||
"""Return True if the received required_repository is only needed to compile a tool dependency defined for the received repository."""
|
||||
# This method is called only from Galaxy when rendering repository dependencies for an installed tool shed repository.
|
||||
|
||||
@@ -258,6 +258,14 @@ def get_tool_dependency_by_name_type_repository( app, repository, name, type ):
|
||||
app.model.ToolDependency.table.c.type == type ) ) \
|
||||
.first()
|
||||
|
||||
def get_tool_dependency_by_name_version_type( app, name, version, type ):
|
||||
sa_session = app.model.context.current
|
||||
return sa_session.query( app.model.ToolDependency ) \
|
||||
.filter( and_( app.model.ToolDependency.table.c.name == name,
|
||||
app.model.ToolDependency.table.c.version == version,
|
||||
app.model.ToolDependency.table.c.type == type ) ) \
|
||||
.first()
|
||||
|
||||
def get_tool_dependency_by_name_version_type_repository( app, repository, name, version, type ):
|
||||
sa_session = app.model.context.current
|
||||
return sa_session.query( app.model.ToolDependency ) \
|
||||
|
||||
@@ -18,9 +18,8 @@ from galaxy.tools.actions.upload import UploadToolAction
|
||||
import tool_shed.util.shed_util_common as suc
|
||||
from xml.etree import ElementTree as XmlET
|
||||
|
||||
import pkg_resources
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
pkg_resources.require( 'mercurial' )
|
||||
from mercurial import commands
|
||||
from mercurial import hg
|
||||
from mercurial import ui
|
||||
@@ -746,7 +745,8 @@ def install_tool_data_tables( app, tool_shed_repository, tool_index_sample_files
|
||||
else:
|
||||
elems.append( elem )
|
||||
else:
|
||||
log.debug( "The '%s' data table file was not found, but was expected to be copied from '%s' during repository installation.", tool_data_table_conf_filename, TOOL_DATA_TABLE_FILE_SAMPLE_NAME )
|
||||
log.debug( "The '%s' data table file was not found, but was expected to be copied from '%s' during repository installation.",
|
||||
tool_data_table_conf_filename, TOOL_DATA_TABLE_FILE_SAMPLE_NAME )
|
||||
for elem in elems:
|
||||
if elem.tag == 'table':
|
||||
for file_elem in elem.findall( 'file' ):
|
||||
@@ -962,7 +962,7 @@ def remove_from_shed_tool_config( trans, shed_tool_conf_dict, guids_to_remove ):
|
||||
suc.config_elems_to_xml_file( trans.app, config_elems, shed_tool_conf, tool_path )
|
||||
|
||||
def remove_from_tool_panel( trans, repository, shed_tool_conf, uninstall ):
|
||||
"""A tool shed repository is being deactivated or uninstalled so handle tool panel alterations accordingly."""
|
||||
"""A tool shed repository is being deactivated or uninstalled, so handle tool panel alterations accordingly."""
|
||||
# Determine where the tools are currently defined in the tool panel and store this information so the tools can be displayed
|
||||
# in the same way when the repository is activated or reinstalled.
|
||||
tool_panel_dict = suc.generate_tool_panel_dict_from_shed_tool_conf_entries( trans.app, repository )
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import logging
|
||||
import os
|
||||
import galaxy.tools
|
||||
import galaxy.tools.parameters
|
||||
import galaxy.webapps.galaxy.controllers.workflow
|
||||
@@ -151,7 +152,7 @@ class RepoWorkflowModuleFactory( WorkflowModuleFactory ):
|
||||
def new( self, trans, type, tools_metadata=None, tool_id=None ):
|
||||
"""Return module for type and (optional) tool_id initialized with new / default state."""
|
||||
assert type in self.module_types
|
||||
return self.module_types[type].new( trans, tool_id )
|
||||
return self.module_types[ type ].new( trans, tool_id )
|
||||
|
||||
def from_dict( self, trans, repository_id, changeset_revision, step_dict, **kwd ):
|
||||
"""Return module initialized from the data in dictionary `step_dict`."""
|
||||
@@ -219,13 +220,11 @@ def generate_workflow_image( trans, workflow_name, repository_metadata_id=None,
|
||||
tool_errors = module.type == 'tool' and not module.tool
|
||||
module_data_inputs = get_workflow_data_inputs( step, module )
|
||||
module_data_outputs = get_workflow_data_outputs( step, module, workflow.steps )
|
||||
step_dict = {
|
||||
'id' : step.order_index,
|
||||
'data_inputs' : module_data_inputs,
|
||||
'data_outputs' : module_data_outputs,
|
||||
'position' : step.position,
|
||||
'tool_errors' : tool_errors
|
||||
}
|
||||
step_dict = { 'id' : step.order_index,
|
||||
'data_inputs' : module_data_inputs,
|
||||
'data_outputs' : module_data_outputs,
|
||||
'position' : step.position,
|
||||
'tool_errors' : tool_errors }
|
||||
input_conn_dict = {}
|
||||
for conn in step.input_connections:
|
||||
input_conn_dict[ conn.input_name ] = dict( id=conn.output_step.order_index, output_name=conn.output_name )
|
||||
@@ -390,19 +389,21 @@ def get_workflow_from_dict( trans, workflow_dict, tools_metadata, repository_id,
|
||||
workflow.has_errors = True
|
||||
# Stick this in the step temporarily.
|
||||
step.temp_input_connections = step_dict[ 'input_connections' ]
|
||||
annotation = step_dict.get( 'annotation', '')
|
||||
if annotation:
|
||||
annotation = sanitize_html( annotation, 'utf-8', 'text/html' )
|
||||
new_step_annotation = trans.model.WorkflowStepAnnotationAssociation()
|
||||
new_step_annotation.annotation = annotation
|
||||
new_step_annotation.user = trans.user
|
||||
step.annotations.append(new_step_annotation)
|
||||
if trans.webapp.name == 'galaxy':
|
||||
annotation = step_dict.get( 'annotation', '')
|
||||
if annotation:
|
||||
annotation = sanitize_html( annotation, 'utf-8', 'text/html' )
|
||||
new_step_annotation = trans.model.WorkflowStepAnnotationAssociation()
|
||||
new_step_annotation.annotation = annotation
|
||||
new_step_annotation.user = trans.user
|
||||
step.annotations.append( new_step_annotation )
|
||||
# Unpack and add post-job actions.
|
||||
post_job_actions = step_dict.get( 'post_job_actions', {} )
|
||||
for name, pja_dict in post_job_actions.items():
|
||||
trans.model.PostJobAction( pja_dict[ 'action_type' ],
|
||||
step, pja_dict[ 'output_name' ],
|
||||
pja_dict[ 'action_arguments' ] )
|
||||
step,
|
||||
pja_dict[ 'output_name' ],
|
||||
pja_dict[ 'action_arguments' ] )
|
||||
steps.append( step )
|
||||
steps_by_external_id[ step_dict[ 'id' ] ] = step
|
||||
# Second pass to deal with connections between steps.
|
||||
@@ -433,6 +434,64 @@ def get_workflow_module_name( module, missing_tool_tups ):
|
||||
break
|
||||
return module_name
|
||||
|
||||
def import_workflow( trans, repository, workflow_name ):
|
||||
"""Import a workflow contained in an installed tool shed repository into Galaxy (this method is called only from Galaxy)."""
|
||||
status = 'done'
|
||||
message = ''
|
||||
changeset_revision = repository.changeset_revision
|
||||
metadata = repository.metadata
|
||||
workflows = metadata.get( 'workflows', [] )
|
||||
tools_metadata = metadata.get( 'tools', [] )
|
||||
workflow_dict = None
|
||||
for workflow_data_tuple in workflows:
|
||||
# The value of workflow_data_tuple is ( relative_path_to_workflow_file, exported_workflow_dict ).
|
||||
relative_path_to_workflow_file, exported_workflow_dict = workflow_data_tuple
|
||||
if exported_workflow_dict[ 'name' ] == workflow_name:
|
||||
# If the exported workflow is available on disk, import it.
|
||||
if os.path.exists( relative_path_to_workflow_file ):
|
||||
workflow_file = open( relative_path_to_workflow_file, 'rb' )
|
||||
workflow_data = workflow_file.read()
|
||||
workflow_file.close()
|
||||
workflow_dict = json.from_json_string( workflow_data )
|
||||
else:
|
||||
# Use the current exported_workflow_dict.
|
||||
workflow_dict = exported_workflow_dict
|
||||
break
|
||||
if workflow_dict:
|
||||
# Create workflow if possible.
|
||||
workflow, missing_tool_tups = get_workflow_from_dict( trans=trans,
|
||||
workflow_dict=workflow_dict,
|
||||
tools_metadata=tools_metadata,
|
||||
repository_id=repository.id,
|
||||
changeset_revision=changeset_revision )
|
||||
# Save the workflow in the Galaxy database. Pass workflow_dict along to create annotation at this point.
|
||||
stored_workflow = save_workflow( trans, workflow, workflow_dict )
|
||||
# Use the latest version of the saved workflow.
|
||||
workflow = stored_workflow.latest_workflow
|
||||
if workflow_name:
|
||||
workflow.name = workflow_name
|
||||
# Provide user feedback and show workflow list.
|
||||
if workflow.has_errors:
|
||||
message += "Imported, but some steps in this workflow have validation errors. "
|
||||
status = "error"
|
||||
if workflow.has_cycles:
|
||||
message += "Imported, but this workflow contains cycles. "
|
||||
status = "error"
|
||||
else:
|
||||
message += "Workflow <b>%s</b> imported successfully. " % workflow.name
|
||||
if missing_tool_tups:
|
||||
name_and_id_str = ''
|
||||
for missing_tool_tup in missing_tool_tups:
|
||||
tool_id, tool_name, other = missing_tool_tup
|
||||
name_and_id_str += 'name: %s, id: %s' % ( str( tool_id ), str( tool_name ) )
|
||||
message += "The following tools required by this workflow are missing from this Galaxy instance: %s. " % name_and_id_str
|
||||
else:
|
||||
workflow = None
|
||||
message += 'The workflow named %s is not included in the metadata for revision %s of repository %s' % \
|
||||
( str( workflow_name ), str( changeset_revision ), str( repository.name ) )
|
||||
status = 'error'
|
||||
return workflow, status, message
|
||||
|
||||
def save_workflow( trans, workflow, workflow_dict = None):
|
||||
"""Use the received in-memory Workflow object for saving to the Galaxy database."""
|
||||
stored = trans.model.StoredWorkflow()
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<object_store type="hierarchical">
|
||||
<backends>
|
||||
<object_store type="distributed" id="primary" order="0">
|
||||
<backends>
|
||||
<backend id="files1" type="disk" weight="1">
|
||||
<files_dir path="database/files1"/>
|
||||
<extra_dir type="temp" path="database/tmp1"/>
|
||||
<extra_dir type="job_work" path="database/job_working_directory1"/>
|
||||
</backend>
|
||||
<backend id="files2" type="disk" weight="1">
|
||||
<files_dir path="database/files2"/>
|
||||
<extra_dir type="temp" path="database/tmp2"/>
|
||||
<extra_dir type="job_work" path="database/job_working_directory2"/>
|
||||
</backend>
|
||||
</backends>
|
||||
</object_store>
|
||||
<object_store type="disk" id="secondary" order="1">
|
||||
<files_dir path="database/files3"/>
|
||||
<extra_dir type="temp" path="database/tmp3"/>
|
||||
<extra_dir type="job_work" path="database/job_working_directory3"/>
|
||||
</object_store>
|
||||
</backends>
|
||||
</object_store>
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Import one or more exported workflows contained within a specified tool shed repository installed into Galaxy.
|
||||
|
||||
Here is a working example of how to use this script to repair a repository installed into Galaxy.
|
||||
python ./import_workflows_from_installed_tool_shed_repository.py -a 22be3b -l http://localhost:8763/ -n workflow_with_tools -o test -r ef45bb64237e -u http://localhost:9009/
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
sys.path.insert( 0, os.path.dirname( __file__ ) )
|
||||
from common import display
|
||||
from common import submit
|
||||
|
||||
def clean_url( url ):
|
||||
if url.find( '//' ) > 0:
|
||||
# We have an url that includes a protocol, something like: http://localhost:9009
|
||||
items = url.split( '//' )
|
||||
return items[ 1 ].rstrip( '/' )
|
||||
return url.rstrip( '/' )
|
||||
|
||||
def main( options ):
|
||||
api_key = options.api
|
||||
base_galaxy_url = options.local_url.rstrip( '/' )
|
||||
base_tool_shed_url = options.tool_shed_url.rstrip( '/' )
|
||||
cleaned_tool_shed_url = clean_url( base_tool_shed_url )
|
||||
installed_tool_shed_repositories_url = '%s/api/tool_shed_repositories' % base_galaxy_url
|
||||
tool_shed_repository_id = None
|
||||
installed_tool_shed_repositories = display( api_key, installed_tool_shed_repositories_url, return_formatted=False )
|
||||
for installed_tool_shed_repository in installed_tool_shed_repositories:
|
||||
tool_shed = str( installed_tool_shed_repository[ 'tool_shed' ] )
|
||||
name = str( installed_tool_shed_repository[ 'name' ] )
|
||||
owner = str( installed_tool_shed_repository[ 'owner' ] )
|
||||
changeset_revision = str( installed_tool_shed_repository[ 'changeset_revision' ] )
|
||||
if tool_shed == cleaned_tool_shed_url and name == options.name and owner == options.owner and changeset_revision == options.changeset_revision:
|
||||
tool_shed_repository_id = installed_tool_shed_repository[ 'id' ]
|
||||
break
|
||||
if tool_shed_repository_id:
|
||||
# Get the list of exported workflows contained in the installed repository.
|
||||
url = '%s%s' % ( base_galaxy_url, '/api/tool_shed_repositories/%s/exported_workflows' % str( tool_shed_repository_id ) )
|
||||
exported_workflows = display( api_key, url, return_formatted=False )
|
||||
if exported_workflows:
|
||||
# Import all of the workflows in the list of exported workflows.
|
||||
data = {}
|
||||
# NOTE: to import a single workflow, add an index to data (e.g.,
|
||||
# data[ 'index' ] = 0
|
||||
# and change the url to be ~/import_workflow (simgular). For example,
|
||||
# url = '%s%s' % ( base_galaxy_url, '/api/tool_shed_repositories/%s/import_workflow' % str( tool_shed_repository_id ) )
|
||||
url = '%s%s' % ( base_galaxy_url, '/api/tool_shed_repositories/%s/import_workflows' % str( tool_shed_repository_id ) )
|
||||
submit( options.api, url, data )
|
||||
else:
|
||||
print "Invalid tool_shed / name / owner / changeset_revision."
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser( description='Import workflows contained in an installed tool shed repository via the Galaxy API.' )
|
||||
parser.add_argument( "-a", "--api", dest="api", required=True, help="API Key" )
|
||||
parser.add_argument( "-u", "--url", dest="tool_shed_url", required=True, help="Tool Shed URL" )
|
||||
parser.add_argument( "-l", "--local", dest="local_url", required=True, help="URL of the galaxy instance." )
|
||||
parser.add_argument( "-n", "--name", required=True, help="Repository name." )
|
||||
parser.add_argument( "-o", "--owner", required=True, help="Repository owner." )
|
||||
parser.add_argument( "-r", "--revision", dest="changeset_revision", required=True, help="Repository owner." )
|
||||
options = parser.parse_args()
|
||||
main( options )
|
||||
@@ -230,11 +230,11 @@ def main():
|
||||
default_cluster_job_runner = default_cluster_job_runner )
|
||||
psu_production = True
|
||||
else:
|
||||
tempdir = tempfile.mkdtemp( dir=galaxy_test_tmp_dir )
|
||||
# Configure the database path.
|
||||
if 'GALAXY_TEST_DBPATH' in os.environ:
|
||||
galaxy_db_path = os.environ[ 'GALAXY_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 test tools.
|
||||
file_path = os.path.join( galaxy_db_path, 'files' )
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd `dirname $0`/../..
|
||||
python ./scripts/migrate_tools/migrate_tools.py 0008_tools.xml $@
|
||||
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0"?>
|
||||
<toolshed name="toolshed.g2.bx.psu.edu">
|
||||
<repository owner="devteam" changeset_revision="dc20f447c0e2" name="bam_to_sam" description="Galaxy wrappers for the tool BAM-to-SAM: converts BAM format to SAM format">
|
||||
<tool id="bam_to_sam" version="1.0.3" file="bam_to_sam.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="586c1f0e1515" name="categorize_elements_satisfying_criteria" description="Galaxy wrappers for the tool Categorize Elements: satisfying criteria">
|
||||
<tool id="categorize_elements_satisfying_criteria" version="1.0.0" file="categorize_elements_satisfying_criteria.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="acc3dc5d26e3" name="compute_motif_frequencies_for_all_motifs" description="Galaxy wrappers for the tool Compute Motif Frequencies For All Motifs: motif by motif">
|
||||
<tool id="compute_motif_frequencies_for_all_motifs" version="1.0.0" file="compute_motif_frequencies_for_all_motifs.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="46325357b948" name="compute_motifs_frequency" description="Galaxy wrappers for the tool Compute Motif Frequencies: in indel flanking regions">
|
||||
<tool id="compute_motifs_frequency" version="1.0.0" file="compute_motifs_frequency.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="4a32700dcaa2" name="ctd_batch" description="Galaxy wrappers for the tool CTD: analysis of chemicals, diseases, or genes">
|
||||
<tool id="ctdBatch_1" version="1.0.0" file="ctd.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="9a3f7a890da6" name="cuffcompare" description="Galaxy wrappers for the Cuffcompare tool.">
|
||||
<tool id="cuffcompare" version="0.0.5" file="cuffcompare_wrapper.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="0dabb2ed6eb1" name="cuffdiff" description="Galaxy wrappers for the Cuffdiff tool.">
|
||||
<tool id="cuffdiff" version="0.0.6" file="cuffdiff_wrapper.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="b01956f26c36" name="cufflinks" description="Galaxy wrappers for the Cufflinks tool.">
|
||||
<tool id="cufflinks" version="0.0.6" file="cufflinks_wrapper.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="fdc55fd74f78" name="cuffmerge" description="Galaxy wrappers for the Cuffmerge tool.">
|
||||
<tool id="cuffmerge" version="0.0.5" file="cuffmerge_wrapper.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="f16000dc644b" name="delete_overlapping_indels" description="Galaxy wrappers for the tool Delete Overlapping Indels: from a chromosome indels file">
|
||||
<tool id="delete_overlapping_indels" version="1.0.0" file="delete_overlapping_indels.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="de839553d2a2" name="divide_pg_snp" description="Galaxy wrappers for the tool Separate pgSnp alleles: into columns">
|
||||
<tool id="dividePgSnp" version="1.0.0" file="dividePgSnpAlleles.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="063ecbe59faf" name="draw_stacked_barplots" description="Galaxy wrappers for the tool Draw Stacked Bar Plots: for different categories and different criteria">
|
||||
<tool id="draw_stacked_barplots" version="1.0.0" file="draw_stacked_barplots.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="f2ab5b44870d" name="fasta_clipping_histogram" description="Galaxy wrappers for the tool Length Distribution chart">
|
||||
<tool id="cshl_fasta_clipping_histogram" version="1.0.0" file="fasta_clipping_histogram.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="8f0ae92440b8" name="fasta_formatter" description="Galaxy wrappers for the tool FASTA Width: formatter">
|
||||
<tool id="cshl_fasta_formatter" version="1.0.0" file="fasta_formatter.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="4b41e3076a50" name="fasta_nucleotide_changer" description="Galaxy wrappers for the tool RNA/DNA: converter">
|
||||
<tool id="cshl_fasta_nucleotides_changer" version="1.0.0" file="fasta_nucleotide_changer.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="e359ce20fbd9" name="fastq_quality_boxplot" description="Galaxy wrappers for the tool Draw quality score boxplot">
|
||||
<tool id="cshl_fastq_quality_boxplot" version="1.0.0" file="fastq_quality_boxplot.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="b87c74b61d81" name="fastq_quality_converter" description="Galaxy wrappers for the tool Quality format converter: (ASCII-Numeric)">
|
||||
<tool id="cshl_fastq_quality_converter" version="1.0.0" file="fastq_quality_converter.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="b145b9b26648" name="fastq_quality_filter" description="Galaxy wrappers for the Filter by quality tool.">
|
||||
<tool id="cshl_fastq_quality_filter" version="1.0.0" file="fastq_quality_filter.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="04614cf6ed39" name="fastq_to_fasta" description="Galaxy wrappers for the tool FASTQ to FASTA: converter">
|
||||
<tool id="cshl_fastq_to_fasta" version="1.0.0" file="fastq_to_fasta.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="7f9660a246c0" name="fastx_artifacts_filter" description="Galaxy wrappers for the Remove sequencing artifacts tool.">
|
||||
<tool id="cshl_fastx_artifacts_filter" version="1.0.0" file="fastx_artifacts_filter.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="a12850d0559b" name="fastx_barcode_splitter" description="Galaxy wrappers for the Barcode Splitter tool.">
|
||||
<tool id="cshl_fastx_barcode_splitter" version="1.0.0" file="fastx_barcode_splitter.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="2fe2d2b9e8e4" name="fastx_clipper" description="Galaxy wrappers for the tool Clip: adapter sequences">
|
||||
<tool id="cshl_fastx_clipper" version="1.0.1" file="fastx_clipper.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="e942fd3a76a5" name="fastx_collapser" description="Galaxy wrappers for the tool Collapse: sequences">
|
||||
<tool id="cshl_fastx_collapser" version="1.0.0" file="fastx_collapser.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="bcc5424a4a3c" name="fastx_nucleotides_distribution" description="Galaxy wrappers for the Draw nucleotides distribution chart tool.">
|
||||
<tool id="cshl_fastx_nucleotides_distribution" version="1.0.0" file="fastx_nucleotides_distribution.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="45f077341b24" name="fastx_quality_statistics" description="Galaxy wrappers for the Compute quality statistics tool.">
|
||||
<tool id="cshl_fastx_quality_statistics" version="1.0.0" file="fastx_quality_statistics.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="d7bce63e6e09" name="fastx_renamer" description="Galaxy wrappers for the Rename sequences tool.">
|
||||
<tool id="cshl_fastx_renamer" version="0.0.11" file="fastx_renamer.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="c58871138668" name="fastx_reverse_complement" description="Galaxy wrappers for the Reverse-Complement tool.">
|
||||
<tool id="cshl_fastx_reverse_complement" version="1.0.0" file="fastx_reverse_complement.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="9cb372481a71" name="fastx_trimmer" description="Galaxy wrappers for the Trim sequences tool.">
|
||||
<tool id="cshl_fastx_trimmer" version="1.0.0" file="fastx_trimmer.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="64133669255f" name="hgv_fundo" description="Galaxy wrappers for the tool FunDO: human genes associated with disease terms">
|
||||
<tool id="hgv_funDo" version="1.0.0" file="funDo.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="0e80ffa5b7f7" name="hgv_hilbertvis" description="Galaxy wrappers for the tool HVIS: visualization of genomic data with the Hilbert curve">
|
||||
<tool id="hgv_hilbertvis" version="1.0.0" file="hilbertvis.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="5ad24b81dd10" name="indels_3way" description="Galaxy wrappers for the tool Fetch Indels: from 3-way alignments">
|
||||
<tool id="indels_3way" version="1.0.3" file="getIndels_3way.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="4e31fad3f08e" name="microsatellite_birthdeath" description="Galaxy wrappers for the tool Identify microsatellite births and deaths: and causal mutational mechanisms from previously identified orthologous microsatellite sets">
|
||||
<tool id="microsatellite_birthdeath" version="1.0.0" file="microsatellite_birthdeath.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="275433d3a395" name="multispecies_orthologous_microsats" description="Galaxy wrappers for the tool Extract orthologous microsatellites: for multiple (>2) species alignments">
|
||||
<tool id="multispecies_orthologous_microsats" version="1.0.0" file="multispecies_MicrosatDataGenerator_interrupted_GALAXY.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="8f0af7251167" name="mutate_snp_codon" description="Galaxy wrappers for the tool Mutate Codons: with SNPs">
|
||||
<tool id="mutate_snp_codon_1" version="1.0.0" file="mutate_snp_codon.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="a110f9d6ae24" name="pileup_interval" description="Galaxy wrappers for the tool Pileup-to-Interval: condenses pileup format into ranges of bases">
|
||||
<tool id="pileup_interval" version="1.0.0" file="pileup_interval.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="ff1ba9b75337" name="pileup_parser" description="Galaxy wrappers for the tool Filter pileup: on coverage and SNPs">
|
||||
<tool id="pileup_parser" version="1.0.2" file="pileup_parser.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="8c737b8ddc45" name="sam2interval" description="Galaxy wrappers for the tool Convert SAM: to interval">
|
||||
<tool id="sam2interval" version="1.0.1" file="sam2interval.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="0b2424a404d9" name="sam_bitwise_flag_filter" description="Galaxy wrappers for the tool Filter SAM: on bitwise flag values">
|
||||
<tool id="sam_bw_filter" version="1.0.0" file="sam_bitwise_flag_filter.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="a7a49d31c5cf" name="sam_merge" description="Galaxy wrappers for the tool Merge BAM Files: merges BAM files together">
|
||||
<tool id="sam_merge2" version="1.1.2" file="sam_merge.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="95612c159681" name="sam_pileup" description="Galaxy wrappers for the tool Generate pileup: from BAM dataset">
|
||||
<tool id="sam_pileup" version="1.1.1" file="sam_pileup.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="30fdbaccb96b" name="sam_to_bam" description="Galaxy wrappers for the tool SAM-to-BAM: converts SAM format to BAM format">
|
||||
<tool id="sam_to_bam" version="1.1.2" file="sam_to_bam.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="13c4ad597626" name="samtools_flagstat" description="Galaxy wrappers for the tool flagstat: provides simple stats on BAM files">
|
||||
<tool id="samtools_flagstat" version="1.0.0" file="samtools_flagstat.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="44a18a94d7a9" name="samtools_mpileup" description="Galaxy wrappers for the tool MPileup: SNP and indel caller">
|
||||
<tool id="samtools_mpileup" version="0.0.1" file="samtools_mpileup.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="7e92b2a53aab" name="samtools_rmdup" description="Galaxy wrappers for the tool rmdup: remove PCR duplicates">
|
||||
<tool id="samtools_rmdup" version="1.0.0" file="samtools_rmdup.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="68ba55e96489" name="samtools_slice_bam" description="Galaxy wrappers for the tool Slice BAM: by provided regions">
|
||||
<tool id="samtools_slice_bam" version="0.0.1" file="samtools_slice_bam.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="36163bff76cc" name="split_paired_reads" description="Galaxy wrappers for the Split paired end reads tool.">
|
||||
<tool id="split_paired_reads" version="1.0.0" file="split_paired_reads.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="e6a85a9297b3" name="t_test_two_samples" description="Galaxy wrappers for the T Test for Two Samples tool.">
|
||||
<tool id="t_test_two_samples" version="1.0.0" file="t_test_two_samples.xml" />
|
||||
</repository>
|
||||
<repository owner="devteam" changeset_revision="acf952549b53" name="xy_plot" description="Galaxy wrappers for the tool Plotting tool: for multiple series and graph types">
|
||||
<tool id="XY_Plot_1" version="1.0.1" file="xy_plot.xml" />
|
||||
</repository>
|
||||
</toolshed>
|
||||
@@ -76,7 +76,7 @@ def quotacheck( sa_session, users, engine ):
|
||||
sa_session.refresh( user )
|
||||
current = user.get_disk_usage()
|
||||
print user.username, '<' + user.email + '>:',
|
||||
if engine != 'postgres':
|
||||
if engine not in ( 'postgres', 'postgresql' ):
|
||||
new = user.calculate_disk_usage()
|
||||
sa_session.refresh( user )
|
||||
# usage changed while calculating, do it again
|
||||
@@ -94,7 +94,7 @@ def quotacheck( sa_session, users, engine ):
|
||||
print '+%s' % ( nice_size( new - current ) )
|
||||
else:
|
||||
print '-%s' % ( nice_size( current - new ) )
|
||||
if not options.dryrun and engine != 'postgres':
|
||||
if not options.dryrun and engine not in ( 'postgres', 'postgresql' ):
|
||||
user.set_disk_usage( new )
|
||||
sa_session.add( user )
|
||||
sa_session.flush()
|
||||
|
||||
@@ -66,7 +66,10 @@ $.fn.makeAbsolute = function(rebase) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets up popupmenu rendering and binds options functions to the appropriate links
|
||||
* Sets up popupmenu rendering and binds options functions to the appropriate links.
|
||||
* initial_options is a dict with text describing the option pointing to either (a) a
|
||||
* function to perform; or (b) another dict with two required keys, 'url' and 'action' (the
|
||||
* function to perform. (b) is useful for exposing the underlying URL of the option.
|
||||
*/
|
||||
function make_popupmenu(button_element, initial_options) {
|
||||
/* Use the $.data feature to store options with the link element.
|
||||
@@ -92,7 +95,9 @@ function make_popupmenu(button_element, initial_options) {
|
||||
}
|
||||
$.each( options, function( k, v ) {
|
||||
if (v) {
|
||||
menu_element.append( $("<li></li>").append( $("<a href='#'></a>").html(k).click(v) ) );
|
||||
// Action can be either an anonymous function and a mapped dict.
|
||||
var action = v.action || v;
|
||||
menu_element.append( $("<li></li>").append( $("<a>").attr("href", v.url).html(k).click(action) ) );
|
||||
} else {
|
||||
menu_element.append( $("<li></li>").addClass( "head" ).append( $("<a href='#'></a>").html(k) ) );
|
||||
}
|
||||
@@ -164,43 +169,20 @@ function make_popup_menus( parent ) {
|
||||
options[ link.text() ] = null;
|
||||
|
||||
} else {
|
||||
options[ link.text() ] = function() {
|
||||
options[ link.text() ] = {
|
||||
url: href,
|
||||
action: function() {
|
||||
|
||||
// if theres confirm text, send the dialog
|
||||
if ( !confirmtext || confirm( confirmtext ) ) {
|
||||
var f;
|
||||
// relocate the center panel
|
||||
if ( target === "_parent" ) {
|
||||
window.parent.location = href;
|
||||
|
||||
// relocate the entire window
|
||||
} else if ( target === "_top" ) {
|
||||
window.top.location = href;
|
||||
|
||||
// Http request target is a window named demolocal on the local box
|
||||
} else if ( target === "demo" ) {
|
||||
if ( f === undefined || f.closed ) {
|
||||
f = window.open( href,target );
|
||||
f.creator = self;
|
||||
// if theres confirm text, send the dialog
|
||||
if ( !confirmtext || confirm( confirmtext ) ) {
|
||||
// link.click() doesn't use target for some reason,
|
||||
// so manually do it here.
|
||||
if (target) {
|
||||
window.open(href, target);
|
||||
}
|
||||
|
||||
// this panel or other, valid panels
|
||||
} else {
|
||||
// it may be that the button and menu href are not in the same frame:
|
||||
// allow certain frame names to be used as a target
|
||||
var other_valid_targets = [
|
||||
'galaxy_main'
|
||||
// following are blocked until needed
|
||||
//'galaxy_tools',
|
||||
//'galaxy_history'
|
||||
];
|
||||
if( ( target && ( target in window )
|
||||
&& ( other_valid_targets.indexOf( target ) !== -1 ) ) ){
|
||||
window[ target ].location = href;
|
||||
|
||||
// relocate this panel
|
||||
} else {
|
||||
window.location = href;
|
||||
// For all other links, do the default action.
|
||||
else {
|
||||
link.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,11 @@ var GalaxyFrameManager = Backbone.View.extend(
|
||||
{
|
||||
icon : 'fa-icon-th',
|
||||
tooltip : 'Enable/Disable Scratchbook',
|
||||
on_click : function(e) { self.event_panel_active(e) }
|
||||
on_click : function(e) { self.event_panel_active(e) },
|
||||
on_unload : function() {
|
||||
if (self.frame_counter > 0)
|
||||
return "You opened " + self.frame_counter + " frame(s) which will be lost.";
|
||||
}
|
||||
});
|
||||
|
||||
// add to master
|
||||
@@ -157,13 +161,6 @@ var GalaxyFrameManager = Backbone.View.extend(
|
||||
if (self.visible)
|
||||
self.panel_refresh();
|
||||
});
|
||||
|
||||
// catch window close
|
||||
window.onbeforeunload = function()
|
||||
{
|
||||
if (self.frame_counter > 0)
|
||||
return "You opened " + self.frame_counter + " frame(s) which will be lost.";
|
||||
};
|
||||
},
|
||||
|
||||
/*
|
||||
@@ -1062,7 +1059,7 @@ var GalaxyFrameManager = Backbone.View.extend(
|
||||
'<span class="f-icon f-pin fa-icon-pushpin"></span>' +
|
||||
'<span class="f-icon f-close fa-icon-trash"></span>' +
|
||||
'</div>' +
|
||||
'<div class="f-content corner">' + content +
|
||||
'<div class="f-content">' + content +
|
||||
'<div class="f-cover"></div>' +
|
||||
'</div>' +
|
||||
'<span class="f-resize f-icon corner fa-icon-resize-full"></span>' +
|
||||
|
||||
@@ -255,7 +255,8 @@ function init_grid_elements() {
|
||||
});
|
||||
|
||||
// Initialize ratings.
|
||||
$('.community_rating_star').rating({});
|
||||
if ($('.community_rating_star').length !== 0)
|
||||
$('.community_rating_star').rating({});
|
||||
|
||||
// Initialize item menu operations.
|
||||
make_popup_menus();
|
||||
@@ -472,7 +473,7 @@ function update_grid(maintain_page_links) {
|
||||
go_to_URL();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// If there's an operation, do POST; otherwise, do GET.
|
||||
var method = (grid.get('operation') ? "POST" : "GET" );
|
||||
$('.loading-elt-overlay').show(); // Show overlay to indicate loading and prevent user actions.
|
||||
@@ -565,4 +566,4 @@ function submit_operation(selected_button, confirmation_text)
|
||||
|
||||
// return
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
galaxy master v1.0
|
||||
galaxy master
|
||||
*/
|
||||
|
||||
// dependencies
|
||||
@@ -11,6 +11,9 @@ var GalaxyMaster = Backbone.View.extend(
|
||||
// base element
|
||||
el_master: '#masthead',
|
||||
|
||||
// item list
|
||||
list : [],
|
||||
|
||||
// initialize
|
||||
initialize : function(options)
|
||||
{
|
||||
@@ -19,6 +22,21 @@ var GalaxyMaster = Backbone.View.extend(
|
||||
|
||||
// append to master
|
||||
$(this.el_master).append($(this.el));
|
||||
|
||||
// loop through item specific unload functions
|
||||
var self = this;
|
||||
window.onbeforeunload = function()
|
||||
{
|
||||
var text = "";
|
||||
for (key in self.list)
|
||||
if (self.list[key].options.on_unload)
|
||||
{
|
||||
var q = self.list[key].options.on_unload();
|
||||
if (q) text += q + " ";
|
||||
}
|
||||
if (text != "")
|
||||
return text;
|
||||
};
|
||||
},
|
||||
|
||||
// prevent default
|
||||
@@ -27,16 +45,18 @@ var GalaxyMaster = Backbone.View.extend(
|
||||
'mousedown' : function(e) {e.preventDefault()}
|
||||
},
|
||||
|
||||
// adds and displays a new frame/window
|
||||
// adds a new item to the master
|
||||
append : function(item)
|
||||
{
|
||||
$(this.el).append($(item.el));
|
||||
this.list.push(item);
|
||||
},
|
||||
|
||||
// adds and displays a new frame/window
|
||||
// adds a new item to the master
|
||||
prepend : function(item)
|
||||
{
|
||||
$(this.el).prepend($(item.el));
|
||||
this.list.push(item);
|
||||
},
|
||||
|
||||
/*
|
||||
@@ -61,6 +81,7 @@ var GalaxyMasterIcon = Backbone.View.extend(
|
||||
tooltip : "galaxy-icon",
|
||||
with_number : false,
|
||||
on_click : function() { alert ('clicked') },
|
||||
on_unload : null,
|
||||
visible : true
|
||||
},
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
galaxy modal v1.0
|
||||
galaxy modal
|
||||
*/
|
||||
|
||||
// dependencies
|
||||
@@ -9,96 +9,139 @@ define(["libs/backbone/backbone-relational"], function() {
|
||||
var GalaxyModal = Backbone.View.extend(
|
||||
{
|
||||
// base element
|
||||
el_main: '#everything',
|
||||
elMain: '#everything',
|
||||
|
||||
// defaults inputs
|
||||
options:
|
||||
{
|
||||
title : "galaxy-modal",
|
||||
body : "No content available."
|
||||
// defaults options
|
||||
optionsDefault: {
|
||||
title : "galaxy-modal",
|
||||
body : "",
|
||||
backdrop : true
|
||||
},
|
||||
|
||||
// options
|
||||
options : {},
|
||||
|
||||
// initialize
|
||||
initialize : function(options)
|
||||
{
|
||||
initialize : function(options) {
|
||||
// create
|
||||
if (options)
|
||||
{
|
||||
this.create(options);
|
||||
|
||||
// hide
|
||||
$(this.el).hide();
|
||||
}
|
||||
},
|
||||
|
||||
// adds and displays a new frame/window
|
||||
show: function(options)
|
||||
{
|
||||
show: function(options) {
|
||||
// create
|
||||
if (options)
|
||||
this.create(options);
|
||||
|
||||
// fade out
|
||||
this.$el.fadeIn('fast');
|
||||
this.initialize(options);
|
||||
|
||||
// fix height
|
||||
var maxHeight = $(document).height() / 2;
|
||||
this.$body.css('max-height', maxHeight);
|
||||
|
||||
// fix height if available
|
||||
if (this.options.height)
|
||||
this.$body.css('height', Math.min(this.options.height, maxHeight));
|
||||
|
||||
// remove scroll bar
|
||||
if (this.options.height)
|
||||
this.$body.css('overflow', 'hidden');
|
||||
|
||||
// show
|
||||
if (this.visible)
|
||||
this.$el.show();
|
||||
else
|
||||
this.$el.fadeIn('fast');
|
||||
|
||||
// set flag
|
||||
this.visible = true;
|
||||
},
|
||||
|
||||
// hide modal
|
||||
hide: function()
|
||||
{
|
||||
hide: function(){
|
||||
// fade out
|
||||
this.$el.fadeOut('fast');
|
||||
|
||||
// set flag
|
||||
this.visible = false;
|
||||
},
|
||||
|
||||
// destroy modal
|
||||
create: function (options)
|
||||
{
|
||||
// remove element
|
||||
this.$el.remove();
|
||||
// create
|
||||
create: function(options) {
|
||||
// configure options
|
||||
this.options = _.defaults(options, this.optionsDefault);
|
||||
|
||||
// read in defaults
|
||||
if (!options)
|
||||
options = this.options;
|
||||
else
|
||||
options = _.defaults(options, this.options);
|
||||
|
||||
// create element
|
||||
this.setElement(this.template(options.title, options.body));
|
||||
|
||||
// append template
|
||||
$(this.el_main).append($(this.el));
|
||||
// check for progress bar request
|
||||
if (this.options.body == 'progress')
|
||||
this.options.body = $('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>');
|
||||
|
||||
// remove former element
|
||||
if (this.$el)
|
||||
this.$el.remove();
|
||||
|
||||
// create new element
|
||||
this.setElement(this.template(this.options.title));
|
||||
|
||||
// link elements
|
||||
var footer = (this.$el).find('.buttons');
|
||||
this.$body = (this.$el).find('.modal-body');
|
||||
this.$footer = (this.$el).find('.modal-footer');
|
||||
this.$buttons = (this.$el).find('.buttons');
|
||||
this.$backdrop = (this.$el).find('.modal-backdrop');
|
||||
|
||||
// append body
|
||||
this.$body.html(this.options.body);
|
||||
|
||||
// fix min-width so that modal cannot shrink considerably if new content is loaded.
|
||||
this.$body.css('min-width', this.$body.width());
|
||||
|
||||
// configure background
|
||||
if (!this.options.backdrop)
|
||||
this.$backdrop.removeClass('in');
|
||||
|
||||
// append buttons
|
||||
var self = this;
|
||||
if (options.buttons)
|
||||
{
|
||||
if (this.options.buttons) {
|
||||
// link functions
|
||||
$.each(options.buttons, function(name, value)
|
||||
{
|
||||
footer.append($('<button></button>').text(name).click(value)).append(" ");
|
||||
var self = this;
|
||||
$.each(this.options.buttons, function(name, value) {
|
||||
self.$buttons.append($('<button id="' + String(name).toLowerCase() + '"></button>').text(name).click(value)).append(" ");
|
||||
});
|
||||
} else
|
||||
// default close button
|
||||
footer.append($('<button></button>').text('Close').click(function() { self.hide() })).append(" ");
|
||||
// hide footer
|
||||
this.$footer.hide();
|
||||
|
||||
// append to main element
|
||||
$(this.elMain).append($(this.el));
|
||||
},
|
||||
|
||||
// enable buttons
|
||||
enableButton: function(name) {
|
||||
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', false);
|
||||
},
|
||||
|
||||
// disable buttons
|
||||
disableButton: function(name) {
|
||||
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', true);
|
||||
},
|
||||
|
||||
// returns scroll top for body element
|
||||
scrollTop: function()
|
||||
{
|
||||
return this.$body.scrollTop();
|
||||
},
|
||||
|
||||
/*
|
||||
HTML TEMPLATES
|
||||
*/
|
||||
|
||||
// fill regular modal template
|
||||
template: function(title, body)
|
||||
{
|
||||
return '<div class="modal in">' +
|
||||
'<div class="modal-backdrop in" style="z-index: -1;"></div>' +
|
||||
template: function(title) {
|
||||
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">' +
|
||||
'<span><h3 class="title">' + title + '</h3></span>' +
|
||||
'<button type="button" class="close" style="display: none;">×</button>' +
|
||||
'<h4 class="title">' + title + '</h4>' +
|
||||
'</div>' +
|
||||
'<div class="modal-body style="min-width: 540px; max-height: 445px;">' + body + '</div>' +
|
||||
'<div class="modal-body"></div>' +
|
||||
'<div class="modal-footer">' +
|
||||
'<div class="buttons" style="float: right;"></div>' +
|
||||
'</div>' +
|
||||
|
||||
@@ -125,15 +125,22 @@ var Modal = function( options ) {
|
||||
this.$body = this.$dialog.find( ".modal-body" );
|
||||
this.$footer = this.$dialog.find( ".modal-footer" );
|
||||
this.$backdrop = options.backdrop;
|
||||
// Close button
|
||||
this.$header.find( ".close" ).on( "click", $.proxy( this.hide, this ) );
|
||||
}
|
||||
$.extend( Modal.prototype, {
|
||||
setContent: function( options ) {
|
||||
this.$header.hide();
|
||||
// Title
|
||||
if ( options.title ) {
|
||||
this.$header.find( ".title" ).html( options.title );
|
||||
this.$header.show();
|
||||
}
|
||||
if ( options.closeButton ) {
|
||||
this.$header.find( ".close" ).show();
|
||||
this.$header.show();
|
||||
} else {
|
||||
this.$header.hide();
|
||||
this.$header.find( ".close" ).hide();
|
||||
}
|
||||
// Buttons
|
||||
this.$footer.hide();
|
||||
@@ -154,7 +161,7 @@ $.extend( Modal.prototype, {
|
||||
// Body
|
||||
var body = options.body;
|
||||
if ( body == "progress" ) {
|
||||
body = $("<div class='progress progress-striped active'><div class='bar' style='width: 100%'></div></div>");
|
||||
body = $("<div class='progress progress-striped active'><div class='progress-bar' style='width: 100%'></div></div>");
|
||||
}
|
||||
this.$body.html( body );
|
||||
},
|
||||
@@ -168,16 +175,17 @@ $.extend( Modal.prototype, {
|
||||
this.$overlay.show();
|
||||
this.$dialog.show();
|
||||
this.$overlay.addClass("in");
|
||||
// Fix min-width so that modal cannot shrink considerably if
|
||||
// new content is loaded.
|
||||
// Fix min-width so that modal cannot shrink considerably if new content is loaded.
|
||||
this.$body.css( "min-width", this.$body.width() );
|
||||
// Set max-height so that modal does not exceed window size.
|
||||
// FIXME: this could perhaps be handled better using a container for the modal
|
||||
// header-body-footer and setting max-height for the container.
|
||||
// Set max-height so that modal does not exceed window size and is in middle of page.
|
||||
// TODO: this could perhaps be handled better using CSS.
|
||||
this.$body.css( "max-height",
|
||||
// 2* to provide buffer between bottom of modal and bottom of page.
|
||||
$(window).height() - 2 * this.$dialog.offset().top -
|
||||
this.$footer.outerHeight() - this.$header.outerHeight());
|
||||
$(window).height() -
|
||||
this.$footer.outerHeight() -
|
||||
this.$header.outerHeight() -
|
||||
parseInt( this.$dialog.css( "padding-top" ), 10 ) -
|
||||
parseInt( this.$dialog.css( "padding-bottom" ), 10 )
|
||||
);
|
||||
}
|
||||
// Callback on init
|
||||
if ( callback ) {
|
||||
@@ -222,8 +230,8 @@ function show_in_overlay( options ) {
|
||||
hide_modal();
|
||||
$("#overlay-background").unbind( "click.overlay" );
|
||||
});
|
||||
show_modal( null, $( "<div style='margin: -5px;'><img id='close_button' style='position:absolute;right:-17px;top:-15px;src='" + galaxy_config.root + "static/images/closebox.png'><iframe style='margin: 0; padding: 0;' src='" + options.url + "' width='" + width + "' height='" + height + "' scrolling='" + scroll + "' frameborder='0'></iframe></div>" ) );
|
||||
$("#close_button").bind( "click", function() { hide_modal(); } );
|
||||
modal.setContent( { closeButton: true, title: " ", body: $( "<div style='margin: -5px;'><iframe style='margin: 0; padding: 0;' src='" + options.url + "' width='" + width + "' height='" + height + "' scrolling='" + scroll + "' frameborder='0'></iframe></div>" ) } );
|
||||
modal.show( { backdrop: true } );
|
||||
}
|
||||
|
||||
function user_changed( user_email, is_admin ) {
|
||||
|
||||
+444
-89
@@ -1,25 +1,66 @@
|
||||
/*
|
||||
galaxy upload v1.0
|
||||
galaxy upload
|
||||
*/
|
||||
|
||||
// dependencies
|
||||
define(["galaxy.modal", "galaxy.master", "utils/galaxy.uploadbox", "libs/backbone/backbone-relational"], function(mod_modal, mod_master) {
|
||||
define(["galaxy.modal", "galaxy.master", "utils/galaxy.utils", "utils/galaxy.uploadbox", "libs/backbone/backbone-relational"], function(mod_modal, mod_master, mod_util) {
|
||||
|
||||
// galaxy upload
|
||||
var GalaxyUpload = Backbone.View.extend(
|
||||
{
|
||||
// own modal
|
||||
modal : null,
|
||||
modal : null,
|
||||
|
||||
// button
|
||||
button_show : null,
|
||||
|
||||
// file counter
|
||||
file_counter: 0,
|
||||
// upload mod
|
||||
uploadbox: null,
|
||||
|
||||
// extension types
|
||||
select_extension : {
|
||||
'auto' : 'Auto-detect'
|
||||
},
|
||||
|
||||
// states
|
||||
state : {
|
||||
init : 'fa-icon-trash',
|
||||
queued : 'fa-icon-spinner fa-icon-spin',
|
||||
running : '__running__',
|
||||
success : 'fa-icon-ok',
|
||||
error : 'fa-icon-warning-sign'
|
||||
},
|
||||
|
||||
// counter
|
||||
counter : {
|
||||
// stats
|
||||
announce : 0,
|
||||
success : 0,
|
||||
error : 0,
|
||||
running : 0,
|
||||
|
||||
// reset stats
|
||||
reset : function()
|
||||
{
|
||||
this.announce = this.success = this.error = this.running = 0;
|
||||
}
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize : function()
|
||||
{
|
||||
// wait for galaxy history panel (workaround due to the use of iframes)
|
||||
if (!Galaxy.currHistoryPanel)
|
||||
{
|
||||
var self = this;
|
||||
window.setTimeout(function() { self.initialize() }, 500)
|
||||
return;
|
||||
}
|
||||
|
||||
// check if logged in
|
||||
if (!Galaxy.currUser.get('id'))
|
||||
return;
|
||||
|
||||
// add activate icon
|
||||
var self = this;
|
||||
this.button_show = new mod_master.GalaxyMasterIcon (
|
||||
@@ -27,97 +68,324 @@ var GalaxyUpload = Backbone.View.extend(
|
||||
icon : 'fa-icon-upload',
|
||||
tooltip : 'Upload Files',
|
||||
on_click : function(e) { self.event_show(e) },
|
||||
on_unload : function() {
|
||||
if (self.counter.running > 0)
|
||||
return "Currently uploads are running.";
|
||||
},
|
||||
with_number : true
|
||||
});
|
||||
|
||||
// add to master
|
||||
Galaxy.master.prepend(this.button_show);
|
||||
},
|
||||
|
||||
// events
|
||||
events :
|
||||
{
|
||||
'mouseover' : 'event_mouseover',
|
||||
'mouseleave' : 'event_mouseleave'
|
||||
|
||||
// load extension
|
||||
var self = this;
|
||||
mod_util.jsonFromUrl(galaxy_config.root + "api/datatypes",
|
||||
function(datatypes) {
|
||||
for (key in datatypes) {
|
||||
var filetype = datatypes[key];
|
||||
self.select_extension[filetype] = filetype;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// mouse over
|
||||
event_mouseover : function (e)
|
||||
event_dragover : function (e)
|
||||
{
|
||||
$('#galaxy-upload-box').addClass('highlight');
|
||||
},
|
||||
|
||||
// mouse left
|
||||
event_mouseleave : function (e)
|
||||
event_dragleave : function (e)
|
||||
{
|
||||
$('#galaxy-upload-box').removeClass('highlight');
|
||||
},
|
||||
|
||||
|
||||
// start
|
||||
event_start : function(index, file, message)
|
||||
event_announce : function(index, file, message)
|
||||
{
|
||||
// make id
|
||||
var id = '#galaxy-upload-file-' + index;
|
||||
var id = '#upload-' + index;
|
||||
|
||||
// add upload item
|
||||
$(this.el).find('tbody:last').append(this.template_row(id, this.select_extension));
|
||||
|
||||
// add tag
|
||||
$('#galaxy-upload-box').append(this.template_file(id));
|
||||
// scroll to bottom
|
||||
//$(this.el).scrollTop($(this.el).prop('scrollHeight'));
|
||||
|
||||
// access upload item
|
||||
var it = this.get_upload_item(index);
|
||||
|
||||
// fade in
|
||||
it.fadeIn();
|
||||
|
||||
// update title
|
||||
$('#galaxy-upload-file-' + index).find('.title').html(file.name);
|
||||
it.find('#title').html(file.name);
|
||||
|
||||
// update info
|
||||
it.find('#size').html(this.size_to_string (file.size));
|
||||
|
||||
// add functionality to remove button
|
||||
var self = this;
|
||||
it.find('#symbol').on('click', function() { self.event_remove (index) });
|
||||
|
||||
// initialize progress
|
||||
this.event_progress(index, file, 0);
|
||||
|
||||
// update counter
|
||||
this.file_counter++;
|
||||
this.refresh();
|
||||
this.counter.announce++;
|
||||
|
||||
// update screen
|
||||
this.update_screen();
|
||||
},
|
||||
|
||||
// start
|
||||
event_initialize : function(index, file, message)
|
||||
{
|
||||
// update on screen counter
|
||||
this.button_show.number(this.counter.announce);
|
||||
|
||||
// get element
|
||||
var it = this.get_upload_item(index);
|
||||
|
||||
// update status
|
||||
var sy = it.find('#symbol');
|
||||
sy.addClass(this.state.running);
|
||||
|
||||
// get configuration
|
||||
var current_history = Galaxy.currHistoryPanel.model.get('id');
|
||||
var file_type = it.find('#extension').val();
|
||||
var space_to_tabs = it.find('#space_to_tabs').is(':checked');
|
||||
|
||||
// configure uploadbox
|
||||
this.uploadbox.configure({url : galaxy_config.root + "api/tools/", paramname : "files_0|file_data"});
|
||||
|
||||
// configure tool
|
||||
tool_input = {};
|
||||
tool_input['dbkey'] = '?';
|
||||
tool_input['file_type'] = file_type;
|
||||
tool_input['files_0|NAME'] = file.name;
|
||||
tool_input['files_0|type'] = 'upload_dataset';
|
||||
tool_input['space_to_tabs'] = space_to_tabs;
|
||||
|
||||
// setup data
|
||||
data = {};
|
||||
data['history_id'] = current_history;
|
||||
data['tool_id'] = 'upload1';
|
||||
data['inputs'] = JSON.stringify(tool_input);
|
||||
|
||||
// return additional data to be send with file
|
||||
return data;
|
||||
},
|
||||
|
||||
// progress
|
||||
event_progress : function(index, file, message)
|
||||
{
|
||||
// get progress bar
|
||||
var el = $('#galaxy-upload-file-' + index);
|
||||
// get element
|
||||
var it = this.get_upload_item(index);
|
||||
|
||||
// get value
|
||||
var percentage = parseInt(message);
|
||||
|
||||
// update progress
|
||||
el.find('.progress').css({ width : percentage + '%' });
|
||||
it.find('.progress-bar').css({ width : percentage + '%' });
|
||||
|
||||
// update info
|
||||
el.find('.info').html(percentage + '% of ' + this.size_to_string (file.size));
|
||||
// update value
|
||||
if (percentage != 100)
|
||||
it.find('#percentage').html(percentage + '%');
|
||||
else
|
||||
it.find('#percentage').html('Adding to history...');
|
||||
},
|
||||
|
||||
// end
|
||||
// success
|
||||
event_success : function(index, file, message)
|
||||
{
|
||||
// update galaxy history
|
||||
Galaxy.currHistoryPanel.refresh();
|
||||
// make sure progress is shown correctly
|
||||
this.event_progress(index, file, 100);
|
||||
|
||||
// update on screen counter
|
||||
this.button_show.number('');
|
||||
|
||||
// update counter
|
||||
this.file_counter--;
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
// end
|
||||
event_error : function(index, file, message)
|
||||
{
|
||||
// get file box
|
||||
var el = $('#galaxy-upload-file-' + index);
|
||||
this.counter.announce--;
|
||||
this.counter.success++;
|
||||
|
||||
// update on screen info
|
||||
this.update_screen();
|
||||
|
||||
// get element
|
||||
var it = this.get_upload_item(index);
|
||||
|
||||
// update progress frame
|
||||
el.find('.progress-frame').addClass('failed');
|
||||
it.addClass('success');
|
||||
|
||||
// update error message
|
||||
el.find('.error').html("<strong>Failed:</strong> " + message);
|
||||
|
||||
// update progress
|
||||
// update text
|
||||
it.find('#percentage').html('100%');
|
||||
|
||||
// update icon
|
||||
var sy = it.find('#symbol');
|
||||
sy.removeClass(this.state.running);
|
||||
sy.removeClass(this.state.queued);
|
||||
sy.addClass(this.state.success);
|
||||
|
||||
// update galaxy history
|
||||
Galaxy.currHistoryPanel.refresh();
|
||||
},
|
||||
|
||||
// error
|
||||
event_error : function(index, file, message)
|
||||
{
|
||||
// make sure progress is shown correctly
|
||||
this.event_progress(index, file, 0);
|
||||
|
||||
// update on screen counter
|
||||
this.button_show.number('');
|
||||
|
||||
// update counter
|
||||
this.file_counter--;
|
||||
this.refresh();
|
||||
this.counter.announce--;
|
||||
this.counter.error++;
|
||||
|
||||
// update on screen info
|
||||
this.update_screen();
|
||||
|
||||
// get element
|
||||
var it = this.get_upload_item(index);
|
||||
|
||||
// update progress frame
|
||||
it.addClass('danger');
|
||||
|
||||
// remove progress bar
|
||||
it.find('.progress').remove();
|
||||
|
||||
// write error message
|
||||
it.find('#info').html('<strong>Failed: </strong>' + message).show();
|
||||
|
||||
// update icon
|
||||
var sy = it.find('#symbol');
|
||||
sy.removeClass(this.state.running);
|
||||
sy.removeClass(this.state.queued);
|
||||
sy.addClass(this.state.error);
|
||||
},
|
||||
|
||||
// start upload process
|
||||
event_upload : function()
|
||||
{
|
||||
// check
|
||||
if (this.counter.announce == 0 || this.counter.running > 0)
|
||||
return;
|
||||
|
||||
// switch icons for new uploads
|
||||
var items = $(this.el).find('.upload-item');
|
||||
var self = this;
|
||||
items.each(function()
|
||||
{
|
||||
var symbol = $(this).find('#symbol');
|
||||
if(symbol.hasClass(self.state.init))
|
||||
{
|
||||
// set status
|
||||
symbol.removeClass(self.state.init);
|
||||
symbol.addClass(self.state.queued);
|
||||
|
||||
// disable options
|
||||
$(this).find('#extension').attr('disabled', true);
|
||||
$(this).find('#space_to_tabs').attr('disabled', true);
|
||||
}
|
||||
});
|
||||
|
||||
// update running
|
||||
this.counter.running = this.counter.announce;
|
||||
this.update_screen();
|
||||
|
||||
// initiate upload procedure in plugin
|
||||
this.uploadbox.upload();
|
||||
},
|
||||
|
||||
// pause upload process
|
||||
event_pause : function()
|
||||
{
|
||||
// check
|
||||
if (this.counter.running == 0)
|
||||
return;
|
||||
|
||||
// request pause
|
||||
this.uploadbox.pause();
|
||||
|
||||
// set html content
|
||||
$('#upload-info').html('Queueing will pause after completing the current file...');
|
||||
},
|
||||
|
||||
// queue is done
|
||||
event_complete: function()
|
||||
{
|
||||
// update running
|
||||
this.counter.running = 0;
|
||||
this.update_screen();
|
||||
|
||||
// switch icons for new uploads
|
||||
var items = $(this.el).find('.upload-item');
|
||||
var self = this;
|
||||
items.each(function()
|
||||
{
|
||||
var symbol = $(this).find('#symbol');
|
||||
if(symbol.hasClass(self.state.queued) && !symbol.hasClass(self.state.running))
|
||||
{
|
||||
// update status
|
||||
symbol.removeClass(self.state.queued);
|
||||
symbol.addClass(self.state.init);
|
||||
|
||||
// disable options
|
||||
$(this).find('#extension').attr('disabled', false);
|
||||
$(this).find('#space_to_tabs').attr('disabled', false);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// remove all
|
||||
event_reset : function()
|
||||
{
|
||||
// make sure queue is not running
|
||||
if (this.counter.running == 0)
|
||||
{
|
||||
// remove from screen
|
||||
var items = $(this.el).find('.upload-item');
|
||||
$(this.el).find('table').fadeOut({ complete : function() { items.remove(); }});
|
||||
|
||||
// reset counter
|
||||
this.counter.reset();
|
||||
|
||||
// show on screen info
|
||||
this.update_screen();
|
||||
|
||||
// remove from queue
|
||||
this.uploadbox.reset();
|
||||
}
|
||||
},
|
||||
|
||||
// remove item from upload list
|
||||
event_remove : function(index)
|
||||
{
|
||||
// get item
|
||||
var it = this.get_upload_item(index);
|
||||
var sy = it.find('#symbol');
|
||||
|
||||
// only remove from queue if not in processing line
|
||||
if (sy.hasClass(this.state.init) || sy.hasClass(this.state.success) || sy.hasClass(this.state.error))
|
||||
{
|
||||
// reduce counter
|
||||
if (it.hasClass('success'))
|
||||
this.counter.success--;
|
||||
else if (it.hasClass('danger'))
|
||||
this.counter.error--;
|
||||
else
|
||||
this.counter.announce--;
|
||||
|
||||
// show on screen info
|
||||
this.update_screen();
|
||||
|
||||
// remove from queue
|
||||
this.uploadbox.remove(index);
|
||||
|
||||
// remove element
|
||||
it.remove();
|
||||
}
|
||||
},
|
||||
|
||||
// show/hide upload frame
|
||||
@@ -126,14 +394,6 @@ var GalaxyUpload = Backbone.View.extend(
|
||||
// prevent default
|
||||
e.preventDefault();
|
||||
|
||||
// wait for galaxy history panel (workaround due to the use of iframes)
|
||||
if (!Galaxy.currHistoryPanel)
|
||||
{
|
||||
var self = this;
|
||||
window.setTimeout(function() { self.event_show(e) }, 200)
|
||||
return;
|
||||
}
|
||||
|
||||
// create modal
|
||||
if (!this.modal)
|
||||
{
|
||||
@@ -142,44 +402,47 @@ var GalaxyUpload = Backbone.View.extend(
|
||||
this.modal = new mod_modal.GalaxyModal(
|
||||
{
|
||||
title : 'Upload files from your local drive',
|
||||
body : this.template(),
|
||||
body : this.template('upload-box', 'upload-info'),
|
||||
buttons : {
|
||||
'Close' : function() {self.modal.hide()}
|
||||
}
|
||||
'Select' : function() {self.uploadbox.select()},
|
||||
'Upload' : function() {self.event_upload()},
|
||||
'Pause' : function() {self.event_pause()},
|
||||
'Reset' : function() {self.event_reset()},
|
||||
'Close' : function() {self.modal.hide()}
|
||||
},
|
||||
height : '350'
|
||||
});
|
||||
|
||||
// get current history
|
||||
var current_history = Galaxy.currHistoryPanel.model.get('id');
|
||||
|
||||
// set element
|
||||
this.setElement('#upload-box');
|
||||
|
||||
// file upload
|
||||
var self = this;
|
||||
$('#galaxy-upload-box').uploadbox(
|
||||
this.uploadbox = this.$el.uploadbox(
|
||||
{
|
||||
url : galaxy_config.root + "api/histories/" + current_history + "/contents",
|
||||
dragover : self.event_mouseover,
|
||||
dragleave : self.event_mouseleave,
|
||||
start : function(index, file, message) { self.event_start(index, file, message) },
|
||||
dragover : function() { self.event_dragover() },
|
||||
dragleave : function() { self.event_dragleave() },
|
||||
announce : function(index, file, message) { self.event_announce(index, file, message) },
|
||||
initialize : function(index, file, message) { return self.event_initialize(index, file, message) },
|
||||
success : function(index, file, message) { self.event_success(index, file, message) },
|
||||
progress : function(index, file, message) { self.event_progress(index, file, message) },
|
||||
error : function(index, file, message) { self.event_error(index, file, message) },
|
||||
data : {source : "upload"}
|
||||
complete : function() { self.event_complete() },
|
||||
});
|
||||
|
||||
// set element
|
||||
this.setElement('#galaxy-upload-box');
|
||||
// setup info
|
||||
this.update_screen();
|
||||
}
|
||||
|
||||
// show modal
|
||||
this.modal.show();
|
||||
},
|
||||
|
||||
// update counter
|
||||
refresh: function ()
|
||||
// get upload item
|
||||
get_upload_item: function(index)
|
||||
{
|
||||
if (this.file_counter > 0)
|
||||
this.button_show.number(this.file_counter);
|
||||
else
|
||||
this.button_show.number('');
|
||||
// get element
|
||||
return $(this.el).find('#upload-' + index);
|
||||
},
|
||||
|
||||
// to string
|
||||
@@ -196,23 +459,115 @@ var GalaxyUpload = Backbone.View.extend(
|
||||
return "<strong>" + (Math.round(size) / 10) + "</strong> " + unit;
|
||||
},
|
||||
|
||||
// load html template
|
||||
template: function()
|
||||
// set screen
|
||||
update_screen: function ()
|
||||
{
|
||||
return '<form id="galaxy-upload-box" class="galaxy-upload-box"></form>';
|
||||
/*
|
||||
update on screen info
|
||||
*/
|
||||
|
||||
// check default message
|
||||
if(this.counter.announce == 0)
|
||||
{
|
||||
if (this.uploadbox.compatible)
|
||||
message = 'Drag&drop files into this box or click \'Select\' to select files!';
|
||||
else
|
||||
message = 'Unfortunately, your browser does not support multiple file uploads or drag&drop.<br>Please upgrade to i.e. Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+.'
|
||||
} else {
|
||||
if (this.counter.running == 0)
|
||||
message = 'You added ' + this.counter.announce + ' file(s) to the queue. Add more files or click \'Upload\' to proceed.';
|
||||
else
|
||||
message = 'Please wait...' + this.counter.announce + ' out of ' + this.counter.running + ' remaining.';
|
||||
}
|
||||
|
||||
// set html content
|
||||
$('#upload-info').html(message);
|
||||
|
||||
/*
|
||||
update button status
|
||||
*/
|
||||
|
||||
// update reset button
|
||||
if (this.counter.running == 0 && this.counter.announce + this.counter.success + this.counter.error > 0)
|
||||
this.modal.enableButton('Reset');
|
||||
else
|
||||
this.modal.disableButton('Reset');
|
||||
|
||||
// update upload button
|
||||
if (this.counter.running == 0 && this.counter.announce > 0)
|
||||
this.modal.enableButton('Upload');
|
||||
else
|
||||
this.modal.disableButton('Upload');
|
||||
|
||||
// pause upload button
|
||||
if (this.counter.running > 0)
|
||||
this.modal.enableButton('Pause');
|
||||
else
|
||||
this.modal.disableButton('Pause');
|
||||
|
||||
// select upload button
|
||||
if (this.counter.running == 0)
|
||||
this.modal.enableButton('Select');
|
||||
else
|
||||
this.modal.disableButton('Select');
|
||||
|
||||
// table visibility
|
||||
if (this.counter.announce + this.counter.success + this.counter.error > 0)
|
||||
$(this.el).find('table').show();
|
||||
else
|
||||
$(this.el).find('table').hide();
|
||||
},
|
||||
|
||||
// load html template
|
||||
template: function(id, idInfo)
|
||||
{
|
||||
return '<div id="' + id + '" class="upload-box">' +
|
||||
'<table class="table table-striped" style="display: none;">' +
|
||||
'<thead>' +
|
||||
'<tr>' +
|
||||
'<th>Name</th>' +
|
||||
'<th>Size</th>' +
|
||||
'<th>Type</th>' +
|
||||
'<th>Space→Tab</th>' +
|
||||
'<th>Status</th>' +
|
||||
'<th></th>' +
|
||||
'</tr>' +
|
||||
'</thead>' +
|
||||
'<tbody></tbody>' +
|
||||
'</table>' +
|
||||
'</div>' +
|
||||
'<h6 id="' + idInfo + '" class="upload-info"></h6>';
|
||||
},
|
||||
|
||||
// load html template
|
||||
template_file: function(id)
|
||||
template_row: function(id, select_extension)
|
||||
{
|
||||
return '<div id="' + id.substr(1) + '" class="file corner-soft shadow">' +
|
||||
'<div class="title"></div>' +
|
||||
'<div class="error"></div>' +
|
||||
'<div class="progress-frame corner-soft">' +
|
||||
'<div class="progress"></div>' +
|
||||
'</div>' +
|
||||
'<div class="info"></div>' +
|
||||
'</div>';
|
||||
// construct template
|
||||
var tmpl = '<tr id="' + id.substr(1) + '" class="upload-item">' +
|
||||
'<td><div id="title" class="title"></div></td>' +
|
||||
'<td><div id="size" class="size"></div></td>' +
|
||||
'<td>' +
|
||||
'<select id="extension" class="extension">';
|
||||
|
||||
// add file types to selection
|
||||
for (key in select_extension)
|
||||
tmpl += '<option value="' + key + '">' + select_extension[key] + '</option>';
|
||||
|
||||
tmpl += '</select>' +
|
||||
'</td>' +
|
||||
'<td><input id="space_to_tabs" type="checkbox"></input></td>' +
|
||||
'<td>' +
|
||||
'<div id="info" class="info">' +
|
||||
'<div class="progress">' +
|
||||
'<div class="progress-bar progress-bar-success"></div>' +
|
||||
'<div id="percentage" class="percentage">0%</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'<td><div id="symbol" class="symbol ' + this.state.init + '"></div></td>' +
|
||||
'</tr>';
|
||||
|
||||
// return html string
|
||||
return tmpl;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -199,8 +199,8 @@ $.extend( Node.prototype, {
|
||||
.css( { display: 'none' } )
|
||||
.appendTo( "body" )
|
||||
.append(
|
||||
$("<div class='buttons'></div>").append(
|
||||
$("<img/>").attr("src", galaxy_config.root + 'static/images/delete_icon.png').click( function() {
|
||||
$("<div class='button'></div>").append(
|
||||
$("<div/>").addClass("fa-icon-button fa-icon-remove").click( function() {
|
||||
$.each( terminal.connectors, function( _, x ) {
|
||||
if (x) {
|
||||
x.destroy();
|
||||
@@ -859,12 +859,9 @@ function prebuild_node( type, title_text, tool_id ) {
|
||||
// Fix width to computed width
|
||||
// Now add floats
|
||||
var buttons = $("<div class='buttons' style='float: right;'></div>");
|
||||
buttons.append( $("<img/>").attr("src", galaxy_config.root + 'static/images/delete_icon.png').click( function( e ) {
|
||||
buttons.append( $("<div>").addClass("fa-icon-button fa-icon-remove").click( function( e ) {
|
||||
node.destroy();
|
||||
} ).hover(
|
||||
function() { $(this).attr( "src", galaxy_config.root + "static/images/delete_icon_dark.png" ); },
|
||||
function() { $(this).attr( "src", galaxy_config.root + "static/images/delete_icon.png" ); }
|
||||
) );
|
||||
}));
|
||||
// Place inside container
|
||||
f.appendTo( "#canvas-container" );
|
||||
// Position in container
|
||||
|
||||
Vendored
+437
-389
@@ -1,9 +1,8 @@
|
||||
|
||||
/* ===================================================
|
||||
* bootstrap-transition.js v2.0.4
|
||||
* http://twitter.github.com/bootstrap/javascript.html#transitions
|
||||
* ===================================================
|
||||
* Copyright 2012 Twitter, Inc.
|
||||
/* ========================================================================
|
||||
* Bootstrap: transition.js v3.0.0
|
||||
* http://twbs.github.com/bootstrap/javascript.html#transitions
|
||||
* ========================================================================
|
||||
* Copyright 2013 Twitter, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,55 +15,49 @@
|
||||
* 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 ($) {
|
||||
+function ($) { "use strict";
|
||||
|
||||
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
|
||||
// ============================================================
|
||||
|
||||
function transitionEnd() {
|
||||
var el = document.createElement('bootstrap')
|
||||
|
||||
var transEndEventNames = {
|
||||
'WebkitTransition' : 'webkitTransitionEnd'
|
||||
, 'MozTransition' : 'transitionend'
|
||||
, 'OTransition' : 'oTransitionEnd otransitionend'
|
||||
, 'transition' : 'transitionend'
|
||||
}
|
||||
|
||||
for (var name in transEndEventNames) {
|
||||
if (el.style[name] !== undefined) {
|
||||
return { end: transEndEventNames[name] }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// http://blog.alexmaccaw.com/css-transitions
|
||||
$.fn.emulateTransitionEnd = function (duration) {
|
||||
var called = false, $el = this
|
||||
$(this).one($.support.transition.end, function () { called = true })
|
||||
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
|
||||
setTimeout(callback, duration)
|
||||
return this
|
||||
}
|
||||
|
||||
$(function () {
|
||||
|
||||
"use strict"; // jshint ;_;
|
||||
|
||||
|
||||
/* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
|
||||
* ======================================================= */
|
||||
|
||||
$.support.transition = (function () {
|
||||
|
||||
var transitionEnd = (function () {
|
||||
|
||||
var el = document.createElement('bootstrap')
|
||||
, transEndEventNames = {
|
||||
'WebkitTransition' : 'webkitTransitionEnd'
|
||||
, 'MozTransition' : 'transitionend'
|
||||
, 'OTransition' : 'oTransitionEnd'
|
||||
, 'msTransition' : 'MSTransitionEnd'
|
||||
, 'transition' : 'transitionend'
|
||||
}
|
||||
, name
|
||||
|
||||
for (name in transEndEventNames){
|
||||
if (el.style[name] !== undefined) {
|
||||
return transEndEventNames[name]
|
||||
}
|
||||
}
|
||||
|
||||
}())
|
||||
|
||||
return transitionEnd && {
|
||||
end: transitionEnd
|
||||
}
|
||||
|
||||
})()
|
||||
|
||||
$.support.transition = transitionEnd()
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
|
||||
/* ========================================================
|
||||
* bootstrap-tab.js v2.0.4
|
||||
* http://twitter.github.com/bootstrap/javascript.html#tabs
|
||||
* ========================================================
|
||||
/* ========================================================================
|
||||
* Bootstrap: tab.js v3.0.0
|
||||
* http://twbs.github.com/bootstrap/javascript.html#tabs
|
||||
* ========================================================================
|
||||
* Copyright 2012 Twitter, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -78,106 +71,99 @@
|
||||
* 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 ($) {
|
||||
+function ($) { "use strict";
|
||||
|
||||
"use strict"; // jshint ;_;
|
||||
// TAB CLASS DEFINITION
|
||||
// ====================
|
||||
|
||||
|
||||
/* TAB CLASS DEFINITION
|
||||
* ==================== */
|
||||
|
||||
var Tab = function ( element ) {
|
||||
var Tab = function (element) {
|
||||
this.element = $(element)
|
||||
}
|
||||
|
||||
Tab.prototype = {
|
||||
Tab.prototype.show = function () {
|
||||
var $this = this.element
|
||||
var $ul = $this.closest('ul:not(.dropdown-menu)')
|
||||
var selector = $this.attr('data-target')
|
||||
|
||||
constructor: Tab
|
||||
|
||||
, show: function () {
|
||||
var $this = this.element
|
||||
, $ul = $this.closest('ul:not(.dropdown-menu)')
|
||||
, selector = $this.attr('data-target')
|
||||
, previous
|
||||
, $target
|
||||
, e
|
||||
|
||||
if (!selector) {
|
||||
selector = $this.attr('href')
|
||||
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
|
||||
}
|
||||
|
||||
if ( $this.parent('li').hasClass('active') ) return
|
||||
|
||||
previous = $ul.find('.active a').last()[0]
|
||||
|
||||
e = $.Event('show', {
|
||||
relatedTarget: previous
|
||||
})
|
||||
|
||||
$this.trigger(e)
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
$target = $(selector)
|
||||
|
||||
this.activate($this.parent('li'), $ul)
|
||||
this.activate($target, $target.parent(), function () {
|
||||
$this.trigger({
|
||||
type: 'shown'
|
||||
, relatedTarget: previous
|
||||
})
|
||||
})
|
||||
if (!selector) {
|
||||
selector = $this.attr('href')
|
||||
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
|
||||
}
|
||||
|
||||
, activate: function ( element, container, callback) {
|
||||
var $active = container.find('> .active')
|
||||
, transition = callback
|
||||
&& $.support.transition
|
||||
&& $active.hasClass('fade')
|
||||
if ($this.parent('li').hasClass('active')) return
|
||||
|
||||
function next() {
|
||||
$active
|
||||
.removeClass('active')
|
||||
.find('> .dropdown-menu > .active')
|
||||
.removeClass('active')
|
||||
var previous = $ul.find('.active:last a')[0]
|
||||
var e = $.Event('show.bs.tab', {
|
||||
relatedTarget: previous
|
||||
})
|
||||
|
||||
element.addClass('active')
|
||||
$this.trigger(e)
|
||||
|
||||
if (transition) {
|
||||
element[0].offsetWidth // reflow for transition
|
||||
element.addClass('in')
|
||||
} else {
|
||||
element.removeClass('fade')
|
||||
}
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
if ( element.parent('.dropdown-menu') ) {
|
||||
element.closest('li.dropdown').addClass('active')
|
||||
}
|
||||
var $target = $(selector)
|
||||
|
||||
callback && callback()
|
||||
this.activate($this.parent('li'), $ul)
|
||||
this.activate($target, $target.parent(), function () {
|
||||
$this.trigger({
|
||||
type: 'shown.bs.tab'
|
||||
, relatedTarget: previous
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Tab.prototype.activate = function (element, container, callback) {
|
||||
var $active = container.find('> .active')
|
||||
var transition = callback
|
||||
&& $.support.transition
|
||||
&& $active.hasClass('fade')
|
||||
|
||||
function next() {
|
||||
$active
|
||||
.removeClass('active')
|
||||
.find('> .dropdown-menu > .active')
|
||||
.removeClass('active')
|
||||
|
||||
element.addClass('active')
|
||||
|
||||
if (transition) {
|
||||
element[0].offsetWidth // reflow for transition
|
||||
element.addClass('in')
|
||||
} else {
|
||||
element.removeClass('fade')
|
||||
}
|
||||
|
||||
transition ?
|
||||
$active.one($.support.transition.end, next) :
|
||||
next()
|
||||
if (element.parent('.dropdown-menu')) {
|
||||
element.closest('li.dropdown').addClass('active')
|
||||
}
|
||||
|
||||
$active.removeClass('in')
|
||||
callback && callback()
|
||||
}
|
||||
|
||||
transition ?
|
||||
$active
|
||||
.one($.support.transition.end, next)
|
||||
.emulateTransitionEnd(150) :
|
||||
next()
|
||||
|
||||
$active.removeClass('in')
|
||||
}
|
||||
|
||||
|
||||
/* TAB PLUGIN DEFINITION
|
||||
* ===================== */
|
||||
// TAB PLUGIN DEFINITION
|
||||
// =====================
|
||||
|
||||
var old = $.fn.tab
|
||||
|
||||
$.fn.tab = function ( option ) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
, data = $this.data('tab')
|
||||
if (!data) $this.data('tab', (data = new Tab(this)))
|
||||
var data = $this.data('bs.tab')
|
||||
|
||||
if (!data) $this.data('bs.tab', (data = new Tab(this)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
}
|
||||
@@ -185,23 +171,29 @@
|
||||
$.fn.tab.Constructor = Tab
|
||||
|
||||
|
||||
/* TAB DATA-API
|
||||
* ============ */
|
||||
// TAB NO CONFLICT
|
||||
// ===============
|
||||
|
||||
$(function () {
|
||||
$('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
|
||||
e.preventDefault()
|
||||
$(this).tab('show')
|
||||
})
|
||||
$.fn.tab.noConflict = function () {
|
||||
$.fn.tab = old
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
// TAB DATA-API
|
||||
// ============
|
||||
|
||||
$(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
|
||||
e.preventDefault()
|
||||
$(this).tab('show')
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
|
||||
/* ===========================================================
|
||||
* bootstrap-tooltip.js v2.0.4
|
||||
* http://twitter.github.com/bootstrap/javascript.html#tooltips
|
||||
/* ========================================================================
|
||||
* Bootstrap: tooltip.js v3.0.0
|
||||
* http://twbs.github.com/bootstrap/javascript.html#tooltip
|
||||
* Inspired by the original jQuery.tipsy by Jason Frame
|
||||
* ===========================================================
|
||||
* ========================================================================
|
||||
* Copyright 2012 Twitter, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -215,315 +207,371 @@
|
||||
* 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 ($) {
|
||||
+function ($) { "use strict";
|
||||
|
||||
"use strict"; // jshint ;_;
|
||||
|
||||
// Add a special event that is called when an element is destroyed
|
||||
(function($){
|
||||
jQuery.event.special.destroyed = {
|
||||
remove: function(o) {
|
||||
if (o.handler) {
|
||||
o.handler()
|
||||
}
|
||||
}
|
||||
}
|
||||
})(jQuery)
|
||||
|
||||
/* TOOLTIP PUBLIC CLASS DEFINITION
|
||||
* =============================== */
|
||||
// TOOLTIP PUBLIC CLASS DEFINITION
|
||||
// ===============================
|
||||
|
||||
var Tooltip = function (element, options) {
|
||||
this.type =
|
||||
this.options =
|
||||
this.enabled =
|
||||
this.timeout =
|
||||
this.hoverState =
|
||||
this.$element = null
|
||||
|
||||
this.init('tooltip', element, options)
|
||||
}
|
||||
|
||||
Tooltip.prototype = {
|
||||
Tooltip.DEFAULTS = {
|
||||
animation: true
|
||||
, placement: 'top'
|
||||
, selector: false
|
||||
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
|
||||
, trigger: 'hover focus'
|
||||
, title: ''
|
||||
, delay: 0
|
||||
, html: false
|
||||
, container: 'body'
|
||||
}
|
||||
|
||||
constructor: Tooltip
|
||||
Tooltip.prototype.init = function (type, element, options) {
|
||||
this.enabled = true
|
||||
this.type = type
|
||||
this.$element = $(element)
|
||||
this.options = this.getOptions(options)
|
||||
|
||||
, init: function (type, element, options) {
|
||||
var eventIn
|
||||
, eventOut
|
||||
var triggers = this.options.trigger.split(' ')
|
||||
|
||||
this.type = type
|
||||
this.$element = $(element)
|
||||
this.options = this.getOptions(options)
|
||||
this.enabled = true
|
||||
for (var i = triggers.length; i--;) {
|
||||
var trigger = triggers[i]
|
||||
|
||||
if (this.options.trigger != 'manual') {
|
||||
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
|
||||
eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
|
||||
this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
|
||||
this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
|
||||
}
|
||||
if (trigger == 'click') {
|
||||
this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
|
||||
} else if (trigger != 'manual') {
|
||||
var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
|
||||
var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
|
||||
|
||||
// Make sure tooltip is hidden when element is destroyed
|
||||
this.$element.on( 'destroyed', $.proxy( this.hide, this ) );
|
||||
|
||||
this.options.selector ?
|
||||
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
|
||||
this.fixTitle()
|
||||
}
|
||||
|
||||
, getOptions: function (options) {
|
||||
options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
|
||||
|
||||
if (options.delay && typeof options.delay == 'number') {
|
||||
options.delay = {
|
||||
show: options.delay
|
||||
, hide: options.delay
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
, enter: function (e) {
|
||||
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
|
||||
|
||||
if (!self.options.delay || !self.options.delay.show) return self.show()
|
||||
|
||||
clearTimeout(this.timeout)
|
||||
self.hoverState = 'in'
|
||||
this.timeout = setTimeout(function() {
|
||||
if (self.hoverState == 'in') self.show()
|
||||
}, self.options.delay.show)
|
||||
}
|
||||
|
||||
, leave: function (e) {
|
||||
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
|
||||
|
||||
if (this.timeout) clearTimeout(this.timeout)
|
||||
if (!self.options.delay || !self.options.delay.hide) return self.hide()
|
||||
|
||||
self.hoverState = 'out'
|
||||
this.timeout = setTimeout(function() {
|
||||
if (self.hoverState == 'out') self.hide()
|
||||
}, self.options.delay.hide)
|
||||
}
|
||||
|
||||
, show: function () {
|
||||
var $tip
|
||||
, inside
|
||||
, pos
|
||||
, actualWidth
|
||||
, actualHeight
|
||||
, placement
|
||||
, placementPosition
|
||||
, $window
|
||||
, tp
|
||||
|
||||
if (this.hasContent() && this.enabled) {
|
||||
$tip = this.tip()
|
||||
this.setContent()
|
||||
|
||||
if (this.options.animation) {
|
||||
$tip.addClass('fade')
|
||||
}
|
||||
|
||||
placement = typeof this.options.placement == 'function' ?
|
||||
this.options.placement.call(this, $tip[0], this.$element[0]) :
|
||||
this.options.placement
|
||||
|
||||
placementPosition = inside ? placement.split(' ')[1] : placement;
|
||||
|
||||
inside = /in/.test(placement)
|
||||
|
||||
$tip
|
||||
.remove()
|
||||
.css({ top: 0, left: 0, display: 'block' })
|
||||
.appendTo(inside ? this.$element : document.body)
|
||||
|
||||
pos = this.getPosition(inside)
|
||||
|
||||
actualWidth = $tip[0].offsetWidth
|
||||
actualHeight = $tip[0].offsetHeight
|
||||
|
||||
switch (placementPosition) {
|
||||
case 'bottom':
|
||||
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
|
||||
break
|
||||
case 'top':
|
||||
tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
|
||||
break
|
||||
case 'left':
|
||||
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
|
||||
break
|
||||
case 'right':
|
||||
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
|
||||
break
|
||||
}
|
||||
|
||||
// Shift if off screen
|
||||
$window = $(window)
|
||||
|
||||
// If off the top of the screen, flip
|
||||
if ( tp.top < $window.scrollTop() && placementPosition == 'top' ) {
|
||||
tp.top = pos.top + pos.height;
|
||||
placement = inside ? 'inside bottom' : 'bottom';
|
||||
}
|
||||
|
||||
// Shift left or right
|
||||
var leftShift = 0;
|
||||
if ( tp.left < $window.scrollLeft() ) {
|
||||
leftShift = tp.left - $window.scrollLeft();
|
||||
}
|
||||
var t = $window.scrollLeft() + $window.width() - $tip.outerWidth();
|
||||
if ( tp.left > t ) {
|
||||
leftShift = tp.left - t;
|
||||
}
|
||||
|
||||
tp.left -= leftShift;
|
||||
|
||||
// Shift background to center over element (not implemented for east/west)
|
||||
switch (inside ? placement.split(' ')[1] : placement) {
|
||||
case 'bottom':
|
||||
$tip.find('.tooltip-arrow').css( 'left', ( $tip.outerWidth() / 2 ) + leftShift + "px" );
|
||||
break;
|
||||
case 'top':
|
||||
$tip.find('.tooltip-arrow').css( 'left', ( $tip.outerWidth() / 2 ) + leftShift + "px" );
|
||||
break;
|
||||
case 'left':
|
||||
break;
|
||||
case 'right':
|
||||
break;
|
||||
}
|
||||
|
||||
// END GALAXY MODIFICATION
|
||||
|
||||
|
||||
$tip
|
||||
.css(tp)
|
||||
.addClass(placement)
|
||||
.addClass('in')
|
||||
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
|
||||
this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
|
||||
}
|
||||
}
|
||||
|
||||
, isHTML: function(text) {
|
||||
// html string detection logic adapted from jQuery
|
||||
return typeof text != 'string'
|
||||
|| ( text.charAt(0) === "<"
|
||||
&& text.charAt( text.length - 1 ) === ">"
|
||||
&& text.length >= 3
|
||||
) || /^(?:[^<]*<[\w\W]+>[^>]*$)/.exec(text)
|
||||
this.options.selector ?
|
||||
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
|
||||
this.fixTitle()
|
||||
}
|
||||
|
||||
Tooltip.prototype.getDefaults = function () {
|
||||
return Tooltip.DEFAULTS
|
||||
}
|
||||
|
||||
Tooltip.prototype.getOptions = function (options) {
|
||||
options = $.extend({}, this.getDefaults(), this.$element.data(), options)
|
||||
|
||||
if (options.delay && typeof options.delay == 'number') {
|
||||
options.delay = {
|
||||
show: options.delay
|
||||
, hide: options.delay
|
||||
}
|
||||
}
|
||||
|
||||
, setContent: function () {
|
||||
return options
|
||||
}
|
||||
|
||||
Tooltip.prototype.getDelegateOptions = function () {
|
||||
var options = {}
|
||||
var defaults = this.getDefaults()
|
||||
|
||||
this._options && $.each(this._options, function (key, value) {
|
||||
if (defaults[key] != value) options[key] = value
|
||||
})
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
Tooltip.prototype.enter = function (obj) {
|
||||
var self = obj instanceof this.constructor ?
|
||||
obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
|
||||
|
||||
clearTimeout(self.timeout)
|
||||
|
||||
self.hoverState = 'in'
|
||||
|
||||
if (!self.options.delay || !self.options.delay.show) return self.show()
|
||||
|
||||
self.timeout = setTimeout(function () {
|
||||
if (self.hoverState == 'in') self.show()
|
||||
}, self.options.delay.show)
|
||||
}
|
||||
|
||||
Tooltip.prototype.leave = function (obj) {
|
||||
var self = obj instanceof this.constructor ?
|
||||
obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
|
||||
|
||||
clearTimeout(self.timeout)
|
||||
|
||||
self.hoverState = 'out'
|
||||
|
||||
if (!self.options.delay || !self.options.delay.hide) return self.hide()
|
||||
|
||||
self.timeout = setTimeout(function () {
|
||||
if (self.hoverState == 'out') self.hide()
|
||||
}, self.options.delay.hide)
|
||||
}
|
||||
|
||||
Tooltip.prototype.show = function () {
|
||||
var e = $.Event('show.bs.'+ this.type)
|
||||
|
||||
if (this.hasContent() && this.enabled) {
|
||||
this.$element.trigger(e)
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
var $tip = this.tip()
|
||||
, title = this.getTitle()
|
||||
|
||||
$tip.find('.tooltip-inner')[this.isHTML(title) ? 'html' : 'text'](title)
|
||||
$tip.removeClass('fade in top bottom left right')
|
||||
}
|
||||
this.setContent()
|
||||
|
||||
, hide: function () {
|
||||
var that = this
|
||||
, $tip = this.tip()
|
||||
if (this.options.animation) $tip.addClass('fade')
|
||||
|
||||
$tip.removeClass('in')
|
||||
var placement = typeof this.options.placement == 'function' ?
|
||||
this.options.placement.call(this, $tip[0], this.$element[0]) :
|
||||
this.options.placement
|
||||
|
||||
function removeWithAnimation() {
|
||||
var timeout = setTimeout(function () {
|
||||
$tip.off($.support.transition.end).remove()
|
||||
}, 500)
|
||||
var autoToken = /\s?auto?\s?/i
|
||||
var autoPlace = autoToken.test(placement)
|
||||
if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
|
||||
|
||||
$tip.one($.support.transition.end, function () {
|
||||
clearTimeout(timeout)
|
||||
$tip.remove()
|
||||
})
|
||||
$tip
|
||||
.detach()
|
||||
.css({ top: 0, left: 0, display: 'block' })
|
||||
.addClass(placement)
|
||||
|
||||
this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
|
||||
|
||||
var pos = this.getPosition()
|
||||
var actualWidth = $tip[0].offsetWidth
|
||||
var actualHeight = $tip[0].offsetHeight
|
||||
|
||||
if (autoPlace) {
|
||||
var $parent = this.$element.parent()
|
||||
|
||||
var orgPlacement = placement
|
||||
var docScroll = document.documentElement.scrollTop || document.body.scrollTop
|
||||
var parentWidth = this.options.container == 'body' ? window.innerWidth : $parent.outerWidth()
|
||||
var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()
|
||||
var parentLeft = this.options.container == 'body' ? 0 : $parent.offset().left
|
||||
|
||||
placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' :
|
||||
placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' :
|
||||
placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' :
|
||||
placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' :
|
||||
placement
|
||||
|
||||
$tip
|
||||
.removeClass(orgPlacement)
|
||||
.addClass(placement)
|
||||
}
|
||||
|
||||
$.support.transition && this.$tip.hasClass('fade') ?
|
||||
removeWithAnimation() :
|
||||
$tip.remove()
|
||||
var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
|
||||
|
||||
this.applyPlacement(calculatedOffset, placement)
|
||||
this.$element.trigger('shown.bs.' + this.type)
|
||||
}
|
||||
}
|
||||
|
||||
Tooltip.prototype.applyPlacement = function(offset, placement) {
|
||||
var replace
|
||||
var $tip = this.tip()
|
||||
var width = $tip[0].offsetWidth
|
||||
var height = $tip[0].offsetHeight
|
||||
|
||||
// manually read margins because getBoundingClientRect includes difference
|
||||
var marginTop = parseInt($tip.css('margin-top'), 10)
|
||||
var marginLeft = parseInt($tip.css('margin-left'), 10)
|
||||
|
||||
// we must check for NaN for ie 8/9
|
||||
if (isNaN(marginTop)) marginTop = 0
|
||||
if (isNaN(marginLeft)) marginLeft = 0
|
||||
|
||||
offset.top = offset.top + marginTop
|
||||
offset.left = offset.left + marginLeft
|
||||
|
||||
$tip
|
||||
.offset(offset)
|
||||
.addClass('in')
|
||||
|
||||
// check to see if placing tip in new offset caused the tip to resize itself
|
||||
var actualWidth = $tip[0].offsetWidth
|
||||
var actualHeight = $tip[0].offsetHeight
|
||||
|
||||
if (placement == 'top' && actualHeight != height) {
|
||||
replace = true
|
||||
offset.top = offset.top + height - actualHeight
|
||||
}
|
||||
|
||||
, fixTitle: function () {
|
||||
var $e = this.$element
|
||||
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
|
||||
$e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
|
||||
if (/bottom|top/.test(placement)) {
|
||||
var delta = 0
|
||||
|
||||
if (offset.left < 0) {
|
||||
delta = offset.left * -2
|
||||
offset.left = 0
|
||||
|
||||
$tip.offset(offset)
|
||||
|
||||
actualWidth = $tip[0].offsetWidth
|
||||
actualHeight = $tip[0].offsetHeight
|
||||
}
|
||||
|
||||
this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
|
||||
} else {
|
||||
this.replaceArrow(actualHeight - height, actualHeight, 'top')
|
||||
}
|
||||
|
||||
, hasContent: function () {
|
||||
return this.getTitle()
|
||||
if (replace) $tip.offset(offset)
|
||||
}
|
||||
|
||||
Tooltip.prototype.replaceArrow = function(delta, dimension, position) {
|
||||
this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
|
||||
}
|
||||
|
||||
Tooltip.prototype.setContent = function () {
|
||||
var $tip = this.tip()
|
||||
var title = this.getTitle()
|
||||
|
||||
$tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
|
||||
$tip.removeClass('fade in top bottom left right')
|
||||
}
|
||||
|
||||
Tooltip.prototype.hide = function () {
|
||||
var that = this
|
||||
var $tip = this.tip()
|
||||
var e = $.Event('hide.bs.' + this.type)
|
||||
|
||||
function complete() {
|
||||
if (that.hoverState != 'in') $tip.detach()
|
||||
}
|
||||
|
||||
, getPosition: function (inside) {
|
||||
return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
|
||||
width: this.$element[0].offsetWidth
|
||||
, height: this.$element[0].offsetHeight
|
||||
})
|
||||
this.$element.trigger(e)
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
$tip.removeClass('in')
|
||||
|
||||
$.support.transition && this.$tip.hasClass('fade') ?
|
||||
$tip
|
||||
.one($.support.transition.end, complete)
|
||||
.emulateTransitionEnd(150) :
|
||||
complete()
|
||||
|
||||
this.$element.trigger('hidden.bs.' + this.type)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
Tooltip.prototype.fixTitle = function () {
|
||||
var $e = this.$element
|
||||
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
|
||||
$e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
|
||||
}
|
||||
}
|
||||
|
||||
, getTitle: function () {
|
||||
var title
|
||||
, $e = this.$element
|
||||
, o = this.options
|
||||
Tooltip.prototype.hasContent = function () {
|
||||
return this.getTitle()
|
||||
}
|
||||
|
||||
title = $e.attr('data-original-title')
|
||||
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
|
||||
Tooltip.prototype.getPosition = function () {
|
||||
var el = this.$element[0]
|
||||
return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
|
||||
width: el.offsetWidth
|
||||
, height: el.offsetHeight
|
||||
}, this.$element.offset())
|
||||
}
|
||||
|
||||
return title
|
||||
Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
|
||||
return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
|
||||
placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
|
||||
placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
|
||||
/* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
|
||||
}
|
||||
|
||||
Tooltip.prototype.getTitle = function () {
|
||||
var title
|
||||
var $e = this.$element
|
||||
var o = this.options
|
||||
|
||||
title = $e.attr('data-original-title')
|
||||
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
Tooltip.prototype.tip = function () {
|
||||
return this.$tip = this.$tip || $(this.options.template)
|
||||
}
|
||||
|
||||
Tooltip.prototype.arrow = function () {
|
||||
return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
|
||||
}
|
||||
|
||||
Tooltip.prototype.validate = function () {
|
||||
if (!this.$element[0].parentNode) {
|
||||
this.hide()
|
||||
this.$element = null
|
||||
this.options = null
|
||||
}
|
||||
}
|
||||
|
||||
, tip: function () {
|
||||
return this.$tip = this.$tip || $(this.options.template)
|
||||
}
|
||||
Tooltip.prototype.enable = function () {
|
||||
this.enabled = true
|
||||
}
|
||||
|
||||
, validate: function () {
|
||||
if (!this.$element[0].parentNode) {
|
||||
this.hide()
|
||||
this.$element = null
|
||||
this.options = null
|
||||
}
|
||||
}
|
||||
Tooltip.prototype.disable = function () {
|
||||
this.enabled = false
|
||||
}
|
||||
|
||||
, enable: function () {
|
||||
this.enabled = true
|
||||
}
|
||||
Tooltip.prototype.toggleEnabled = function () {
|
||||
this.enabled = !this.enabled
|
||||
}
|
||||
|
||||
, disable: function () {
|
||||
this.enabled = false
|
||||
}
|
||||
|
||||
, toggleEnabled: function () {
|
||||
this.enabled = !this.enabled
|
||||
}
|
||||
|
||||
, toggle: function () {
|
||||
this[this.tip().hasClass('in') ? 'hide' : 'show']()
|
||||
}
|
||||
Tooltip.prototype.toggle = function (e) {
|
||||
var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
|
||||
self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
|
||||
}
|
||||
|
||||
Tooltip.prototype.destroy = function () {
|
||||
this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
|
||||
}
|
||||
|
||||
|
||||
/* TOOLTIP PLUGIN DEFINITION
|
||||
* ========================= */
|
||||
// TOOLTIP PLUGIN DEFINITION
|
||||
// =========================
|
||||
|
||||
$.fn.tooltip = function ( option ) {
|
||||
var old = $.fn.tooltip
|
||||
|
||||
$.fn.tooltip = function (option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
, data = $this.data('tooltip')
|
||||
, options = typeof option == 'object' && option
|
||||
if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.tooltip')
|
||||
var options = typeof option == 'object' && option
|
||||
|
||||
if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
$.fn.tooltip.Constructor = Tooltip
|
||||
|
||||
$.fn.tooltip.defaults = {
|
||||
animation: false
|
||||
, placement: 'top'
|
||||
, selector: false
|
||||
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
|
||||
, trigger: 'hover'
|
||||
, title: ''
|
||||
, delay: 0
|
||||
|
||||
// TOOLTIP NO CONFLICT
|
||||
// ===================
|
||||
|
||||
$.fn.tooltip.noConflict = function () {
|
||||
$.fn.tooltip = old
|
||||
return this
|
||||
}
|
||||
|
||||
}(window.jQuery);
|
||||
|
||||
+28
-24
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright 2012 Igor Vaynberg
|
||||
|
||||
Version: 3.4.2 Timestamp: Mon Aug 12 15:04:12 PDT 2013
|
||||
Version: 3.4.3 Timestamp: Tue Sep 17 06:47:14 PDT 2013
|
||||
|
||||
This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
|
||||
General Public License version 2 (the "GPL License"). You may choose either license to govern your
|
||||
@@ -688,15 +688,19 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
this.opts.element
|
||||
.data("select2", this)
|
||||
.attr("tabindex", "-1")
|
||||
.before(this.container);
|
||||
.before(this.container)
|
||||
.on("click.select2", killEvent); // do not leak click events
|
||||
|
||||
this.container.data("select2", this);
|
||||
|
||||
this.dropdown = this.container.find(".select2-drop");
|
||||
this.dropdown.addClass(evaluate(opts.dropdownCssClass));
|
||||
this.dropdown.data("select2", this);
|
||||
|
||||
syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
|
||||
|
||||
this.dropdown.addClass(evaluate(opts.dropdownCssClass));
|
||||
this.dropdown.data("select2", this);
|
||||
this.dropdown.on("click", killEvent);
|
||||
|
||||
this.results = results = this.container.find(resultsSelector);
|
||||
this.search = search = this.container.find("input.select2-input");
|
||||
|
||||
@@ -707,6 +711,8 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
// initialize the container
|
||||
this.initContainer();
|
||||
|
||||
this.container.on("click", killEvent);
|
||||
|
||||
installFilteredMouseMove(this.results);
|
||||
this.dropdown.on("mousemove-filtered touchstart touchmove touchend", resultsSelector, this.bind(this.highlightUnderEvent));
|
||||
|
||||
@@ -968,10 +974,11 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
opts.initSelection = function (element, callback) {
|
||||
var data = [];
|
||||
$(splitVal(element.val(), opts.separator)).each(function () {
|
||||
var id = this, text = this, tags=opts.tags;
|
||||
var obj = { id: this, text: this },
|
||||
tags = opts.tags;
|
||||
if ($.isFunction(tags)) tags=tags();
|
||||
$(tags).each(function() { if (equal(this.id, id)) { text = this.text; return false; } });
|
||||
data.push({id: id, text: text});
|
||||
$(tags).each(function() { if (equal(this.id, obj.id)) { obj = this; return false; } });
|
||||
data.push(obj);
|
||||
});
|
||||
|
||||
callback(data);
|
||||
@@ -1320,7 +1327,7 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
$("#select2-drop-mask").hide();
|
||||
this.dropdown.removeAttr("id"); // only the active dropdown has the select2-drop id
|
||||
this.dropdown.hide();
|
||||
this.container.removeClass("select2-dropdown-open");
|
||||
this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");
|
||||
this.results.empty();
|
||||
|
||||
|
||||
@@ -1395,7 +1402,7 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
|
||||
// abstract
|
||||
findHighlightableChoices: function() {
|
||||
return this.results.find(".select2-result-selectable:not(.select2-selected):not(.select2-disabled)");
|
||||
return this.results.find(".select2-result-selectable:not(.select2-disabled)");
|
||||
},
|
||||
|
||||
// abstract
|
||||
@@ -1926,10 +1933,7 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
killEvent(e);
|
||||
return;
|
||||
case KEY.TAB:
|
||||
// if selectOnBlur == true, select the currently highlighted option
|
||||
if (this.opts.selectOnBlur) {
|
||||
this.selectHighlighted({noFocus: true});
|
||||
}
|
||||
this.selectHighlighted({noFocus: true});
|
||||
return;
|
||||
case KEY.ESC:
|
||||
this.cancel(e);
|
||||
@@ -2046,6 +2050,11 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
clear: function(triggerChange) {
|
||||
var data=this.selection.data("select2-data");
|
||||
if (data) { // guard against queued quick consecutive clicks
|
||||
var evt = $.Event("select2-clearing");
|
||||
this.opts.element.trigger(evt);
|
||||
if (evt.isDefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
var placeholderOption = this.getPlaceholderOption();
|
||||
this.opts.element.val(placeholderOption ? placeholderOption.val() : "");
|
||||
this.selection.find(".select2-chosen").empty();
|
||||
@@ -2083,7 +2092,7 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
|
||||
isPlaceholderOptionSelected: function() {
|
||||
var placeholderOption;
|
||||
if (!this.opts.placeholder) return false; // no placeholder specified so no option should be considered
|
||||
if (!this.getPlaceholder()) return false; // no placeholder specified so no option should be considered
|
||||
return ((placeholderOption = this.getPlaceholderOption()) !== undefined && placeholderOption.is(':selected'))
|
||||
|| (this.opts.element.val() === "")
|
||||
|| (this.opts.element.val() === undefined)
|
||||
@@ -2216,7 +2225,7 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
this.close();
|
||||
|
||||
if (!options || !options.noFocus)
|
||||
this.selection.focus();
|
||||
this.focusser.focus();
|
||||
|
||||
if (!equal(old, this.id(data))) { this.triggerChange({added:data,removed:oldData}); }
|
||||
},
|
||||
@@ -2442,7 +2451,7 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
this.selection = selection = this.container.find(selector);
|
||||
|
||||
var _this = this;
|
||||
this.selection.on("click", ".select2-search-choice", function (e) {
|
||||
this.selection.on("click", ".select2-search-choice:not(.select2-locked)", function (e) {
|
||||
//killEvent(e);
|
||||
_this.search[0].focus();
|
||||
_this.selectChoice($(this));
|
||||
@@ -2521,10 +2530,7 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
killEvent(e);
|
||||
return;
|
||||
case KEY.TAB:
|
||||
// if selectOnBlur == true, select the currently highlighted option
|
||||
if (this.opts.selectOnBlur) {
|
||||
this.selectHighlighted({noFocus:true});
|
||||
}
|
||||
this.selectHighlighted({noFocus:true});
|
||||
this.close();
|
||||
return;
|
||||
case KEY.ESC:
|
||||
@@ -2842,9 +2848,7 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
return;
|
||||
}
|
||||
|
||||
index = indexOf(this.id(data), val);
|
||||
|
||||
if (index >= 0) {
|
||||
while((index = indexOf(this.id(data), val)) >= 0) {
|
||||
val.splice(index, 1);
|
||||
this.setVal(val);
|
||||
if (this.select) this.postprocessResults();
|
||||
@@ -2925,7 +2929,7 @@ the specific language governing permissions and limitations under the Apache Lic
|
||||
searchWidth = minimumWidth;
|
||||
}
|
||||
|
||||
this.search.width(searchWidth);
|
||||
this.search.width(Math.floor(searchWidth));
|
||||
},
|
||||
|
||||
// multi
|
||||
|
||||
@@ -91,7 +91,7 @@ var LoggableMixin = /** @lends LoggableMixin# */{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
* @class persistant storage adapter.
|
||||
* @class persistent storage adapter.
|
||||
* Provides an easy interface to object based storage using method chaining.
|
||||
* Allows easy change of the storage engine used (h5's local storage?).
|
||||
* @augments StorageRecursionHelper
|
||||
@@ -111,9 +111,9 @@ var LoggableMixin = /** @lends LoggableMixin# */{
|
||||
* });
|
||||
* @constructor
|
||||
*/
|
||||
var PersistantStorage = function( storageKey, storageDefaults ){
|
||||
var PersistentStorage = function( storageKey, storageDefaults ){
|
||||
if( !storageKey ){
|
||||
throw( "PersistantStorage needs storageKey argument" );
|
||||
throw( "PersistentStorage needs storageKey argument" );
|
||||
}
|
||||
storageDefaults = storageDefaults || {};
|
||||
|
||||
@@ -194,7 +194,7 @@ var PersistantStorage = function( storageKey, storageDefaults ){
|
||||
|
||||
// the object returned by this constructor will be a modified StorageRecursionHelper
|
||||
returnedStorage = new StorageRecursionHelper( data );
|
||||
jQuery.extend( returnedStorage, /** @lends PersistantStorage.prototype */{
|
||||
jQuery.extend( returnedStorage, /** @lends PersistentStorage.prototype */{
|
||||
/** The base case for save()'s upward recursion - save everything to storage.
|
||||
* @private
|
||||
* @param {Any} newData data object to save to storage
|
||||
@@ -211,7 +211,7 @@ var PersistantStorage = function( storageKey, storageDefaults ){
|
||||
},
|
||||
/** String representation.
|
||||
*/
|
||||
toString : function(){ return 'PersistantStorage(' + storageKey + ')'; }
|
||||
toString : function(){ return 'PersistentStorage(' + storageKey + ')'; }
|
||||
});
|
||||
|
||||
return returnedStorage;
|
||||
|
||||
+56
-49
@@ -179,17 +179,19 @@ var TabularDatasetChunkedView = Backbone.View.extend({
|
||||
// -- Helper functions. --
|
||||
|
||||
_renderCell: function(cell_contents, index, colspan) {
|
||||
var $cell = $('<td>').text(cell_contents);
|
||||
var column_types = this.model.get_metadata('column_types');
|
||||
if (colspan !== undefined) {
|
||||
return $('<td>').attr('colspan', colspan).addClass('stringalign').text(cell_contents);
|
||||
}
|
||||
else if (column_types[index] === 'str' || column_types === 'list') {
|
||||
/* Left align all str columns, right align the rest */
|
||||
return $('<td>').addClass('stringalign').text(cell_contents);
|
||||
}
|
||||
else {
|
||||
return $('<td>').text(cell_contents);
|
||||
$cell.attr('colspan', colspan).addClass('stringalign');
|
||||
} else if (column_types) {
|
||||
if (index < column_types.length) {
|
||||
if (column_types[index] === 'str' || column_types === 'list') {
|
||||
/* Left align all str columns, right align the rest */
|
||||
$cell.addClass('stringalign');
|
||||
}
|
||||
}
|
||||
}
|
||||
return $cell;
|
||||
},
|
||||
|
||||
_renderRow: function(line) {
|
||||
@@ -416,49 +418,54 @@ var TabularButtonTracksterView = Backbone.View.extend(
|
||||
error: function() { alert( ( "Could not add this dataset to browser" ) + '.' ); },
|
||||
success: function(table_html) {
|
||||
var parent = window.parent;
|
||||
|
||||
parent.show_modal( ( "View Data in a New or Saved Visualization" ), "", {
|
||||
"Cancel": function() {
|
||||
parent.hide_modal();
|
||||
},
|
||||
"View in saved visualization": function() {
|
||||
// Show new modal with saved visualizations.
|
||||
parent.show_modal( ( "Add Data to Saved Visualization" ), table_html, {
|
||||
"Cancel": function() {
|
||||
parent.hide_modal();
|
||||
},
|
||||
"Add to visualization": function() {
|
||||
$(parent.document).find('input[name=id]:checked').each(function() {
|
||||
var vis_id = $(this).val();
|
||||
dataset_params.id = vis_id;
|
||||
parent.Galaxy.modal.show({
|
||||
title : "View Data in a New or Saved Visualization",
|
||||
buttons :{
|
||||
"Cancel": function(){
|
||||
parent.Galaxy.modal.hide();
|
||||
},
|
||||
"View in saved visualization": function(){
|
||||
// Show new modal with saved visualizations.
|
||||
parent.Galaxy.modal.show(
|
||||
{
|
||||
title: "Add Data to Saved Visualization",
|
||||
body: table_html,
|
||||
buttons :{
|
||||
"Cancel": function(){
|
||||
parent.Galaxy.modal.hide();
|
||||
},
|
||||
"Add to visualization": function(){
|
||||
$(parent.document).find('input[name=id]:checked').each(function(){
|
||||
// hide
|
||||
parent.Galaxy.modal.hide();
|
||||
|
||||
var vis_id = $(this).val();
|
||||
dataset_params.id = vis_id;
|
||||
|
||||
// add widget
|
||||
parent.Galaxy.frame_manager.frame_new(
|
||||
{
|
||||
title : "Trackster",
|
||||
type : "url",
|
||||
content : vis_url + "/trackster?" + $.param(dataset_params)
|
||||
});
|
||||
// add widget
|
||||
parent.Galaxy.frame_manager.frame_new({
|
||||
title : "Trackster",
|
||||
type : "url",
|
||||
content : vis_url + "/trackster?" + $.param(dataset_params)
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
"View in new visualization": function(){
|
||||
// hide
|
||||
parent.Galaxy.modal.hide();
|
||||
|
||||
var url = vis_url + "/trackster?" + $.param(dataset_params);
|
||||
|
||||
// hide
|
||||
parent.hide_modal();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
"View in new visualization": function() {
|
||||
var url = vis_url + "/trackster?" + $.param(dataset_params);
|
||||
|
||||
// add widget
|
||||
parent.Galaxy.frame_manager.frame_new(
|
||||
{
|
||||
title : "Trackster",
|
||||
type : "url",
|
||||
content : url
|
||||
});
|
||||
|
||||
// hide
|
||||
parent.hide_modal();
|
||||
// add widget
|
||||
parent.Galaxy.frame_manager.frame_new({
|
||||
title : "Trackster",
|
||||
type : "url",
|
||||
content : url
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user