This commit is contained in:
Bjoern Gruening
2013-10-15 21:03:05 +02:00
341 changed files with 7507 additions and 34984 deletions
@@ -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>
-4
View File
@@ -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
View File
@@ -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 =
+6 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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 ):
+10 -6
View File
@@ -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>' )
+1 -1
View File
@@ -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(","),
+20 -2
View File
@@ -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):
+29 -5
View File
@@ -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),
+18 -1
View File
@@ -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 ):
+18 -2
View File
@@ -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
+63 -10
View File
@@ -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
+2
View File
@@ -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:
+3 -1
View File
@@ -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 ) ) )
+154 -81
View File
@@ -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))
+1 -1
View File
@@ -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
+13 -4
View File
@@ -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 ):
+35 -18
View File
@@ -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 )
+3
View File
@@ -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:
+23 -20
View File
@@ -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={} ):
+8 -9
View File
@@ -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 ):
+9 -8
View File
@@ -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 = [
+84 -94
View File
@@ -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 -9
View File
@@ -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', [] ),
+475 -158
View File
@@ -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
-7
View File
@@ -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
+1 -1
View File
@@ -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 ) }
+2 -2
View File
@@ -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.
+2 -2
View File
@@ -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
+12 -1
View File
@@ -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'
+22 -10
View File
@@ -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 ):
"""
+192 -22
View File
@@ -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:
+6 -2
View File
@@ -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='/' )
+7
View File
@@ -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()
+37 -19
View File
@@ -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&nbsp;&nbsp;A newer installable revision is available for this repository<br/>' % generate_revision_upgrades_img_str()
legend_str += '%s&nbsp;&nbsp;This is the latest installable revision of this repository<br/>' % generate_latest_revision_img_str()
legend_str += '%s&nbsp;&nbsp;This repository is deprecated in the Tool Shed<br/>' % generate_deprecated_repository_img_str()
legend_str += '%s&nbsp;&nbsp;This repository contains exported workflows<br/>' % generate_includes_workflows_img_str()
legend_str += '%s&nbsp;&nbsp;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 -24
View File
@@ -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 ):
"""
+10 -6
View File
@@ -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__ )
+146 -95
View File
@@ -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 ):
+3
View File
@@ -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 ):
+35 -32
View File
@@ -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 \
+63 -44
View File
@@ -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:
+81 -12
View File
@@ -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
+106 -148
View File
@@ -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
+1 -2
View File
@@ -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
+119 -14
View File
@@ -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 ) \
+4 -4
View File
@@ -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 )
+76 -17
View File
@@ -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()
+24
View File
@@ -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 )
+1 -1
View File
@@ -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' )
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
cd `dirname $0`/../..
python ./scripts/migrate_tools/migrate_tools.py 0008_tools.xml $@
+147
View File
@@ -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 (&gt;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>
+2 -2
View File
@@ -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()
+19 -37
View File
@@ -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();
}
}
}
+6 -9
View File
@@ -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>' +
+4 -3
View File
@@ -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;
}
}
+24 -3
View File
@@ -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
},
+97 -54
View File
@@ -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;">&times;</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>' +
+20 -12
View File
@@ -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: "&nbsp;", 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
View File
@@ -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&#8594;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
+437 -389
View File
@@ -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
View File
@@ -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
+5 -5
View File
@@ -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
View File
@@ -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