galaxy-commits
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions

commit/galaxy-central: greg: More enhancements for hte tool shed functional test framework.
by Bitbucket 29 Nov '12
by Bitbucket 29 Nov '12
29 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/44ae555b804b/
changeset: 44ae555b804b
user: greg
date: 2012-11-29 20:39:31
summary: More enhancements for hte tool shed functional test framework.
affected #: 4 files
diff -r 17d6bfafebecd39dd42fd2377e1a5ce1e16e566a -r 44ae555b804bae7aa8170326ff37d5c0dc47be28 test/tool_shed/base/test_db_util.py
--- a/test/tool_shed/base/test_db_util.py
+++ b/test/tool_shed/base/test_db_util.py
@@ -46,3 +46,9 @@
.filter( model.Repository.table.c.user_id==owner.id ) \
.first()
return repository
+def get_repository_metadata_by_repository_id_changeset_revision( repository_id, changeset_revision ):
+ repository_metadata = sa_session.query( model.RepositoryMetadata ) \
+ .filter( and_( model.RepositoryMetadata.table.c.id == repository_id,
+ model.RepositoryMetadata.table.c.changeset_revision == changeset_revision ) ) \
+ .first()
+ return repository_metadata
diff -r 17d6bfafebecd39dd42fd2377e1a5ce1e16e566a -r 44ae555b804bae7aa8170326ff37d5c0dc47be28 test/tool_shed/base/twilltestcase.py
--- a/test/tool_shed/base/twilltestcase.py
+++ b/test/tool_shed/base/twilltestcase.py
@@ -1,13 +1,19 @@
from base.twilltestcase import *
-import ConfigParser
+from galaxy.webapps.community.util.hgweb_config import *
+from test_db_util import *
+
+from galaxy import eggs
+eggs.require('mercurial')
+from mercurial import hg, ui
class ShedTwillTestCase( TwillTestCase ):
def setUp( self ):
# Security helper
self.security = security.SecurityHelper( id_secret='changethisinproductiontoo' )
self.history_id = None
- self.hgweb_config_manager = None
self.hgweb_config_dir = os.environ.get( 'TEST_HG_WEB_CONFIG_DIR' )
+ self.hgweb_config_manager = HgWebConfigManager()
+ self.hgweb_config_manager.hgweb_config_dir = self.hgweb_config_dir
self.host = os.environ.get( 'TOOL_SHED_TEST_HOST' )
self.port = os.environ.get( 'TOOL_SHED_TEST_PORT' )
self.url = "http://%s:%s" % ( self.host, self.port )
@@ -35,7 +41,7 @@
def check_for_valid_tools( self, repository, strings_displayed=[], strings_not_displayed=[] ):
strings_displayed.append( 'Valid tools' )
self.manage_repository( repository, strings_displayed, strings_not_displayed )
- def check_metadata_in_repository_changelog( self, repository, metadata_count ):
+ def check_count_of_metadata_revisions_associated_with_repository( self, repository, metadata_count ):
self.check_repository_changelog( repository )
self.check_string_count_in_page( 'Repository metadata is associated with this change set.', metadata_count )
def check_repository_changelog( self, repository, strings_displayed=[], strings_not_displayed=[] ):
@@ -43,9 +49,13 @@
self.visit_url( url )
self.check_for_strings( strings_displayed, strings_not_displayed )
def check_repository_metadata( self, repository, tip_only=True ):
- assert self.tip_has_metadata( repository, tip_only ), \
- 'Repository tip is not a metadata revision: Repository tip - %s, metadata revisions - %s.' % \
- ( self.get_repository_tip( repository ), ', '.join( self.get_repository_metadata_revisions( repository ) ) )
+ if tip_only:
+ assert self.tip_has_metadata( repository ) and len( self.get_repository_metadata_revisions( repository ) ) == 1, \
+ 'Repository tip is not a metadata revision: Repository tip - %s, metadata revisions - %s.'
+ else:
+ assert len( self.get_repository_metadata_revisions( repository ) ) > 0, \
+ 'Repository tip is not a metadata revision: Repository tip - %s, metadata revisions - %s.' % \
+ ( self.get_repository_tip( repository ), ', '.join( self.get_repository_metadata_revisions( repository ) ) )
def check_repository_tools( self, repository, include_invalid=False ):
'''
Loop through each repository_metadata dict in the repository object.
@@ -73,12 +83,36 @@
if include_invalid and invalid_tool_list:
for invalid_tool_dict in invalid_tool_list:
for tool in invalid_tool_dict[ 'tools' ]:
- tool_path = '%s/%s' % ( self.get_repository_filesystem_path( repository ), tool )
+ tool_path = '%s/%s' % ( self.get_repo_path( repository ), tool )
self.load_display_tool_page( repository,
tool_xml_path=tool_path,
changeset_revision=changeset_revision,
strings_displayed=[ 'properly loaded' ],
strings_not_displayed=[] )
+ def check_repository_tools_for_changeset_revision( self, repository, changeset_revision ):
+ '''
+ Loop through each tool dictionary in the repository metadata associated with the received changeset_revision.
+ For each of these, check for a tools attribute, and load the tool metadata page if it exists, then display that tool's page.
+ '''
+ repository_metadata = get_repository_metadata_by_repository_id_changeset_revision( repository.id, changeset_revision )
+ metadata = repository_metadata.metadata
+ for tool_dict in metadata[ 'tools' ]:
+ metadata_strings_displayed = [ tool[ 'guid' ],
+ tool[ 'version' ],
+ tool[ 'id' ],
+ tool[ 'name' ],
+ tool[ 'description' ],
+ changeset_revision ]
+ url = '/repository/view_tool_metadata?repository_id=%s&changeset_revision=%s&tool_id=%s' % \
+ ( self.security.encode_id( repository.id ), changeset_revision, tool[ 'id' ] )
+ self.visit_url( url )
+ self.check_for_strings( metadata_strings_displayed )
+ self.load_display_tool_page( repository, tool_xml_path=tool[ 'tool_config' ],
+ changeset_revision=changeset_revision,
+ strings_displayed=[ '%s (version %s)' % ( tool[ 'name' ], tool[ 'version' ] ) ],
+ strings_not_displayed=[] )
+ def check_repository_invalid_tools_for_changeset_revision( self, repository, changeset_revision ):
+ pass
def check_string_count_in_page( self, pattern, min_count, max_count=None ):
"""Checks the number of 'pattern' occurrences in the current browser page"""
page = self.last_page()
@@ -202,15 +236,10 @@
def get_repo_path( self, repository ):
# An entry in the hgweb.config file looks something like: repos/test/mira_assembler = database/community_files/000/repo_123
lhs = "repos/%s/%s" % ( repository.user.username, repository.name )
- hgweb_config = "%s/hgweb.config" % self.hgweb_config_dir
- if not os.path.exists( hgweb_config ):
- raise Exception( "Required file hgweb.config does not exist in directory %s" % self.hgweb_config_dir )
- config = ConfigParser.ConfigParser()
- config.read( hgweb_config )
- for option in config.options( "paths" ):
- if option == lhs:
- return config.get( "paths", option )
- raise Exception( "Entry for repository %s missing in %s/hgweb.config file." % ( lhs, self.hgweb_config_dir ) )
+ try:
+ return self.hgweb_config_manager.get_entry( lhs )
+ except:
+ raise Exception( "Entry for repository %s missing in hgweb config file %s." % ( lhs, self.hgweb_config_manager.hgweb_config ) )
def get_repository_file_list( self, base_path, current_path=None ):
'''Recursively load repository folder contents and append them to a list. Similar to os.walk but via /repository/open_folder.'''
if current_path is None:
@@ -247,15 +276,8 @@
def get_repository_metadata_revisions( self, repository ):
return [ str( repository_metadata.changeset_revision ) for repository_metadata in repository.metadata_revisions ]
def get_repository_tip( self, repository ):
- self.browse_repository( repository )
- html = self.last_page()
- revision_regex = re.search( 'revision ([0-9a-f]+) \(repository tip\)', html )
- if revision_regex is not None:
- return revision_regex.group( 1 )
- return None
- def get_repository_filesystem_path( self, repository ):
- repo_subdirectory = '%03d' % int( repository.id / 1000 )
- return os.path.join( 'database', 'community_files', repo_subdirectory, 'repo_%d' % repository.id )
+ repo = hg.repository( ui.ui(), self.get_repo_path( repository ) )
+ return str( repo.changectx( repo.changelog.tip() ) )
def get_tools_from_repository_metadata( self, repository, include_invalid=False ):
'''Get a list of valid and (optionally) invalid tool dicts from the repository metadata.'''
valid_tools = []
@@ -296,12 +318,9 @@
tc.fv( "malicious", "malicious", set_malicious )
tc.submit( "malicious_button" )
self.check_for_strings( strings_displayed, strings_not_displayed )
- def tip_has_metadata( self, repository, tip_only=True ):
+ def tip_has_metadata( self, repository ):
tip = self.get_repository_tip( repository )
- changeset_revisions = [ str( repository_metadata.changeset_revision ) for repository_metadata in repository.metadata_revisions ]
- if tip_only:
- return len( changeset_revisions ) == 1 and tip in changeset_revisions
- return tip in changeset_revisions
+ return get_repository_metadata_by_repository_id_changeset_revision( repository.id, tip )
def upload_file( self,
repository,
filename,
diff -r 17d6bfafebecd39dd42fd2377e1a5ce1e16e566a -r 44ae555b804bae7aa8170326ff37d5c0dc47be28 test/tool_shed/functional/test_0000_basic_repository_features.py
--- a/test/tool_shed/functional/test_0000_basic_repository_features.py
+++ b/test/tool_shed/functional/test_0000_basic_repository_features.py
@@ -66,7 +66,7 @@
repository = get_repository_by_name_and_owner( repository_name, admin_username )
latest_changeset_revision = self.get_repository_tip( repository )
self.check_for_valid_tools( repository, strings_displayed=[ 'Filter1' ] )
- self.check_metadata_in_repository_changelog( repository, metadata_count=1 )
+ self.check_count_of_metadata_revisions_associated_with_repository( repository, metadata_count=1 )
self.check_repository_tools( repository )
self.check_repository_metadata( repository, tip_only=False )
self.browse_repository( repository, strings_displayed=[ 'Browse %s revision' % repository.name, '(repository tip)' ] )
@@ -128,7 +128,7 @@
self.check_for_valid_tools( repository )
strings_displayed = self.get_repository_metadata_revisions( repository ).append( 'Select a revision' )
self.manage_repository( repository, strings_displayed=strings_displayed )
- self.check_metadata_in_repository_changelog( repository, metadata_count=2 )
+ self.check_count_of_metadata_revisions_associated_with_repository( repository, metadata_count=2 )
self.check_repository_tools( repository, include_invalid=False )
self.check_repository_metadata( repository, tip_only=False )
def test_0070_upload_readme_txt_file( self ):
@@ -146,7 +146,7 @@
'''Delete the readme.txt file.'''
repository = get_repository_by_name_and_owner( repository_name, admin_username )
self.delete_files_from_repository( repository, filenames=[ 'readme.txt' ] )
- self.check_metadata_in_repository_changelog( repository, metadata_count=2 )
+ self.check_count_of_metadata_revisions_associated_with_repository( repository, metadata_count=2 )
# Deleting a readme file for a specific revision should make the repository fall back
# to a previous revision's readme file, if one exists.
# TODO: All readme files should be displayed.
diff -r 17d6bfafebecd39dd42fd2377e1a5ce1e16e566a -r 44ae555b804bae7aa8170326ff37d5c0dc47be28 test/tool_shed/functional_tests.py
--- a/test/tool_shed/functional_tests.py
+++ b/test/tool_shed/functional_tests.py
@@ -270,7 +270,7 @@
for dir in [ tool_shed_test_tmp_dir ]:
if os.path.exists( dir ):
log.info( "Cleaning up temporary files in %s" % dir )
- shutil.rmtree( dir )
+ #shutil.rmtree( dir )
except:
pass
if success:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: carlfeberhard: LazyDataLoader.js: now handles NaN/Infinity/-Infinity as null, allows overriding jQuery's datatype converters; controllers/root: added echo_json for debugging/testing
by Bitbucket 29 Nov '12
by Bitbucket 29 Nov '12
29 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/17d6bfafebec/
changeset: 17d6bfafebec
user: carlfeberhard
date: 2012-11-29 19:32:44
summary: LazyDataLoader.js: now handles NaN/Infinity/-Infinity as null, allows overriding jQuery's datatype converters; controllers/root: added echo_json for debugging/testing
affected #: 3 files
diff -r 7fbdc96660062a9194fc16039e6be3b63831ee4e -r 17d6bfafebecd39dd42fd2377e1a5ce1e16e566a lib/galaxy/webapps/galaxy/controllers/root.py
--- a/lib/galaxy/webapps/galaxy/controllers/root.py
+++ b/lib/galaxy/webapps/galaxy/controllers/root.py
@@ -531,6 +531,25 @@
rval += "-> %s" % kwd[k].file.read()
return rval
+ @web.json
+ def echo_json( self, trans, **kwd ):
+ """Echos parameters as JSON (debugging)
+
+ Attempts to parse values passed as boolean, float, then int. Defaults
+ to string. Non-recursive (will not parse lists).
+ """
+ rval = {}
+ for k in kwd:
+ rval[ k ] = kwd[k]
+ try:
+ if rval[ k ] in [ 'true', 'True', 'false', 'False' ]:
+ rval[ k ] = util.string_as_bool( rval[ k ] )
+ rval[ k ] = float( rval[ k ] )
+ rval[ k ] = int( rval[ k ] )
+ except:
+ pass
+ return rval
+
@web.expose
def generate_error( self, trans ):
raise Exception( "Fake error!" )
diff -r 7fbdc96660062a9194fc16039e6be3b63831ee4e -r 17d6bfafebecd39dd42fd2377e1a5ce1e16e566a static/scripts/packed/utils/LazyDataLoader.js
--- a/static/scripts/packed/utils/LazyDataLoader.js
+++ b/static/scripts/packed/utils/LazyDataLoader.js
@@ -1,1 +1,1 @@
-function LazyDataLoader(c){var a=this,d="loaded.new",b="complete";ERROR_EVENT="error";jQuery.extend(a,LoggableMixin);jQuery.extend(a,{total:undefined,url:undefined,currentIntervalId:undefined,data:[],delay:4000,start:0,size:4000,initialize:function(e){jQuery.extend(a,e);if(e.hasOwnProperty("initialize")){e.initialize.call(a,e)}this.log(this+" initialized:",a)},buildUrl:function(f,e){return this.url+"&"+jQuery.param({start_val:f,max_vals:e})},ajaxErrorFn:function(g,e,f){},load:function(h){this.log(this+".load");if(!a.url){throw (a+" requires a url")}if(this.total===null){this.log("\t total is null (will load all)")}else{this.log("\t total:",this.total)}var g=a.size;if((a.total!==null)&&(a.total<a.size)){g=a.total}a.log(a+"\t beginning recursion");f(a.start,g);function f(k,j){a.log(a+".loadHelper, start:",k,"size:",j);var i=a.buildUrl(k,j);a.log("\t url:",i);jQuery.ajax({url:a.buildUrl(k,j),dataType:"json",error:function(n,l,m){a.log("\t ajax error, status:",l,"error:",m);if(a.currentIntervalId){clearInterval(a.currentIntervalId)}$(a).trigger(ERROR_EVENT,[l,m]);a.ajaxErrorFn(n,l,m)},success:function(l){a.log("\t ajax success, response:",l,"next:",m,"remainder:",n);if(l!==null){a.data.push(l);$(a).trigger(d,[l,k,j]);var m=k+j,n=a.size;if(a.total!==null){n=Math.min(a.total-m,a.size)}a.log("\t next recursion, start:",m,"size:",n);if(a.total===null||n>0){a.currentIntervalId=setTimeout(function(){f(m,n)},a.delay);a.log("\t currentIntervalId:",a.currentIntervalId)}else{e()}}else{e()}}})}function e(){a.log(a+".loadHelper, has finished:",a.data);$(a).trigger(b,[a.data,a.total]);if(h){h(a.data)}}},toString:function(){return"LazyDataLoader"}});a.initialize(c);return a};
\ No newline at end of file
+function LazyDataLoader(c){var a=this,d="loaded.new",b="complete";ERROR_EVENT="error";jQuery.extend(a,LoggableMixin);jQuery.extend(a,{total:undefined,url:undefined,currentIntervalId:undefined,data:[],delay:4000,start:0,size:4000,initialize:function(e){jQuery.extend(a,e);if(e.hasOwnProperty("initialize")){e.initialize.call(a,e)}this.log(this+" initialized:",a)},buildUrl:function(f,e){return this.url+"&"+jQuery.param({start_val:f,max_vals:e})},ajaxErrorFn:function(g,e,f){console.error("ERROR fetching data:",f)},converters:{"* text":window.String,"text html":true,"text xml":jQuery.parseXML,"text json":function(e){e=e.replace(/NaN/g,"null");e=e.replace(/-Infinity/g,"null");e=e.replace(/Infinity/g,"null");return jQuery.parseJSON(e)}},load:function(h){this.log(this+".load");if(!a.url){throw (a+" requires a url")}if(this.total===null){this.log("\t total is null (will load all)")}else{this.log("\t total:",this.total)}var g=a.size;if((a.total!==null)&&(a.total<a.size)){g=a.total}a.log(a+"\t beginning recursion");f(a.start,g);function f(k,j){a.log(a+".loadHelper, start:",k,"size:",j);var i=a.buildUrl(k,j);a.log("\t url:",i);jQuery.ajax({url:a.buildUrl(k,j),converters:a.converters,dataType:"json",error:function(n,l,m){a.log("\t ajax error, status:",l,"error:",m);if(a.currentIntervalId){clearInterval(a.currentIntervalId)}$(a).trigger(ERROR_EVENT,[l,m]);a.ajaxErrorFn(n,l,m)},success:function(l){a.log("\t ajax success, response:",l,"next:",m,"remainder:",n);if(l!==null){a.data.push(l);$(a).trigger(d,[l,k,j]);var m=k+j,n=a.size;if(a.total!==null){n=Math.min(a.total-m,a.size)}a.log("\t next recursion, start:",m,"size:",n);if(a.total===null||n>0){a.currentIntervalId=setTimeout(function(){f(m,n)},a.delay);a.log("\t currentIntervalId:",a.currentIntervalId)}else{e()}}else{e()}}})}function e(){a.log(a+".loadHelper, has finished:",a.data);$(a).trigger(b,[a.data,a.total]);if(h){h(a.data)}}},toString:function(){return"LazyDataLoader"}});a.initialize(c);return a};
\ No newline at end of file
diff -r 7fbdc96660062a9194fc16039e6be3b63831ee4e -r 17d6bfafebecd39dd42fd2377e1a5ce1e16e566a static/scripts/utils/LazyDataLoader.js
--- a/static/scripts/utils/LazyDataLoader.js
+++ b/static/scripts/utils/LazyDataLoader.js
@@ -97,7 +97,6 @@
if( config.hasOwnProperty( 'initialize' ) ){
config.initialize.call( loader, config );
}
-
this.log( this + ' initialized:', loader );
},
@@ -115,6 +114,23 @@
//OVERRIDE: to handle ajax errors differently
ajaxErrorFn : function( xhr, status, error ){
+ console.error( 'ERROR fetching data:', error );
+ },
+
+ // converters passed to the jQuery ajax call for data type parsing
+ //OVERRIDE: to provide custom parsing
+ converters : {
+ '* text' : window.String,
+ 'text html' : true,
+ 'text xml' : jQuery.parseXML,
+
+ // add NaN, inf, -inf handling to jquery json parser (by default)
+ 'text json' : function( json ){
+ json = json.replace( /NaN/g, 'null' );
+ json = json.replace( /-Infinity/g, 'null' );
+ json = json.replace( /Infinity/g, 'null' );
+ return jQuery.parseJSON( json );
+ }
},
// interface to begin load (and first recursive call)
@@ -149,7 +165,9 @@
jQuery.ajax({
url : loader.buildUrl( start, size ),
+ converters : loader.converters,
dataType : 'json',
+
error : function( xhr, status, error ){
loader.log( '\t ajax error, status:', status, 'error:', error );
if( loader.currentIntervalId ){
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: inithello: Fixed functional test script creating stray directories in galaxy root.
by Bitbucket 29 Nov '12
by Bitbucket 29 Nov '12
29 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/7fbdc9666006/
changeset: 7fbdc9666006
user: inithello
date: 2012-11-29 19:06:02
summary: Fixed functional test script creating stray directories in galaxy root.
affected #: 1 file
diff -r 607e9db258f20ad47b8bb813508bd6a9056596b8 -r 7fbdc96660062a9194fc16039e6be3b63831ee4e test/tool_shed/functional_tests.py
--- a/test/tool_shed/functional_tests.py
+++ b/test/tool_shed/functional_tests.py
@@ -129,7 +129,7 @@
else:
database_connection = 'sqlite:///' + os.path.join( db_path, 'universe.sqlite' )
kwargs = {}
- for dir in tool_shed_test_tmp_dir:
+ for dir in [ tool_shed_test_tmp_dir ]:
try:
os.makedirs( dir )
except OSError:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

29 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/607e9db258f2/
changeset: 607e9db258f2
user: greg
date: 2012-11-29 18:42:04
summary: Fixes for tool shed functional tests.
affected #: 4 files
diff -r 60786207e48d952a69ccef95fd8fdc1740970923 -r 607e9db258f20ad47b8bb813508bd6a9056596b8 test/tool_shed/base/twilltestcase.py
--- a/test/tool_shed/base/twilltestcase.py
+++ b/test/tool_shed/base/twilltestcase.py
@@ -1,4 +1,5 @@
from base.twilltestcase import *
+import ConfigParser
class ShedTwillTestCase( TwillTestCase ):
def setUp( self ):
@@ -6,6 +7,7 @@
self.security = security.SecurityHelper( id_secret='changethisinproductiontoo' )
self.history_id = None
self.hgweb_config_manager = None
+ self.hgweb_config_dir = os.environ.get( 'TEST_HG_WEB_CONFIG_DIR' )
self.host = os.environ.get( 'TOOL_SHED_TEST_HOST' )
self.port = os.environ.get( 'TOOL_SHED_TEST_PORT' )
self.url = "http://%s:%s" % ( self.host, self.port )
@@ -108,8 +110,8 @@
self.check_for_strings( strings_displayed, strings_not_displayed )
def delete_files_from_repository( self, repository, filenames=[], strings_displayed=[ 'were deleted from the repository' ], strings_not_displayed=[] ):
files_to_delete = []
- basepath = self.get_repository_base_path_from_browse_page( repository )
- repository_files = self.get_repository_file_list( repository, basepath )
+ basepath = self.get_repo_path( repository )
+ repository_files = self.get_repository_file_list( base_path=basepath, current_path=None )
# Verify that the files to delete actually exist in the repository.
for filename in repository_files:
if filename in filenames:
@@ -133,14 +135,15 @@
self.check_for_strings( strings_displayed, strings_not_displayed )
def display_repository_file_contents( self, repository, filename, filepath=None, strings_displayed=[], strings_not_displayed=[] ):
'''Find a file in the repository and display the contents.'''
- basepath = self.get_repository_base_path_from_browse_page( repository )
+ basepath = self.get_repo_path( repository )
repository_file_list = []
- relative_path = filename
- if filepath is not None:
- relative_path = os.path.join( filepath, filename )
- repository_file_list = self.get_repository_file_list( repository, basepath )
- assert relative_path in repository_file_list, 'File %s not found in the repository under %s.' % ( filename, filepath )
- url = '/repository/get_file_contents?file_path=%s' % os.path.join( basepath, relative_path ).replace( '/', '%2f' )
+ if filepath:
+ relative_path = os.path.join( basepath, filepath )
+ else:
+ relative_path = basepath
+ repository_file_list = self.get_repository_file_list( base_path=relative_path, current_path=None )
+ assert filename in repository_file_list, 'File %s not found in the repository under %s.' % ( filename, relative_path )
+ url = '/repository/get_file_contents?file_path=%s' % os.path.join( relative_path, filename )
self.visit_url( url )
self.check_for_strings( strings_displayed, strings_not_displayed )
def edit_repository_categories( self, repository, categories_to_add=[], categories_to_remove=[], restore_original=True ):
@@ -196,27 +199,27 @@
def get_latest_repository_metadata_for_repository( self, repository ):
# TODO: This will not work as expected. Fix it.
return repository.metadata_revisions[ 0 ]
- def get_repository_base_path_from_browse_page( self, repository ):
- '''Get the base path from the javascript dynatree parameters. This must be updated if the browse feature is altered.'''
- self.visit_url( '/repository/browse_repository?id=%s' % self.security.encode_id( repository.id ) )
- html = self.last_page()
- found_file = None
- basepath = None
- search = re.search( 'data: { folder_path: "([^"]+)" },', html )
- if search is not None:
- basepath = search.group( 1 )
- return basepath
- def get_repository_file_list( self, repository, base_path, current_path=None ):
- '''
- Recursively load repository folder contents and append them to a list. Similar to os.walk,
- but via /repository/open_folder.
- '''
+ def get_repo_path( self, repository ):
+ # An entry in the hgweb.config file looks something like: repos/test/mira_assembler = database/community_files/000/repo_123
+ lhs = "repos/%s/%s" % ( repository.user.username, repository.name )
+ hgweb_config = "%s/hgweb.config" % self.hgweb_config_dir
+ if not os.path.exists( hgweb_config ):
+ raise Exception( "Required file hgweb.config does not exist in directory %s" % self.hgweb_config_dir )
+ config = ConfigParser.ConfigParser()
+ config.read( hgweb_config )
+ for option in config.options( "paths" ):
+ if option == lhs:
+ return config.get( "paths", option )
+ raise Exception( "Entry for repository %s missing in %s/hgweb.config file." % ( lhs, self.hgweb_config_dir ) )
+ def get_repository_file_list( self, base_path, current_path=None ):
+ '''Recursively load repository folder contents and append them to a list. Similar to os.walk but via /repository/open_folder.'''
if current_path is None:
- full_path = base_path
+ request_param_path = base_path
else:
- full_path = os.path.join( base_path, current_path )
+ request_param_path = os.path.join( base_path, current_path )
+ #request_param_path = request_param_path.replace( '/', '%2f' )
# Get the current folder's contents.
- url = '/repository/open_folder?folder_path=%s' % full_path.replace( '/', '%2f' )
+ url = '/repository/open_folder?folder_path=%s' % request_param_path
self.visit_url( url )
file_list = from_json_string( self.last_page() )
returned_file_list = []
@@ -228,10 +231,10 @@
# This is a folder. Get the contents of the folder and append it to the list,
# prefixed with the path relative to the repository root, if any.
if current_path is None:
- returned_file_list.extend( self.get_repository_file_list( repository, base_path, file_dict[ 'title' ] ) )
+ returned_file_list.extend( self.get_repository_file_list( base_path=base_path, current_path=file_dict[ 'title' ] ) )
else:
sub_path = os.path.join( current_path, file_dict[ 'title' ] )
- returned_file_list.extend( self.get_repository_file_list( repository, base_path, sub_path ) )
+ returned_file_list.extend( self.get_repository_file_list( base_path=base_path, current_path=sub_path ) )
else:
# This is a regular file, prefix the filename with the current path and append it to the list.
if current_path is not None:
diff -r 60786207e48d952a69ccef95fd8fdc1740970923 -r 607e9db258f20ad47b8bb813508bd6a9056596b8 test/tool_shed/functional/test_0000_basic_repository_features.py
--- a/test/tool_shed/functional/test_0000_basic_repository_features.py
+++ b/test/tool_shed/functional/test_0000_basic_repository_features.py
@@ -15,7 +15,7 @@
repository_description = "Galaxy's filtering tool"
repository_long_description = "Long description of Galaxy's filtering tool"
-class TestCreateRepository( ShedTwillTestCase ):
+class TestBasicRepositoryFeatures( ShedTwillTestCase ):
def test_0000_initiate_users( self ):
"""Create necessary user accounts and login as an admin user."""
@@ -89,9 +89,13 @@
strings_displayed=[ 'has been marked as not deprecated', 'Mark as deprecated' ],
set_deprecated=False )
def test_0045_display_repository_tip_file( self ):
- '''Display the contents of a file in the repository tip revision'''
+ '''Display the contents of filtering.xml in the repository tip revision'''
repository = get_repository_by_name_and_owner( repository_name, admin_username )
- self.display_repository_file_contents( repository, filename='filtering.xml', strings_displayed=[ '1.1.0' ] )
+ self.display_repository_file_contents( repository=repository,
+ filename='filtering.xml',
+ filepath=None,
+ strings_displayed=[ '1.1.0' ],
+ strings_not_displayed=[] )
def test_0050_upload_filtering_txt_file( self ):
'''Upload filtering.txt file associated with tool version 1.1.0.'''
repository = get_repository_by_name_and_owner( repository_name, admin_username )
@@ -105,7 +109,11 @@
'''Upload filtering test data.'''
repository = get_repository_by_name_and_owner( repository_name, admin_username )
self.upload_file( repository, 'filtering_test_data.tar', commit_message="Uploaded filtering test data", remove_repo_files_not_in_tar='No' )
- self.display_repository_file_contents( repository, filename='1.bed', filepath='test-data' )
+ self.display_repository_file_contents( repository=repository,
+ filename='1.bed',
+ filepath='test-data',
+ strings_displayed=[],
+ strings_not_displayed=[] )
self.check_repository_metadata( repository, tip_only=True )
def test_0060_upload_filtering_2_2_0( self ):
'''Upload filtering version 2.2.0'''
diff -r 60786207e48d952a69ccef95fd8fdc1740970923 -r 607e9db258f20ad47b8bb813508bd6a9056596b8 test/tool_shed/functional/test_0010_repository_with_tool_dependencies.py
--- /dev/null
+++ b/test/tool_shed/functional/test_0010_repository_with_tool_dependencies.py
@@ -0,0 +1,19 @@
+from tool_shed.base.twilltestcase import *
+from tool_shed.base.test_db_util import *
+
+admin_user = None
+admin_user_private_role = None
+admin_email = 'test(a)bx.psu.edu'
+admin_username = 'admin-user'
+
+regular_user = None
+regular_user_private_role = None
+regular_email = 'test-1(a)bx.psu.edu'
+regular_username = 'user1'
+
+repository_name = 'freebayes'
+repository_description = "Galaxy's freebayes tool"
+repository_long_description = "Long description of Galaxy's freebayes tool"
+
+class TestRepositoryWithToolDependencies( ShedTwillTestCase ):
+ pass
\ No newline at end of file
diff -r 60786207e48d952a69ccef95fd8fdc1740970923 -r 607e9db258f20ad47b8bb813508bd6a9056596b8 test/tool_shed/functional_tests.py
--- a/test/tool_shed/functional_tests.py
+++ b/test/tool_shed/functional_tests.py
@@ -6,6 +6,10 @@
cwd = os.getcwd()
tool_shed_home_directory = os.path.join( cwd, 'test', 'tool_shed' )
default_tool_shed_test_file_dir = os.path.join( tool_shed_home_directory, 'test_data' )
+# Here's the directory where everything happens. Temporary directories are created within this directory to contain
+# the hgweb.config file, the database, new repositories, etc. Since the tool shed browses repository contents via HTTP,
+# the full path to the temporary directroy wher eht repositories are located cannot contain invalid url characters.
+tool_shed_test_tmp_dir = os.path.join( tool_shed_home_directory, 'tmp' )
new_path = [ os.path.join( cwd, "lib" ) ]
new_path.extend( sys.path[1:] )
sys.path = new_path
@@ -31,7 +35,6 @@
import galaxy.webapps.community.app
from galaxy.webapps.community.app import UniverseApplication
from galaxy.webapps.community import buildapp
-#from galaxy.webapps.community.util.hgweb_config import *
import nose.core
import nose.config
@@ -81,6 +84,8 @@
use_distributed_object_store = os.environ.get( 'TOOL_SHED_USE_DISTRIBUTED_OBJECT_STORE', False )
if start_server:
+ if not os.path.isdir( tool_shed_test_tmp_dir ):
+ os.mkdir( tool_shed_test_tmp_dir )
psu_production = False
tool_shed_test_proxy_port = None
if 'TOOL_SHED_TEST_PSU_PRODUCTION' in os.environ:
@@ -102,7 +107,8 @@
if not nginx_upload_store:
raise Exception( 'Set TOOL_SHED_TEST_NGINX_UPLOAD_STORE to the path where the nginx upload module places uploaded files' )
file_path = tempfile.mkdtemp( dir=base_file_path )
- new_file_path = tempfile.mkdtemp( dir=base_new_file_path )
+ new_repos_path = tempfile.mkdtemp( dir=base_new_file_path )
+ hgweb_config_file_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir )
kwargs = dict( database_engine_option_pool_size = '10',
database_engine_option_max_overflow = '20',
database_engine_option_strategy = 'threadlocal',
@@ -113,16 +119,17 @@
if 'TOOL_SHED_TEST_DBPATH' in os.environ:
db_path = os.environ[ 'TOOL_SHED_TEST_DBPATH' ]
else:
- tempdir = tempfile.mkdtemp()
+ tempdir = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir )
db_path = os.path.join( tempdir, 'database' )
file_path = os.path.join( db_path, 'files' )
- new_file_path = os.path.join( db_path, 'tmp' )
+ hgweb_config_file_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir )
+ new_repos_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir )
if 'TOOL_SHED_TEST_DBURI' in os.environ:
database_connection = os.environ[ 'TOOL_SHED_TEST_DBURI' ]
else:
database_connection = 'sqlite:///' + os.path.join( db_path, 'universe.sqlite' )
kwargs = {}
- for dir in file_path, new_file_path:
+ for dir in tool_shed_test_tmp_dir:
try:
os.makedirs( dir )
except OSError:
@@ -130,6 +137,11 @@
print "Database connection:", database_connection
+ hgweb_config_dir = hgweb_config_file_path
+ os.environ[ 'TEST_HG_WEB_CONFIG_DIR' ] = hgweb_config_dir
+
+ print "Directory location for hgweb.config:", hgweb_config_dir
+
# ---- Build Application --------------------------------------------------
app = None
if start_server:
@@ -150,7 +162,7 @@
database_connection = database_connection,
database_engine_option_pool_size = '10',
file_path = file_path,
- new_file_path = new_file_path,
+ new_file_path = new_repos_path,
tool_path=tool_path,
datatype_converters_config_file = 'datatype_converters_conf.xml.sample',
tool_parse_help = False,
@@ -163,9 +175,8 @@
admin_users = 'test(a)bx.psu.edu',
global_conf = global_conf,
running_functional_tests = True,
- hgweb_config_dir = new_file_path,
+ hgweb_config_dir = hgweb_config_dir,
**kwargs )
-# app.hgweb_config_manager = HgWebConfigManager()
log.info( "Embedded Universe application started" )
@@ -254,20 +265,14 @@
app.shutdown()
app = None
log.info( "Embedded Universe application stopped" )
- try:
- if os.path.exists( tempdir ) and 'TOOL_SHED_TEST_NO_CLEANUP' not in os.environ:
- log.info( "Cleaning up temporary files in %s" % tempdir )
- shutil.rmtree( tempdir )
- except:
- pass
- if psu_production and 'TOOL_SHED_TEST_NO_CLEANUP' not in os.environ:
- for dir in ( file_path, new_file_path ):
- try:
+ if 'TOOL_SHED_TEST_NO_CLEANUP' not in os.environ:
+ try:
+ for dir in [ tool_shed_test_tmp_dir ]:
if os.path.exists( dir ):
- log.info( 'Cleaning up temporary files in %s' % dir )
+ log.info( "Cleaning up temporary files in %s" % dir )
shutil.rmtree( dir )
- except:
- pass
+ except:
+ pass
if success:
return 0
else:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: inithello: Added more functional tests for basic tool shed repository features.
by Bitbucket 28 Nov '12
by Bitbucket 28 Nov '12
28 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/e291341d0127/
changeset: e291341d0127
user: inithello
date: 2012-11-28 21:21:23
summary: Added more functional tests for basic tool shed repository features.
affected #: 9 files
diff -r 4eaa644dd47877c40ee7090d56379998d7aecafa -r e291341d0127df0fd9bf8f66ac1d9df3f58b9bd7 lib/galaxy/webapps/community/controllers/upload.py
--- a/lib/galaxy/webapps/community/controllers/upload.py
+++ b/lib/galaxy/webapps/community/controllers/upload.py
@@ -77,7 +77,7 @@
elif file_data not in ( '', None ):
uploaded_file = file_data.file
uploaded_file_name = uploaded_file.name
- uploaded_file_filename = file_data.filename
+ uploaded_file_filename = os.path.split( file_data.filename )[ -1 ]
isempty = os.path.getsize( os.path.abspath( uploaded_file_name ) ) == 0
if uploaded_file or uploaded_directory:
ok = True
diff -r 4eaa644dd47877c40ee7090d56379998d7aecafa -r e291341d0127df0fd9bf8f66ac1d9df3f58b9bd7 test/tool_shed/base/test_db_util.py
--- a/test/tool_shed/base/test_db_util.py
+++ b/test/tool_shed/base/test_db_util.py
@@ -39,7 +39,7 @@
sa_session.flush()
def refresh( obj ):
sa_session.refresh( obj )
-def get_repository_by_name( name, owner_username ):
+def get_repository_by_name_and_owner( name, owner_username ):
owner = get_user_by_name( owner_username )
repository = sa_session.query( model.Repository ) \
.filter( model.Repository.table.c.name==name ) \
diff -r 4eaa644dd47877c40ee7090d56379998d7aecafa -r e291341d0127df0fd9bf8f66ac1d9df3f58b9bd7 test/tool_shed/base/twilltestcase.py
--- a/test/tool_shed/base/twilltestcase.py
+++ b/test/tool_shed/base/twilltestcase.py
@@ -5,6 +5,7 @@
# Security helper
self.security = security.SecurityHelper( id_secret='changethisinproductiontoo' )
self.history_id = None
+ self.hgweb_config_manager = None
self.host = os.environ.get( 'TOOL_SHED_TEST_HOST' )
self.port = os.environ.get( 'TOOL_SHED_TEST_PORT' )
self.url = "http://%s:%s" % ( self.host, self.port )
@@ -29,18 +30,67 @@
if strings_not_displayed:
for string in strings_not_displayed:
self.check_string_not_in_page( string )
- def check_for_tool_metadata(self, repository, changeset_revision, tool_id, strings_displayed=[], strings_not_displayed=[] ):
- url = '/repository/view_tool_metadata?repository_id=%s&changeset_revision=%s&tool_id=%s' % \
- ( self.security.encode_id( repository.id ), changeset_revision, tool_id )
- self.visit_url( url )
- self.check_for_strings( strings_displayed, strings_not_displayed )
- def check_for_valid_tools( self, repository ):
- self.manage_repository( repository )
- self.check_page_for_string( 'Valid tools' )
+ def check_for_valid_tools( self, repository, strings_displayed=[], strings_not_displayed=[] ):
+ strings_displayed.append( 'Valid tools' )
+ self.manage_repository( repository, strings_displayed, strings_not_displayed )
+ def check_metadata_in_repository_changelog( self, repository, metadata_count ):
+ self.check_repository_changelog( repository )
+ self.check_string_count_in_page( 'Repository metadata is associated with this change set.', metadata_count )
def check_repository_changelog( self, repository, strings_displayed=[], strings_not_displayed=[] ):
url = '/repository/view_changelog?id=%s' % self.security.encode_id( repository.id )
self.visit_url( url )
self.check_for_strings( strings_displayed, strings_not_displayed )
+ def check_repository_metadata( self, repository, tip_only=True ):
+ assert self.tip_has_metadata( repository, tip_only ), \
+ 'Repository tip is not a metadata revision: Repository tip - %s, metadata revisions - %s.' % \
+ ( self.get_repository_tip( repository ), ', '.join( self.get_repository_metadata_revisions( repository ) ) )
+ def check_repository_tools( self, repository, include_invalid=False ):
+ '''
+ Loop through each repository_metadata dict in the repository object.
+ For each of these, check for a tools attribute, and load the tool metadata
+ page if it exists, then display that tool's page.
+ '''
+ tool_list, invalid_tool_list = self.get_tools_from_repository_metadata( repository, include_invalid=include_invalid )
+ for valid_tool_dict in tool_list:
+ changeset_revision = valid_tool_dict[ 'changeset_revision' ]
+ for tool in valid_tool_dict[ 'tools' ]:
+ metadata_strings_displayed = [ tool[ 'guid' ],
+ tool[ 'version' ],
+ tool[ 'id' ],
+ tool[ 'name' ],
+ tool[ 'description' ],
+ changeset_revision ]
+ url = '/repository/view_tool_metadata?repository_id=%s&changeset_revision=%s&tool_id=%s' % \
+ ( self.security.encode_id( repository.id ), changeset_revision, tool[ 'id' ] )
+ self.visit_url( url )
+ self.check_for_strings( metadata_strings_displayed )
+ self.load_display_tool_page( repository, tool_xml_path=tool[ 'tool_config' ],
+ changeset_revision=changeset_revision,
+ strings_displayed=[ '%s (version %s)' % ( tool[ 'name' ], tool[ 'version' ] ) ],
+ strings_not_displayed=[] )
+ if include_invalid and invalid_tool_list:
+ for invalid_tool_dict in invalid_tool_list:
+ for tool in invalid_tool_dict[ 'tools' ]:
+ tool_path = '%s/%s' % ( self.get_repository_filesystem_path( repository ), tool )
+ self.load_display_tool_page( repository,
+ tool_xml_path=tool_path,
+ changeset_revision=changeset_revision,
+ strings_displayed=[ 'properly loaded' ],
+ strings_not_displayed=[] )
+ def check_string_count_in_page( self, pattern, min_count, max_count=None ):
+ """Checks the number of 'pattern' occurrences in the current browser page"""
+ page = self.last_page()
+ pattern_count = page.count( pattern )
+ if max_count is None:
+ max_count = min_count
+ # The number of occurrences of pattern in the page should be between min_count
+ # and max_count, so show error if pattern_count is less than min_count or greater
+ # than max_count.
+ if pattern_count < min_count or pattern_count > max_count:
+ fname = self.write_temp_file( page )
+ errmsg = "%i occurrences of '%s' found (min. %i, max. %i).\npage content written to '%s' " % \
+ ( pattern_count, pattern, min_count, max_count, fname )
+ raise AssertionError( errmsg )
def create_category( self, category_name, category_description ):
self.visit_url( '/admin/manage_categories?operation=create' )
tc.fv( "1", "name", category_name )
@@ -56,10 +106,43 @@
tc.fv( "1", "category_id", "+%s" % category )
tc.submit( "create_repository_button" )
self.check_for_strings( strings_displayed, strings_not_displayed )
+ def delete_files_from_repository( self, repository, filenames=[], strings_displayed=[ 'were deleted from the repository' ], strings_not_displayed=[] ):
+ files_to_delete = []
+ basepath = self.get_repository_base_path_from_browse_page( repository )
+ repository_files = self.get_repository_file_list( repository, basepath )
+ # Verify that the files to delete actually exist in the repository.
+ for filename in repository_files:
+ if filename in filenames:
+ files_to_delete.append( os.path.join( basepath, filename ) )
+ self.browse_repository( repository )
+ # Twill sets hidden form fields to read-only by default. We need to write to this field.
+ form = tc.browser.get_form( 'select_files_to_delete' )
+ form.find_control( "selected_files_to_delete" ).readonly = False
+ tc.fv( "1", "selected_files_to_delete", ','.join( files_to_delete ) )
+ tc.submit( 'select_files_to_delete_button' )
+ self.check_for_strings( strings_displayed, strings_not_displayed )
+ def display_readme_file( self, repository, changeset_revision=None, strings_displayed=[], strings_not_displayed=[] ):
+ if changeset_revision is None:
+ changeset_revision = self.get_repository_tip( repository )
+ repository_id = self.security.encode_id( repository.id )
+ self.visit_url( '/repository/view_readme?changeset_revision=%s&id=%s' % ( changeset_revision, repository_id ) )
+ self.check_for_strings( strings_displayed, strings_not_displayed )
def display_repository_clone_page( self, owner_name, repository_name, strings_displayed=[], strings_not_displayed=[] ):
url = '/repos/%s/%s' % ( owner_name, repository_name )
self.visit_url( url )
self.check_for_strings( strings_displayed, strings_not_displayed )
+ def display_repository_file_contents( self, repository, filename, filepath=None, strings_displayed=[], strings_not_displayed=[] ):
+ '''Find a file in the repository and display the contents.'''
+ basepath = self.get_repository_base_path_from_browse_page( repository )
+ repository_file_list = []
+ relative_path = filename
+ if filepath is not None:
+ relative_path = os.path.join( filepath, filename )
+ repository_file_list = self.get_repository_file_list( repository, basepath )
+ assert relative_path in repository_file_list, 'File %s not found in the repository under %s.' % ( filename, filepath )
+ url = '/repository/get_file_contents?file_path=%s' % os.path.join( basepath, relative_path ).replace( '/', '%2f' )
+ self.visit_url( url )
+ self.check_for_strings( strings_displayed, strings_not_displayed )
def edit_repository_categories( self, repository, categories_to_add=[], categories_to_remove=[], restore_original=True ):
url = '/repository/manage_repository?id=%s' % self.security.encode_id( repository.id )
self.visit_url( url )
@@ -67,10 +150,10 @@
strings_not_displayed = []
for category in categories_to_add:
tc.fv( "2", "category_id", '+%s' % category)
- strings_displayed.append( "selected>%s</option>" % category )
+ strings_displayed.append( "selected>%s" % category )
for category in categories_to_remove:
tc.fv( "2", "category_id", '-%s' % category)
- strings_not_displayed.append( "selected>%s</option>" % category )
+ strings_not_displayed.append( "selected>%s" % category )
tc.submit( "manage_categories_button" )
self.check_for_strings( strings_displayed, strings_not_displayed )
if restore_original:
@@ -78,22 +161,22 @@
strings_not_displayed = []
for category in categories_to_remove:
tc.fv( "2", "category_id", '+%s' % category)
- strings_displayed.append( "selected>%s</option>" % category )
+ strings_displayed.append( "selected>%s" % category )
for category in categories_to_add:
tc.fv( "2", "category_id", '-%s' % category)
- strings_not_displayed.append( "selected>%s</option>" % category )
+ strings_not_displayed.append( "selected>%s" % category )
tc.submit( "manage_categories_button" )
self.check_for_strings( strings_displayed, strings_not_displayed )
- def edit_repository_information( self, repository, **kwargs ):
+ def edit_repository_information( self, repository, **kwd ):
url = '/repository/manage_repository?id=%s' % self.security.encode_id( repository.id )
self.visit_url( url )
original_information = dict( repo_name=repository.name, description=repository.description, long_description=repository.long_description )
strings_displayed = []
strings_not_displayed = []
for input_elem_name in [ 'repo_name', 'description', 'long_description' ]:
- if input_elem_name in kwargs:
- tc.fv( "1", input_elem_name, kwargs[ input_elem_name ] )
- strings_displayed.append( self.escape_html( kwargs[ input_elem_name ] ) )
+ if input_elem_name in kwd:
+ tc.fv( "1", input_elem_name, kwd[ input_elem_name ] )
+ strings_displayed.append( self.escape_html( kwd[ input_elem_name ] ) )
tc.submit( "edit_repository_button" )
self.check_for_strings( strings_displayed )
strings_displayed = []
@@ -102,19 +185,84 @@
strings_displayed.append( self.escape_html( original_information[ input_elem_name ] ) )
tc.submit( "edit_repository_button" )
self.check_for_strings( strings_displayed )
- def escape_html( self, string ):
+ def escape_html( self, string, unescape=False ):
html_entities = [ ('&', 'X' ), ( "'", ''' ), ( '"', '"' ) ]
for character, replacement in html_entities:
- string = string.replace( character, replacement )
+ if unescape:
+ string = string.replace( replacement, character )
+ else:
+ string = string.replace( character, replacement )
return string
def get_latest_repository_metadata_for_repository( self, repository ):
+ # TODO: This will not work as expected. Fix it.
return repository.metadata_revisions[ 0 ]
- def get_readme( self, repository, strings_displayed=[], strings_not_displayed=[] ):
- repository_metadata = self.get_latest_repository_metadata_for_repository( repository )
- changeset_revision = repository_metadata.changeset_revision
- repository_id = self.security.encode_id( repository.id )
- self.visit_url( '/repository/view_readme?changeset_revision=%s&id=%s' % ( changeset_revision, repository_id ) )
- self.check_for_strings( strings_displayed, strings_not_displayed )
+ def get_repository_base_path_from_browse_page( self, repository ):
+ '''Get the base path from the javascript dynatree parameters. This must be updated if the browse feature is altered.'''
+ self.visit_url( '/repository/browse_repository?id=%s' % self.security.encode_id( repository.id ) )
+ html = self.last_page()
+ found_file = None
+ basepath = None
+ search = re.search( 'data: { folder_path: "([^"]+)" },', html )
+ if search is not None:
+ basepath = search.group( 1 )
+ return basepath
+ def get_repository_file_list( self, repository, base_path, current_path=None ):
+ '''
+ Recursively load repository folder contents and append them to a list. Similar to os.walk,
+ but via /repository/open_folder.
+ '''
+ if current_path is None:
+ full_path = base_path
+ else:
+ full_path = os.path.join( base_path, current_path )
+ # Get the current folder's contents.
+ url = '/repository/open_folder?folder_path=%s' % full_path.replace( '/', '%2f' )
+ self.visit_url( url )
+ file_list = from_json_string( self.last_page() )
+ returned_file_list = []
+ if current_path is not None:
+ returned_file_list.append( current_path )
+ # Loop through the json dict returned by /repository/open_folder.
+ for file_dict in file_list:
+ if file_dict[ 'isFolder' ]:
+ # This is a folder. Get the contents of the folder and append it to the list,
+ # prefixed with the path relative to the repository root, if any.
+ if current_path is None:
+ returned_file_list.extend( self.get_repository_file_list( repository, base_path, file_dict[ 'title' ] ) )
+ else:
+ sub_path = os.path.join( current_path, file_dict[ 'title' ] )
+ returned_file_list.extend( self.get_repository_file_list( repository, base_path, sub_path ) )
+ else:
+ # This is a regular file, prefix the filename with the current path and append it to the list.
+ if current_path is not None:
+ returned_file_list.append( os.path.join( current_path, file_dict[ 'title' ] ) )
+ else:
+ returned_file_list.append( file_dict[ 'title' ] )
+ return returned_file_list
+ def get_repository_metadata( self, repository ):
+ return [ metadata_revision for metadata_revision in repository.metadata_revisions ]
+ def get_repository_metadata_revisions( self, repository ):
+ return [ str( repository_metadata.changeset_revision ) for repository_metadata in repository.metadata_revisions ]
+ def get_repository_tip( self, repository ):
+ self.browse_repository( repository )
+ html = self.last_page()
+ revision_regex = re.search( 'revision ([0-9a-f]+) \(repository tip\)', html )
+ if revision_regex is not None:
+ return revision_regex.group( 1 )
+ return None
+ def get_repository_filesystem_path( self, repository ):
+ repo_subdirectory = '%03d' % int( repository.id / 1000 )
+ return os.path.join( 'database', 'community_files', repo_subdirectory, 'repo_%d' % repository.id )
+ def get_tools_from_repository_metadata( self, repository, include_invalid=False ):
+ '''Get a list of valid and (optionally) invalid tool dicts from the repository metadata.'''
+ valid_tools = []
+ invalid_tools = []
+ for repository_metadata in repository.metadata_revisions:
+ if 'tools' in repository_metadata.metadata:
+ valid_tools.append( dict( tools=repository_metadata.metadata[ 'tools' ], changeset_revision=repository_metadata.changeset_revision ) )
+ if include_invalid and 'invalid_tools' in repository_metadata.metadata:
+ invalid_tools.append( dict( tools=repository_metadata.metadata[ 'invalid_tools' ], changeset_revision=repository_metadata.changeset_revision ) )
+ return valid_tools, invalid_tools
def grant_write_access( self, repository, usernames=[], strings_displayed=[], strings_not_displayed=[] ):
self.manage_repository( repository )
tc.fv( "3", "allow_push", '-Select one' )
@@ -122,10 +270,8 @@
tc.fv( "3", "allow_push", '+%s' % username )
tc.submit( 'user_access_button' )
self.check_for_strings( strings_displayed, strings_not_displayed )
- def load_display_tool_page( self, repository, tool_xml_filename, changeset_revision, strings_displayed=[], strings_not_displayed=[] ):
+ def load_display_tool_page( self, repository, tool_xml_path, changeset_revision, strings_displayed=[], strings_not_displayed=[] ):
repository_id = self.security.encode_id( repository.id )
- repo_subdirectory = '%03d' % int( repository.id / 1000 )
- tool_xml_path = '%2f'.join( [ 'database', 'community_files', repo_subdirectory, 'repo_%d' % repository.id, tool_xml_filename ] )
url = '/repository/display_tool?repository_id=%s&tool_config=%s&changeset_revision=%s' % \
( repository_id, tool_xml_path, changeset_revision )
self.visit_url( url )
@@ -134,20 +280,37 @@
url = '/repository/manage_repository?id=%s' % self.security.encode_id( repository.id )
self.visit_url( url )
self.check_for_strings( strings_displayed, strings_not_displayed )
- def set_repository_malicious( self, repository, strings_displayed=[], strings_not_displayed=[] ):
+ def revoke_write_access( self, repository, username ):
+ url = '/repository/manage_repository?user_access_button=Remove&id=%s&remove_auth=%s' % \
+ ( self.security.encode_id( repository.id ), username )
+ self.visit_url( url )
+ def set_repository_deprecated( self, repository, set_deprecated=True, strings_displayed=[], strings_not_displayed=[] ):
+ url = '/repository/deprecate?id=%s&mark_deprecated=%s' % ( self.security.encode_id( repository.id ), set_deprecated )
+ self.visit_url( url )
+ self.check_for_strings( strings_displayed, strings_not_displayed )
+ def set_repository_malicious( self, repository, set_malicious=True, strings_displayed=[], strings_not_displayed=[] ):
self.manage_repository( repository )
- tc.fv( "malicious", "malicious", True )
+ tc.fv( "malicious", "malicious", set_malicious )
tc.submit( "malicious_button" )
self.check_for_strings( strings_displayed, strings_not_displayed )
- def unset_repository_malicious( self, repository, strings_displayed=[], strings_not_displayed=[] ):
- self.manage_repository( repository )
- tc.fv( "malicious", "malicious", False )
- tc.submit( "malicious_button" )
- self.check_for_strings( strings_displayed, strings_not_displayed )
- def upload( self, repository, filename, strings_displayed=[], strings_not_displayed=[], **kwargs ):
+ def tip_has_metadata( self, repository, tip_only=True ):
+ tip = self.get_repository_tip( repository )
+ changeset_revisions = [ str( repository_metadata.changeset_revision ) for repository_metadata in repository.metadata_revisions ]
+ if tip_only:
+ return len( changeset_revisions ) == 1 and tip in changeset_revisions
+ return tip in changeset_revisions
+ def upload_file( self,
+ repository,
+ filename,
+ valid_tools_only=True,
+ strings_displayed=[],
+ strings_not_displayed=[],
+ **kwd ):
self.visit_url( '/upload/upload?repository_id=%s' % self.security.encode_id( repository.id ) )
- for key in kwargs:
- tc.fv( "1", key, kwargs[ key ] )
+ if valid_tools_only:
+ strings_displayed.append( "has been successfully uploaded to the repository." )
+ for key in kwd:
+ tc.fv( "1", key, kwd[ key ] )
tc.formfile( "1", "file_data", self.get_filename( filename ) )
tc.submit( "upload_button" )
self.check_for_strings( strings_displayed, strings_not_displayed )
diff -r 4eaa644dd47877c40ee7090d56379998d7aecafa -r e291341d0127df0fd9bf8f66ac1d9df3f58b9bd7 test/tool_shed/functional/test_0000_basic_repository_features.py
--- a/test/tool_shed/functional/test_0000_basic_repository_features.py
+++ b/test/tool_shed/functional/test_0000_basic_repository_features.py
@@ -11,14 +11,14 @@
regular_email = 'test-1(a)bx.psu.edu'
regular_username = 'user1'
-repository_name = 'filter'
-repository_description = "Galaxy's filter tool"
-repository_long_description = "Long description of Galaxy's filter tool"
+repository_name = 'filtering'
+repository_description = "Galaxy's filtering tool"
+repository_long_description = "Long description of Galaxy's filtering tool"
class TestCreateRepository( ShedTwillTestCase ):
def test_0000_initiate_users( self ):
- """Create necessary users and login as an admin user."""
+ """Create necessary user accounts and login as an admin user."""
self.login( email=regular_email, username=regular_username )
regular_user = get_user( regular_email )
assert regular_user is not None, 'Problem retrieving user with email %s from the database' % regular_email
@@ -29,60 +29,117 @@
assert admin_user is not None, 'Problem retrieving user with email %s from the database' % admin_email
admin_user_private_role = get_private_role( admin_user )
def test_0005_create_categories( self ):
- """Create a category"""
+ """Create categories"""
self.create_category( 'Text Manipulation', 'Tools for manipulating text' )
self.create_category( 'Text Analysis', 'Tools for analyzing text' )
def test_0010_create_repository( self ):
- """Create a repository"""
- strings_displayed = [ '<div class="toolFormTitle">Repository %s</div>' % "'%s'" % repository_name, \
+ """Create the filtering repository"""
+ strings_displayed = [ 'Repository %s' % "'%s'" % repository_name,
'Repository %s has been created' % "'%s'" % repository_name ]
- self.create_repository( repository_name, repository_description, \
- repository_long_description=repository_long_description, \
- categories=[ 'Text Manipulation' ], \
+ self.create_repository( repository_name,
+ repository_description,
+ repository_long_description=repository_long_description,
+ categories=[ 'Text Manipulation' ],
strings_displayed=strings_displayed )
def test_0015_edit_repository( self ):
"""Edit the repository name, description, and long description"""
- repository = get_repository_by_name( repository_name, admin_username )
- new_name = "renamed_filter"
- new_description = "Edited filter tool"
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
+ new_name = "renamed_filtering"
+ new_description = "Edited filtering tool"
new_long_description = "Edited long description"
self.edit_repository_information( repository, repo_name=new_name, description=new_description, long_description=new_long_description )
def test_0020_change_repository_category( self ):
- """Change the category of a repository"""
- repository = get_repository_by_name( repository_name, admin_username )
+ """Change the categories associated with the filtering repository"""
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
self.edit_repository_categories( repository, categories_to_add=[ "Text Analysis" ], categories_to_remove=[ "Text Manipulation" ] )
-# def test_0025_grant_write_access( self ):
-# '''Grant write access to another user'''
-# repository = get_repository_by_name( repository_name, admin_username )
-# self.grant_write_access( repository, usernames=[ regular_username ] )
- def test_0030_upload_tarball( self ):
+ def test_0025_grant_write_access( self ):
+ '''Grant write access to another user'''
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
+ self.grant_write_access( repository, usernames=[ regular_username ] )
+ self.revoke_write_access( repository, regular_username )
+ def test_0030_upload_filtering_1_1_0( self ):
"""Upload filtering_1.1.0.tar to the repository"""
- repository = get_repository_by_name( repository_name, admin_username )
- self.upload( repository,
- 'filtering_1.1.0.tar',
- strings_displayed=[ "has been successfully uploaded to the repository." ],
- commit_message="Uploaded filtering 1.1.0" )
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
+ self.upload_file( repository, 'filtering_1.1.0.tar', commit_message="Uploaded filtering 1.1.0" )
+ def test_0035_verify_repository( self ):
+ '''Display basic repository pages'''
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
+ latest_changeset_revision = self.get_repository_tip( repository )
+ self.check_for_valid_tools( repository, strings_displayed=[ 'Filter1' ] )
+ self.check_metadata_in_repository_changelog( repository, metadata_count=1 )
+ self.check_repository_tools( repository )
+ self.check_repository_metadata( repository, tip_only=False )
+ self.browse_repository( repository, strings_displayed=[ 'Browse %s revision' % repository.name, '(repository tip)' ] )
+ self.display_repository_clone_page( admin_username,
+ repository_name,
+ strings_displayed=[ 'Uploaded filtering 1.1.0', latest_changeset_revision ] )
+ def test_0040_alter_repository_states( self ):
+ '''Test toggling the malicious and deprecated repository flags.'''
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
+ self.set_repository_malicious( repository, set_malicious=True, strings_displayed=[ 'The repository tip has been defined as malicious.' ] )
+ self.set_repository_malicious( repository,
+ set_malicious=False,
+ strings_displayed=[ 'The repository tip has been defined as <b>not</b> malicious.' ] )
+ self.set_repository_deprecated( repository,
+ strings_displayed=[ 'has been marked as deprecated', 'Mark as not deprecated' ] )
+ self.manage_repository( repository,
+ strings_displayed=[ 'This repository has been marked as deprecated' ],
+ strings_not_displayed=[ 'Upload files', 'Reset all repository metadata' ] )
+ self.set_repository_deprecated( repository,
+ strings_displayed=[ 'has been marked as not deprecated', 'Mark as deprecated' ],
+ set_deprecated=False )
+ def test_0045_display_repository_tip_file( self ):
+ '''Display the contents of a file in the repository tip revision'''
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
+ self.display_repository_file_contents( repository, filename='filtering.xml', strings_displayed=[ '1.1.0' ] )
+ def test_0050_upload_filtering_txt_file( self ):
+ '''Upload filtering.txt file associated with tool version 1.1.0.'''
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
+ self.upload_file( repository,
+ 'filtering.txt',
+ commit_message="Uploaded filtering.txt",
+ uncompress_file='No',
+ remove_repo_files_not_in_tar='No' )
+ self.display_readme_file( repository, strings_displayed=[ 'Readme file for filtering 1.1.0' ] )
+ def test_0055_upload_filtering_test_data( self ):
+ '''Upload filtering test data.'''
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
+ self.upload_file( repository, 'filtering_test_data.tar', commit_message="Uploaded filtering test data", remove_repo_files_not_in_tar='No' )
+ self.display_repository_file_contents( repository, filename='1.bed', filepath='test-data' )
+ self.check_repository_metadata( repository, tip_only=True )
+ def test_0060_upload_filtering_2_2_0( self ):
+ '''Upload filtering version 2.2.0'''
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
+ self.upload_file( repository,
+ 'filtering_2.2.0.tar',
+ commit_message="Uploaded filtering 2.2.0",
+ remove_repo_files_not_in_tar='No' )
+ def test_0065_verify_filtering_repository( self ):
+ '''Verify the new tool versions and repository metadata.'''
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
self.check_for_valid_tools( repository )
- latest_repository_metadata = self.get_latest_repository_metadata_for_repository( repository )
- changeset_revision = latest_repository_metadata.changeset_revision
- self.check_repository_changelog( repository, strings_displayed=[ 'Repository metadata is associated with this change set.' ] )
- self.set_repository_malicious( repository, strings_displayed=[ 'The repository tip has been defined as malicious.' ] )
- self.unset_repository_malicious( repository, strings_displayed=[ 'The repository tip has been defined as <b>not</b> malicious.' ] )
- self.load_display_tool_page( repository, tool_xml_filename='filtering.xml', \
- changeset_revision=changeset_revision, \
- strings_displayed=[ 'Filter (version 1.1.0)', "c1=='chr1'" ], \
- strings_not_displayed=[] )
- tool = latest_repository_metadata.metadata[ 'tools' ][0]
- metadata_strings_displayed = [ tool[ 'guid' ], tool[ 'version' ], tool[ 'id' ], tool[ 'name' ], tool[ 'description' ], changeset_revision ]
- self.check_for_tool_metadata( repository, changeset_revision, 'Filter1', strings_displayed=metadata_strings_displayed )
- def test_0035_repository_browse_page( self ):
- '''Visit the repository browse page'''
- repository = get_repository_by_name( repository_name, admin_username )
- self.browse_repository( repository, strings_displayed=[ 'Browse %s revision' % repository.name, '(repository tip)' ] )
- def test_0040_visit_clone_url_via_hgweb( self ):
- '''Visit the repository clone URL via hgweb'''
- repository = get_repository_by_name( repository_name, admin_username )
- latest_changeset_revision = self.get_latest_repository_metadata_for_repository( repository )
- self.display_repository_clone_page( admin_username, \
- repository_name, \
- strings_displayed=[ 'Uploaded filtering 1.1.0', latest_changeset_revision.changeset_revision ] )
+ strings_displayed = self.get_repository_metadata_revisions( repository ).append( 'Select a revision' )
+ self.manage_repository( repository, strings_displayed=strings_displayed )
+ self.check_metadata_in_repository_changelog( repository, metadata_count=2 )
+ self.check_repository_tools( repository, include_invalid=False )
+ self.check_repository_metadata( repository, tip_only=False )
+ def test_0070_upload_readme_txt_file( self ):
+ '''Upload readme.txt file associated with tool version 2.2.0.'''
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
+ self.upload_file( repository, 'readme.txt', commit_message="Uploaded readme.txt" )
+ self.display_readme_file( repository, strings_displayed=[ 'This is a readme file.' ] )
+ # Verify that there is a different readme file for each metadata revision.
+ metadata_revisions = self.get_repository_metadata_revisions( repository )
+ strings_to_check = [ 'Readme file for filtering 1.1.0', 'This is a readme file.' ]
+ for key, metadata_revision in enumerate( metadata_revisions ):
+ # Metadata revisions are in order newest to oldest at this point.
+ self.display_readme_file( repository, metadata_revision, strings_not_displayed=[ strings_to_check[ key ] ] )
+ def test_0075_delete_readme_txt_file( self ):
+ '''Delete the readme.txt file.'''
+ repository = get_repository_by_name_and_owner( repository_name, admin_username )
+ self.delete_files_from_repository( repository, filenames=[ 'readme.txt' ] )
+ self.check_metadata_in_repository_changelog( repository, metadata_count=2 )
+ # Deleting a readme file for a specific revision should make the repository fall back
+ # to a previous revision's readme file, if one exists.
+ # TODO: All readme files should be displayed.
+ self.display_readme_file( repository, strings_displayed=[ 'Readme file for filtering 1.1.0' ] )
diff -r 4eaa644dd47877c40ee7090d56379998d7aecafa -r e291341d0127df0fd9bf8f66ac1d9df3f58b9bd7 test/tool_shed/functional_tests.py
--- a/test/tool_shed/functional_tests.py
+++ b/test/tool_shed/functional_tests.py
@@ -31,6 +31,7 @@
import galaxy.webapps.community.app
from galaxy.webapps.community.app import UniverseApplication
from galaxy.webapps.community import buildapp
+#from galaxy.webapps.community.util.hgweb_config import *
import nose.core
import nose.config
@@ -164,6 +165,8 @@
running_functional_tests = True,
hgweb_config_dir = new_file_path,
**kwargs )
+# app.hgweb_config_manager = HgWebConfigManager()
+
log.info( "Embedded Universe application started" )
# ---- Run webserver ------------------------------------------------------
diff -r 4eaa644dd47877c40ee7090d56379998d7aecafa -r e291341d0127df0fd9bf8f66ac1d9df3f58b9bd7 test/tool_shed/test_data/filtering.txt
--- /dev/null
+++ b/test/tool_shed/test_data/filtering.txt
@@ -0,0 +1,1 @@
+Readme file for filtering 1.1.0
diff -r 4eaa644dd47877c40ee7090d56379998d7aecafa -r e291341d0127df0fd9bf8f66ac1d9df3f58b9bd7 test/tool_shed/test_data/filtering_2.2.0.tar
Binary file test/tool_shed/test_data/filtering_2.2.0.tar has changed
diff -r 4eaa644dd47877c40ee7090d56379998d7aecafa -r e291341d0127df0fd9bf8f66ac1d9df3f58b9bd7 test/tool_shed/test_data/filtering_test_data.tar
Binary file test/tool_shed/test_data/filtering_test_data.tar has changed
diff -r 4eaa644dd47877c40ee7090d56379998d7aecafa -r e291341d0127df0fd9bf8f66ac1d9df3f58b9bd7 test/tool_shed/test_data/readme.txt
--- /dev/null
+++ b/test/tool_shed/test_data/readme.txt
@@ -0,0 +1,1 @@
+This is a readme file.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: carlfeberhard: scatterplot: add header discovery from first dataset line; fix animation; better hover info; fix column selection; pack scripts
by Bitbucket 28 Nov '12
by Bitbucket 28 Nov '12
28 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/4eaa644dd478/
changeset: 4eaa644dd478
user: carlfeberhard
date: 2012-11-28 16:45:01
summary: scatterplot: add header discovery from first dataset line; fix animation; better hover info; fix column selection; pack scripts
affected #: 15 files
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa lib/galaxy/webapps/galaxy/api/datasets.py
--- a/lib/galaxy/webapps/galaxy/api/datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/datasets.py
@@ -187,7 +187,6 @@
when the dataset is not yet indexed and hence using data would
be slow because indexes need to be created.
"""
-
# Dataset check.
msg = self.check_dataset_state( trans, dataset )
if msg:
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -1322,4 +1322,3 @@
def get_item( self, trans, id ):
return self.get_history( trans, id )
-
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa lib/galaxy/webapps/galaxy/controllers/visualization.py
--- a/lib/galaxy/webapps/galaxy/controllers/visualization.py
+++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py
@@ -793,9 +793,23 @@
hda = self.get_dataset( trans, dataset_id, check_ownership=False, check_accessible=True )
hda_dict = hda.get_api_value()
hda_dict[ 'id' ] = dataset_id
- if( hda_dict[ 'metadata_column_names' ] == None
- and hasattr( hda.datatype, 'column_names' ) ):
- hda_dict[ 'metadata_column_names' ] = hda.datatype.column_names
+
+ if( ( hda_dict[ 'metadata_column_names' ] == None )
+ and ( hasattr( hda.datatype, 'column_names' ) ) ):
+ hda_dict[ 'metadata_column_names' ] = hda.datatype.column_names
+
+ # try to get the first line (assuming it's a header)
+ #TODO: doesn't belong here
+ try:
+ with open( hda.file_name ) as infile:
+ for index, line in enumerate( infile ):
+ if 'comment_lines' not in hda_dict:
+ hda_dict[ 'comment_lines' ] = []
+ hda_dict[ 'comment_lines' ].append( line )
+ if index >= 3:
+ break
+ except Exception, exc:
+ log.error( str( exc ) )
history_id = trans.security.encode_id( hda.history.id )
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/mvc/visualizations/scatterplotControlForm.js
--- a/static/scripts/mvc/visualizations/scatterplotControlForm.js
+++ b/static/scripts/mvc/visualizations/scatterplotControlForm.js
@@ -67,8 +67,9 @@
//logger : console,
className : 'scatterplot-control-form',
- dataLoadDelay : 500,
- dataLoadSize : 3001,
+ //NOTE: should include time needed to render
+ dataLoadDelay : 4000,
+ dataLoadSize : 5000,
loadingIndicatorImage : 'loading_small_white_bg.gif',
fetchMsg : 'Fetching data...',
@@ -107,6 +108,16 @@
}
this.log( '\t dataset:', this.dataset );
+ // attempt to get possible headers from the data's first line
+ if( this.dataset.comment_lines && this.dataset.comment_lines.length ){
+ //TODO:??
+ var firstLine = this.dataset.comment_lines[0],
+ possibleHeaders = firstLine.split( '\t' );
+ if( possibleHeaders.length === this.dataset.metadata_column_types.length ){
+ this.possibleHeaders = possibleHeaders;
+ }
+ }
+
// passed from mako helper
//TODO: integrate to galaxyPaths
//TODO: ?? seems like data loader section would be better
@@ -194,16 +205,24 @@
// controls for which columns are used to plot datapoints (and ids/additional info to attach if desired)
var view = this,
allColumns = [],
- numericColumns = [];
-
+ numericColumns = [],
+ usePossibleHeaders = ( this.possibleHeaders && this.$dataControl )?
+ ( this.$dataControl.find( '#first-line-header-checkbox' ).is( ':checked' ) ):( false );
+
// gather column indeces (from metadata_column_types) and names (from metadata_columnnames)
_.each( this.dataset.metadata_column_types, function( type, index ){
// use a 1 based index in names/values within the form (will be dec. when parsed out)
var oneBasedIndex = index + 1,
- // label with the name if available (fall back on 'column <index>')
+ // default name is 'column <index>'...
name = 'column ' + oneBasedIndex;
+
+ // ...but label with the name if available...
if( view.dataset.metadata_column_names ){
name = view.dataset.metadata_column_names[ index ];
+
+ // ...or, use the first line as headers if the user wants
+ } else if( usePossibleHeaders ){
+ name = view.possibleHeaders[ index ];
}
// cache all columns here
@@ -218,11 +237,17 @@
// render the html
var $dataControl = this.$el.find( '.tab-pane#data-control' );
- $dataControl.append( ScatterplotControlForm.templates.dataControl({
+ $dataControl.html( ScatterplotControlForm.templates.dataControl({
allColumns : allColumns,
- numericColumns : numericColumns
+ numericColumns : numericColumns,
+ possibleHeaders : ( this.possibleHeaders )?( this.possibleHeaders.join( ', ' ) ):( '' ),
+ usePossibleHeaders : usePossibleHeaders
}));
+ if( !this.dataset.metadata_column_names && this.possibleHeaders ){
+ $dataControl.find( '#first-line-header' ).show();
+ }
+
// preset to column selectors if they were passed in the config in the query string
$dataControl.find( '#X-select' ).val( this.chartConfig.xColumn );
$dataControl.find( '#Y-select' ).val( this.chartConfig.yColumn );
@@ -288,6 +313,7 @@
// ------------------------------------------------------------------------- EVENTS
events : {
'change #include-id-checkbox' : 'toggleThirdColumnSelector',
+ 'change #first-line-header-checkbox' : 'rerenderDataControl',
'click #data-control #render-button' : 'renderChart',
'click #chart-control #render-button' : 'changeChartSettings'
},
@@ -296,6 +322,10 @@
// show/hide the id selector on the data settings panel
this.$el.find( 'select[name="ID"]' ).parent().toggle();
},
+
+ rerenderDataControl : function(){
+ this.$dataControl = this._render_dataControl();
+ },
showLoadingIndicator : function( message, callback ){
// display the loading indicator over the tab panels if hidden, update message (if passed)
@@ -577,10 +607,8 @@
settings.yLabel = ( chartSettingsYLabel === 'Y' )?
( colSelections.Y.colName ):( chartSettingsYLabel );
- settings.animDuration = 10;
- if( this.$chartControl.find( '#animDuration.checkbox-input' ).is( ':checked' ) ){
- settings.animDuration = 500;
- }
+ settings.animDuration = ( this.$chartControl.find( '#animate-chart' ).is( ':checked' ) )?
+ ( this.chart.defaults.animDuration ):( 0 );
this.log( '\t chartSettings:', settings );
return settings;
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/packed/mvc/visualizations/scatterplotControlForm.js
--- a/static/scripts/packed/mvc/visualizations/scatterplotControlForm.js
+++ b/static/scripts/packed/mvc/visualizations/scatterplotControlForm.js
@@ -1,1 +1,1 @@
-var ScatterplotControlForm=BaseView.extend(LoggableMixin).extend({className:"scatterplot-control-form",dataLoadDelay:500,dataLoadSize:3001,loadingIndicatorImage:"loading_small_white_bg.gif",fetchMsg:"Fetching data...",renderMsg:"Rendering...",initialize:function(a){this.log(this+".initialize, attributes:",a);this.dataset=null;this.chartConfig=null;this.chart=null;this.loader=null;this.$dataControl=null;this.$chartControl=null;this.$statsDisplay=null;this.$chartDisplay=null;this.dataFetch=null;this.initializeFromAttributes(a);this.initializeChart(a);this.initializeDataLoader(a)},initializeFromAttributes:function(a){if(!a||!a.dataset){throw ("ScatterplotView requires a dataset")}else{this.dataset=a.dataset}if(jQuery.type(this.dataset.metadata_column_types)==="string"){this.dataset.metadata_column_types=this.dataset.metadata_column_types.split(", ")}this.log("\t dataset:",this.dataset);if(!a.apiDatasetsURL){throw ("ScatterplotView requires a apiDatasetsURL")}else{this.dataURL=a.apiDatasetsURL+"/"+this.dataset.id+"?"}this.log("\t dataURL:",this.dataURL)},initializeChart:function(a){this.chartConfig=a.chartConfig||{};this.log("\t initial chartConfig:",this.chartConfig);this.chart=new TwoVarScatterplot(this.chartConfig);this.chartConfig=this.chart.config},initializeDataLoader:function(b){var a=this;this.loader=new LazyDataLoader({url:null,start:b.start||0,total:b.total||this.dataset.metadata_data_lines,delay:this.dataLoadDelay,size:this.dataLoadSize,buildUrl:function(d,c){return this.url+"&"+jQuery.param({start_val:d,max_vals:c})}});$(this.loader).bind("error",function(e,c,d){a.log("ERROR:",c,d);alert("ERROR fetching data:\n"+c+"\n"+d);a.hideLoadingIndicator()})},render:function(){this.log(this+".render");this.$el.append(ScatterplotControlForm.templates.mainLayout({loadingIndicatorImagePath:galaxy_paths.get("image_path")+"/"+this.loadingIndicatorImage,message:""}));this.$dataControl=this._render_dataControl();this.$chartControl=this._render_chartControl();this.$statsDisplay=this.$el.find(".tab-pane#stats-display");this.$chartDisplay=this._render_chartDisplay();if(this.chartConfig.xColumn&&this.chartConfig.yColumn){this.renderChart()}this.$el.find(".tooltip").tooltip();return this},_render_dataControl:function(){var b=this,a=[],d=[];_.each(this.dataset.metadata_column_types,function(h,f){var g=f+1,e="column "+g;if(b.dataset.metadata_column_names){e=b.dataset.metadata_column_names[f]}a.push({index:g,name:e});if(h==="int"||h==="float"){d.push({index:g,name:e})}});var c=this.$el.find(".tab-pane#data-control");c.append(ScatterplotControlForm.templates.dataControl({allColumns:a,numericColumns:d}));c.find("#X-select").val(this.chartConfig.xColumn);c.find("#Y-select").val(this.chartConfig.yColumn);if(this.chartConfig.idColumn!==undefined){c.find("#include-id-checkbox").attr("checked",true).trigger("change");c.find("#ID-select").val(this.chartConfig.idColumn)}return c},_render_chartControl:function(){var a=this,b=this.$el.find(".tab-pane#chart-control"),c={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};b.append(ScatterplotControlForm.templates.chartControl(this.chartConfig));b.find(".numeric-slider-input").each(function(){var f=$(this),e=f.find(".slider-output"),g=f.find(".slider"),h=f.attr("id");function d(){var j=$(this),i=j.slider("value");e.text(i)}g.slider(_.extend(c[h],{value:a.chartConfig[h],change:d,slide:d}))});return b},_render_chartDisplay:function(){var a=this.$el.find(".tab-pane#chart-display");a.append(ScatterplotControlForm.templates.chartDisplay(this.chartConfig));return a},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-control #render-button":"renderChart","click #chart-control #render-button":"changeChartSettings"},toggleThirdColumnSelector:function(){this.$el.find('select[name="ID"]').parent().toggle()},showLoadingIndicator:function(b,c){b=b||"";var a=this.$el.find("div#loading-indicator");messageBox=a.find(".loading-message");if(a.is(":visible")){if(b){messageBox.fadeOut("fast",function(){messageBox.text(b);messageBox.fadeIn("fast",c)})}else{c()}}else{if(b){messageBox.text(b)}a.fadeIn("fast",c)}},hideLoadingIndicator:function(a){this.$el.find("div#loading-indicator").fadeOut("fast",a)},renderChart:function(){this.log(this+".renderChart");this.data=null;this.meta=null;_.extend(this.chartConfig,this.getChartSettings());this.log("\t chartConfig:",this.chartConfig);this.chart.updateConfig(this.chartConfig,false);this.loader.url=this.dataURL+"&"+jQuery.param(this.getDataSettings());this.log("\t loader: total lines:",this.loader.total," url:",this.loader.url);var a=this;$(this.loader).bind("loaded.new",function(c,b){a.log(a+" loaded.new",b);a.postProcessDataFetchResponse(b);a.log("\t postprocessed data:",a.data);a.log("\t postprocessed meta:",a.meta);a.showLoadingIndicator(a.renderMsg,function(){a.chart.render(a.data,a.meta);a.renderStats(a.data,a.meta);a.hideLoadingIndicator()})});$(this.loader).bind("complete",function(b,c){a.log(a+" complete",c);$(a.loader).unbind()});a.showLoadingIndicator(a.fetchMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show");a.loader.load()})},renderStats:function(){this.log(this+".renderStats");this.$statsDisplay.html(ScatterplotControlForm.templates.statsDisplay({stats:[{name:"Count",xval:this.meta[0].count,yval:this.meta[1].count},{name:"Min",xval:this.meta[0].min,yval:this.meta[1].min},{name:"Max",xval:this.meta[0].max,yval:this.meta[1].max},{name:"Sum",xval:this.meta[0].sum,yval:this.meta[1].sum},{name:"Mean",xval:this.meta[0].mean,yval:this.meta[1].mean},{name:"Median",xval:this.meta[0].median,yval:this.meta[1].median}]}))},changeChartSettings:function(){var a=this;newChartSettings=this.getChartSettings();_.extend(this.chartConfig,newChartSettings);this.log("this.chartConfig:",this.chartConfig);this.chart.updateConfig(this.chartConfig,false);if(a.data&&a.meta){a.showLoadingIndicator(a.renderMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show");a.chart.render(a.data,a.meta);a.hideLoadingIndicator()})}else{this.renderChart()}},postProcessDataFetchResponse:function(a){this.postProcessData(a.data);this.postProcessMeta(a.meta)},postProcessData:function(b){var a=this;if(a.data){_.each(b,function(d,c){a.data[c]=a.data[c].concat(d)})}else{a.data=b}},postProcessMeta:function(c){var a=this,b=this.dataset.metadata_column_types;if(a.meta){_.each(c,function(e,d){var i=a.meta[d],g=b[d];i.count+=(e.count)?(e.count):(0);if((g==="int")||(g==="float")){i.min=Math.min(e.min,i.min);i.max=Math.max(e.max,i.max);i.sum=e.sum+i.sum;i.mean=(i.count)?(i.sum/i.count):(null);var f=a.data[d].slice().sort(),h=Math.floor(f.length/2);if(f.length%2===0){i.median=((f[h]+f[(h+1)])/2)}else{i.median=f[h]}}})}else{a.meta=c}},getDataSettings:function(){var b=this.getColumnSelections(),a=[];this.log("\t columnSelections:",b);a=[b.X.colIndex-1,b.Y.colIndex-1];if(this.$dataControl.find("#include-id-checkbox").attr("checked")){a.push(b.ID.colIndex-1)}var c={data_type:"raw_data",columns:"["+a+"]"};this.log("\t data settings (url params):",c);return c},getColumnSelections:function(){var a={};this.$dataControl.find("div.column-select select").each(function(){var b=$(this),c=b.val();a[b.attr("name")]={colIndex:c,colName:b.children('[value="'+c+'"]').text()}});return a},getChartSettings:function(){var c={},d=this.getColumnSelections();c.datapointSize=this.$chartControl.find("#datapointSize.numeric-slider-input").find(".slider").slider("value");c.width=this.$chartControl.find("#width.numeric-slider-input").find(".slider").slider("value");c.height=this.$chartControl.find("#height.numeric-slider-input").find(".slider").slider("value");var b=this.$chartControl.find("input#X-axis-label").val(),a=this.$chartControl.find("input#Y-axis-label").val();c.xLabel=(b==="X")?(d.X.colName):(b);c.yLabel=(a==="Y")?(d.Y.colName):(a);c.animDuration=10;if(this.$chartControl.find("#animDuration.checkbox-input").is(":checked")){c.animDuration=500}this.log("\t chartSettings:",c);return c},toString:function(){return"ScatterplotControlForm("+((this.dataset)?(this.dataset.id):(""))+")"}});ScatterplotControlForm.templates={mainLayout:Handlebars.templates["template-visualization-scatterplotControlForm"],dataControl:Handlebars.templates["template-visualization-dataControl"],chartControl:Handlebars.templates["template-visualization-chartControl"],statsDisplay:Handlebars.templates["template-visualization-statsDisplay"],chartDisplay:Handlebars.templates["template-visualization-chartDisplay"]};
\ No newline at end of file
+var ScatterplotControlForm=BaseView.extend(LoggableMixin).extend({className:"scatterplot-control-form",dataLoadDelay:4000,dataLoadSize:5000,loadingIndicatorImage:"loading_small_white_bg.gif",fetchMsg:"Fetching data...",renderMsg:"Rendering...",initialize:function(a){this.log(this+".initialize, attributes:",a);this.dataset=null;this.chartConfig=null;this.chart=null;this.loader=null;this.$dataControl=null;this.$chartControl=null;this.$statsDisplay=null;this.$chartDisplay=null;this.dataFetch=null;this.initializeFromAttributes(a);this.initializeChart(a);this.initializeDataLoader(a)},initializeFromAttributes:function(a){if(!a||!a.dataset){throw ("ScatterplotView requires a dataset")}else{this.dataset=a.dataset}if(jQuery.type(this.dataset.metadata_column_types)==="string"){this.dataset.metadata_column_types=this.dataset.metadata_column_types.split(", ")}this.log("\t dataset:",this.dataset);if(this.dataset.comment_lines&&this.dataset.comment_lines.length){var b=this.dataset.comment_lines[0],c=b.split("\t");if(c.length===this.dataset.metadata_column_types.length){this.possibleHeaders=c}}if(!a.apiDatasetsURL){throw ("ScatterplotView requires a apiDatasetsURL")}else{this.dataURL=a.apiDatasetsURL+"/"+this.dataset.id+"?"}this.log("\t dataURL:",this.dataURL)},initializeChart:function(a){this.chartConfig=a.chartConfig||{};this.log("\t initial chartConfig:",this.chartConfig);this.chart=new TwoVarScatterplot(this.chartConfig);this.chartConfig=this.chart.config},initializeDataLoader:function(b){var a=this;this.loader=new LazyDataLoader({url:null,start:b.start||0,total:b.total||this.dataset.metadata_data_lines,delay:this.dataLoadDelay,size:this.dataLoadSize,buildUrl:function(d,c){return this.url+"&"+jQuery.param({start_val:d,max_vals:c})}});$(this.loader).bind("error",function(e,c,d){a.log("ERROR:",c,d);alert("ERROR fetching data:\n"+c+"\n"+d);a.hideLoadingIndicator()})},render:function(){this.log(this+".render");this.$el.append(ScatterplotControlForm.templates.mainLayout({loadingIndicatorImagePath:galaxy_paths.get("image_path")+"/"+this.loadingIndicatorImage,message:""}));this.$dataControl=this._render_dataControl();this.$chartControl=this._render_chartControl();this.$statsDisplay=this.$el.find(".tab-pane#stats-display");this.$chartDisplay=this._render_chartDisplay();if(this.chartConfig.xColumn&&this.chartConfig.yColumn){this.renderChart()}this.$el.find(".tooltip").tooltip();return this},_render_dataControl:function(){var b=this,a=[],e=[],c=(this.possibleHeaders&&this.$dataControl)?(this.$dataControl.find("#first-line-header-checkbox").is(":checked")):(false);_.each(this.dataset.metadata_column_types,function(i,g){var h=g+1,f="column "+h;if(b.dataset.metadata_column_names){f=b.dataset.metadata_column_names[g]}else{if(c){f=b.possibleHeaders[g]}}a.push({index:h,name:f});if(i==="int"||i==="float"){e.push({index:h,name:f})}});var d=this.$el.find(".tab-pane#data-control");d.html(ScatterplotControlForm.templates.dataControl({allColumns:a,numericColumns:e,possibleHeaders:(this.possibleHeaders)?(this.possibleHeaders.join(", ")):(""),usePossibleHeaders:c}));if(!this.dataset.metadata_column_names&&this.possibleHeaders){d.find("#first-line-header").show()}d.find("#X-select").val(this.chartConfig.xColumn);d.find("#Y-select").val(this.chartConfig.yColumn);if(this.chartConfig.idColumn!==undefined){d.find("#include-id-checkbox").attr("checked",true).trigger("change");d.find("#ID-select").val(this.chartConfig.idColumn)}return d},_render_chartControl:function(){var a=this,b=this.$el.find(".tab-pane#chart-control"),c={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};b.append(ScatterplotControlForm.templates.chartControl(this.chartConfig));b.find(".numeric-slider-input").each(function(){var f=$(this),e=f.find(".slider-output"),g=f.find(".slider"),h=f.attr("id");function d(){var j=$(this),i=j.slider("value");e.text(i)}g.slider(_.extend(c[h],{value:a.chartConfig[h],change:d,slide:d}))});return b},_render_chartDisplay:function(){var a=this.$el.find(".tab-pane#chart-display");a.append(ScatterplotControlForm.templates.chartDisplay(this.chartConfig));return a},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","change #first-line-header-checkbox":"rerenderDataControl","click #data-control #render-button":"renderChart","click #chart-control #render-button":"changeChartSettings"},toggleThirdColumnSelector:function(){this.$el.find('select[name="ID"]').parent().toggle()},rerenderDataControl:function(){this.$dataControl=this._render_dataControl()},showLoadingIndicator:function(b,c){b=b||"";var a=this.$el.find("div#loading-indicator");messageBox=a.find(".loading-message");if(a.is(":visible")){if(b){messageBox.fadeOut("fast",function(){messageBox.text(b);messageBox.fadeIn("fast",c)})}else{c()}}else{if(b){messageBox.text(b)}a.fadeIn("fast",c)}},hideLoadingIndicator:function(a){this.$el.find("div#loading-indicator").fadeOut("fast",a)},renderChart:function(){this.log(this+".renderChart");this.data=null;this.meta=null;_.extend(this.chartConfig,this.getChartSettings());this.log("\t chartConfig:",this.chartConfig);this.chart.updateConfig(this.chartConfig,false);this.loader.url=this.dataURL+"&"+jQuery.param(this.getDataSettings());this.log("\t loader: total lines:",this.loader.total," url:",this.loader.url);var a=this;$(this.loader).bind("loaded.new",function(c,b){a.log(a+" loaded.new",b);a.postProcessDataFetchResponse(b);a.log("\t postprocessed data:",a.data);a.log("\t postprocessed meta:",a.meta);a.showLoadingIndicator(a.renderMsg,function(){a.chart.render(a.data,a.meta);a.renderStats(a.data,a.meta);a.hideLoadingIndicator()})});$(this.loader).bind("complete",function(b,c){a.log(a+" complete",c);$(a.loader).unbind()});a.showLoadingIndicator(a.fetchMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show");a.loader.load()})},renderStats:function(){this.log(this+".renderStats");this.$statsDisplay.html(ScatterplotControlForm.templates.statsDisplay({stats:[{name:"Count",xval:this.meta[0].count,yval:this.meta[1].count},{name:"Min",xval:this.meta[0].min,yval:this.meta[1].min},{name:"Max",xval:this.meta[0].max,yval:this.meta[1].max},{name:"Sum",xval:this.meta[0].sum,yval:this.meta[1].sum},{name:"Mean",xval:this.meta[0].mean,yval:this.meta[1].mean},{name:"Median",xval:this.meta[0].median,yval:this.meta[1].median}]}))},changeChartSettings:function(){var a=this;newChartSettings=this.getChartSettings();_.extend(this.chartConfig,newChartSettings);this.log("this.chartConfig:",this.chartConfig);this.chart.updateConfig(this.chartConfig,false);if(a.data&&a.meta){a.showLoadingIndicator(a.renderMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show");a.chart.render(a.data,a.meta);a.hideLoadingIndicator()})}else{this.renderChart()}},postProcessDataFetchResponse:function(a){this.postProcessData(a.data);this.postProcessMeta(a.meta)},postProcessData:function(b){var a=this;if(a.data){_.each(b,function(d,c){a.data[c]=a.data[c].concat(d)})}else{a.data=b}},postProcessMeta:function(c){var a=this,b=this.dataset.metadata_column_types;if(a.meta){_.each(c,function(e,d){var i=a.meta[d],g=b[d];i.count+=(e.count)?(e.count):(0);if((g==="int")||(g==="float")){i.min=Math.min(e.min,i.min);i.max=Math.max(e.max,i.max);i.sum=e.sum+i.sum;i.mean=(i.count)?(i.sum/i.count):(null);var f=a.data[d].slice().sort(),h=Math.floor(f.length/2);if(f.length%2===0){i.median=((f[h]+f[(h+1)])/2)}else{i.median=f[h]}}})}else{a.meta=c}},getDataSettings:function(){var b=this.getColumnSelections(),a=[];this.log("\t columnSelections:",b);a=[b.X.colIndex-1,b.Y.colIndex-1];if(this.$dataControl.find("#include-id-checkbox").attr("checked")){a.push(b.ID.colIndex-1)}var c={data_type:"raw_data",columns:"["+a+"]"};this.log("\t data settings (url params):",c);return c},getColumnSelections:function(){var a={};this.$dataControl.find("div.column-select select").each(function(){var b=$(this),c=b.val();a[b.attr("name")]={colIndex:c,colName:b.children('[value="'+c+'"]').text()}});return a},getChartSettings:function(){var c={},d=this.getColumnSelections();c.datapointSize=this.$chartControl.find("#datapointSize.numeric-slider-input").find(".slider").slider("value");c.width=this.$chartControl.find("#width.numeric-slider-input").find(".slider").slider("value");c.height=this.$chartControl.find("#height.numeric-slider-input").find(".slider").slider("value");var b=this.$chartControl.find("input#X-axis-label").val(),a=this.$chartControl.find("input#Y-axis-label").val();c.xLabel=(b==="X")?(d.X.colName):(b);c.yLabel=(a==="Y")?(d.Y.colName):(a);c.animDuration=(this.$chartControl.find("#animate-chart").is(":checked"))?(this.chart.defaults.animDuration):(0);this.log("\t chartSettings:",c);return c},toString:function(){return"ScatterplotControlForm("+((this.dataset)?(this.dataset.id):(""))+")"}});ScatterplotControlForm.templates={mainLayout:Handlebars.templates["template-visualization-scatterplotControlForm"],dataControl:Handlebars.templates["template-visualization-dataControl"],chartControl:Handlebars.templates["template-visualization-chartControl"],statsDisplay:Handlebars.templates["template-visualization-statsDisplay"],chartDisplay:Handlebars.templates["template-visualization-chartDisplay"]};
\ No newline at end of file
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/packed/templates/compiled/template-visualization-chartControl.js
--- a/static/scripts/packed/templates/compiled/template-visualization-chartControl.js
+++ b/static/scripts/packed/templates/compiled/template-visualization-chartControl.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-chartControl"]=b(function(f,m,e,l,k){e=e||f.helpers;var i="",c,h,g="function",j=this.escapeExpression,n=this;function d(p,o){return' checked="true"'}i+='<p class="help-text">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n </p>\n\n <div id="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">';h=e.datapointSize;if(h){c=h.call(m,{hash:{}})}else{c=m.datapointSize;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id="animDuration" class="form-input checkbox-input">\n <label for="animated">Animate graph transitions?: </label>\n <input type="checkbox" id="animated"\n class="checkbox control"';c=m.animDuration;c=e["if"].call(m,c,{hash:{},inverse:n.noop,fn:n.program(1,d,k)});if(c||c===0){i+=c}i+=' />\n <p class="form-help help-text-small">\n Uncheck this to disable the animations used on the graph\n </p>\n </div>\n\n <div id="width" class="form-input numeric-slider-input">\n <label for="width">Graph width: </label>\n <div class="slider-output">';h=e.width;if(h){c=h.call(m,{hash:{}})}else{c=m.width;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id="height" class="form-input numeric-slider-input">\n <label for="height">Graph height: </label>\n <div class="slider-output">';h=e.height;if(h){c=h.call(m,{hash:{}})}else{c=m.height;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id="X-axis-label"class="text-input form-input">\n <label for="X-axis-label">Re-label the X axis: </label>\n <input type="text" name="X-axis-label" id="X-axis-label" value="';h=e.xLabel;if(h){c=h.call(m,{hash:{}})}else{c=m.xLabel;c=typeof c===g?c():c}i+=j(c)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <div id="Y-axis-label" class="text-input form-input">\n <label for="Y-axis-label">Re-label the Y axis: </label>\n <input type="text" name="Y-axis-label" id="Y-axis-label" value="';h=e.yLabel;if(h){c=h.call(m,{hash:{}})}else{c=m.yLabel;c=typeof c===g?c():c}i+=j(c)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <input id="render-button" type="button" value="Draw" />';return i})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-chartControl"]=b(function(f,m,e,l,k){e=e||f.helpers;var i="",c,h,g="function",j=this.escapeExpression,n=this;function d(p,o){return' checked="true"'}i+='<p class="help-text">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n </p>\n\n <div id="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">';h=e.datapointSize;if(h){c=h.call(m,{hash:{}})}else{c=m.datapointSize;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id="animDuration" class="form-input checkbox-input">\n <label for="animate-chart">Animate chart transitions?: </label>\n <input type="checkbox" id="animate-chart"\n class="checkbox control"';c=m.animDuration;c=e["if"].call(m,c,{hash:{},inverse:n.noop,fn:n.program(1,d,k)});if(c||c===0){i+=c}i+=' />\n <p class="form-help help-text-small">\n Uncheck this to disable the animations used on the chart\n </p>\n </div>\n\n <div id="width" class="form-input numeric-slider-input">\n <label for="width">Chart width: </label>\n <div class="slider-output">';h=e.width;if(h){c=h.call(m,{hash:{}})}else{c=m.width;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n </div>\n\n <div id="height" class="form-input numeric-slider-input">\n <label for="height">Chart height: </label>\n <div class="slider-output">';h=e.height;if(h){c=h.call(m,{hash:{}})}else{c=m.height;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n </div>\n\n <div id="X-axis-label"class="text-input form-input">\n <label for="X-axis-label">Re-label the X axis: </label>\n <input type="text" name="X-axis-label" id="X-axis-label" value="';h=e.xLabel;if(h){c=h.call(m,{hash:{}})}else{c=m.xLabel;c=typeof c===g?c():c}i+=j(c)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <div id="Y-axis-label" class="text-input form-input">\n <label for="Y-axis-label">Re-label the Y axis: </label>\n <input type="text" name="Y-axis-label" id="Y-axis-label" value="';h=e.yLabel;if(h){c=h.call(m,{hash:{}})}else{c=m.yLabel;c=typeof c===g?c():c}i+=j(c)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <input id="render-button" type="button" value="Draw" />';return i})})();
\ No newline at end of file
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/packed/templates/compiled/template-visualization-dataControl.js
--- a/static/scripts/packed/templates/compiled/template-visualization-dataControl.js
+++ b/static/scripts/packed/templates/compiled/template-visualization-dataControl.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-dataControl"]=b(function(g,m,f,l,k){f=f||g.helpers;var i="",d,h="function",j=this.escapeExpression,o=this;function e(t,s){var q="",r,p;q+='\n <option value="';p=f.index;if(p){r=p.call(t,{hash:{}})}else{r=t.index;r=typeof r===h?r():r}q+=j(r)+'">';p=f.name;if(p){r=p.call(t,{hash:{}})}else{r=t.name;r=typeof r===h?r():r}q+=j(r)+"</option>\n ";return q}function c(t,s){var q="",r,p;q+='\n <option value="';p=f.index;if(p){r=p.call(t,{hash:{}})}else{r=t.index;r=typeof r===h?r():r}q+=j(r)+'">';p=f.name;if(p){r=p.call(t,{hash:{}})}else{r=t.name;r=typeof r===h?r():r}q+=j(r)+"</option>\n ";return q}function n(t,s){var q="",r,p;q+='\n <option value="';p=f.index;if(p){r=p.call(t,{hash:{}})}else{r=t.index;r=typeof r===h?r():r}q+=j(r)+'">';p=f.name;if(p){r=p.call(t,{hash:{}})}else{r=t.name;r=typeof r===h?r():r}q+=j(r)+"</option>\n ";return q}i+="<p class=\"help-text\">\n Use the following controls to change the data used by the chart.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n </p>\n\n ";i+='\n <div class="column-select">\n <label for="X-select">Data column for X: </label>\n <select name="X" id="X-select">\n ';d=m.numericColumns;d=f.each.call(m,d,{hash:{},inverse:o.noop,fn:o.program(1,e,k)});if(d||d===0){i+=d}i+='\n </select>\n </div>\n <div class="column-select">\n <label for="Y-select">Data column for Y: </label>\n <select name="Y" id="Y-select">\n ';d=m.numericColumns;d=f.each.call(m,d,{hash:{},inverse:o.noop,fn:o.program(3,c,k)});if(d||d===0){i+=d}i+="\n </select>\n </div>\n\n ";i+='\n <div id="include-id">\n <label for="include-id-checkbox">Include a third column as data point IDs?</label>\n <input type="checkbox" name="include-id" id="include-id-checkbox" />\n <p class="help-text-small">\n These will be displayed (along with the x and y values) when you hover over\n a data point.\n </p>\n </div>\n <div class="column-select" style="display: none">\n <label for="ID-select">Data column for IDs: </label>\n <select name="ID" id="ID-select">\n ';d=m.allColumns;d=f.each.call(m,d,{hash:{},inverse:o.noop,fn:o.program(5,n,k)});if(d||d===0){i+=d}i+='\n </select>\n </div>\n\n <input id="render-button" type="button" value="Draw" />\n <div class="clear"></div>';return i})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-dataControl"]=b(function(g,n,f,m,l){f=f||g.helpers;var j="",d,i,h="function",k=this.escapeExpression,q=this;function e(v,u){var s="",t,r;s+='\n <option value="';r=f.index;if(r){t=r.call(v,{hash:{}})}else{t=v.index;t=typeof t===h?t():t}s+=k(t)+'">';r=f.name;if(r){t=r.call(v,{hash:{}})}else{t=v.name;t=typeof t===h?t():t}s+=k(t)+"</option>\n ";return s}function c(v,u){var s="",t,r;s+='\n <option value="';r=f.index;if(r){t=r.call(v,{hash:{}})}else{t=v.index;t=typeof t===h?t():t}s+=k(t)+'">';r=f.name;if(r){t=r.call(v,{hash:{}})}else{t=v.name;t=typeof t===h?t():t}s+=k(t)+"</option>\n ";return s}function p(v,u){var s="",t,r;s+='\n <option value="';r=f.index;if(r){t=r.call(v,{hash:{}})}else{t=v.index;t=typeof t===h?t():t}s+=k(t)+'">';r=f.name;if(r){t=r.call(v,{hash:{}})}else{t=v.name;t=typeof t===h?t():t}s+=k(t)+"</option>\n ";return s}function o(s,r){return'checked="true"'}j+="<p class=\"help-text\">\n Use the following controls to change the data used by the chart.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n </p>\n\n ";j+='\n <div class="column-select">\n <label for="X-select">Data column for X: </label>\n <select name="X" id="X-select">\n ';d=n.numericColumns;d=f.each.call(n,d,{hash:{},inverse:q.noop,fn:q.program(1,e,l)});if(d||d===0){j+=d}j+='\n </select>\n </div>\n <div class="column-select">\n <label for="Y-select">Data column for Y: </label>\n <select name="Y" id="Y-select">\n ';d=n.numericColumns;d=f.each.call(n,d,{hash:{},inverse:q.noop,fn:q.program(3,c,l)});if(d||d===0){j+=d}j+="\n </select>\n </div>\n\n ";j+='\n <div id="include-id">\n <label for="include-id-checkbox">Include a third column as data point IDs?</label>\n <input type="checkbox" name="include-id" id="include-id-checkbox" />\n <p class="help-text-small">\n These will be displayed (along with the x and y values) when you hover over\n a data point.\n </p>\n </div>\n <div class="column-select" style="display: none">\n <label for="ID-select">Data column for IDs: </label>\n <select name="ID" id="ID-select">\n ';d=n.allColumns;d=f.each.call(n,d,{hash:{},inverse:q.noop,fn:q.program(5,p,l)});if(d||d===0){j+=d}j+="\n </select>\n </div>\n\n ";j+='\n <div id="first-line-header" style="display: none;">\n <p>Possible headers: ';i=f.possibleHeaders;if(i){d=i.call(n,{hash:{}})}else{d=n.possibleHeaders;d=typeof d===h?d():d}j+=k(d)+'\n </p>\n <label for="first-line-header-checkbox">Use the above as column headers?</label>\n <input type="checkbox" name="include-id" id="first-line-header-checkbox"\n ';d=n.usePossibleHeaders;d=f["if"].call(n,d,{hash:{},inverse:q.noop,fn:q.program(7,o,l)});if(d||d===0){j+=d}j+='/>\n <p class="help-text-small">\n It looks like Galaxy couldn\'t get proper column headers for this data.\n Would you like to use the column headers above as column names to select columns?\n </p>\n </div>\n\n <input id="render-button" type="button" value="Draw" />\n <div class="clear"></div>';return j})})();
\ No newline at end of file
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/packed/utils/LazyDataLoader.js
--- a/static/scripts/packed/utils/LazyDataLoader.js
+++ b/static/scripts/packed/utils/LazyDataLoader.js
@@ -1,1 +1,1 @@
-function LazyDataLoader(c){var a=this,d="loaded.new",b="complete";ERROR_EVENT="error";jQuery.extend(a,LoggableMixin);jQuery.extend(a,{total:undefined,url:undefined,currentIntervalId:undefined,data:[],delay:500,start:0,size:1000,initialize:function(e){jQuery.extend(a,e);if(e.hasOwnProperty("initialize")){e.initialize.call(a,e)}this.log(this+" initialized:",a)},buildUrl:function(f,e){return this.url+"&"+jQuery.param({start_val:f,max_vals:e})},ajaxErrorFn:function(g,e,f){},load:function(h){this.log(this+".load");if(!a.url){throw (a+" requires a url")}if(this.total===null){this.log("\t total is null (will load all)")}else{this.log("\t total:",this.total)}var g=a.size;if((a.total!==null)&&(a.total<a.size)){g=a.total}a.log(a+"\t beginning recursion");f(a.start,g);function f(k,j){a.log(a+".loadHelper, start:",k,"size:",j);var i=a.buildUrl(k,j);a.log("\t url:",i);jQuery.ajax({url:a.buildUrl(k,j),dataType:"json",error:function(n,l,m){a.log("\t ajax error, status:",l,"error:",m);if(a.currentIntervalId){clearInterval(a.currentIntervalId)}$(a).trigger(ERROR_EVENT,[l,m]);a.ajaxErrorFn(n,l,m)},success:function(l){a.log("\t ajax success, response:",l,"next:",m,"remainder:",n);if(l!==null){a.data.push(l);$(a).trigger(d,[l,k,j]);var m=k+j,n=a.size;if(a.total!==null){n=Math.min(a.total-m,a.size)}a.log("\t next recursion, start:",m,"size:",n);if(a.total===null||n>0){a.currentIntervalId=setTimeout(function(){f(m,n)},a.delay);a.log("\t currentIntervalId:",a.currentIntervalId)}else{e()}}else{e()}}})}function e(){a.log(a+".loadHelper, has finished:",a.data);$(a).trigger(b,[a.data,a.total]);if(h){h(a.data)}}},toString:function(){return"LazyDataLoader"}});a.initialize(c);return a};
\ No newline at end of file
+function LazyDataLoader(c){var a=this,d="loaded.new",b="complete";ERROR_EVENT="error";jQuery.extend(a,LoggableMixin);jQuery.extend(a,{total:undefined,url:undefined,currentIntervalId:undefined,data:[],delay:4000,start:0,size:4000,initialize:function(e){jQuery.extend(a,e);if(e.hasOwnProperty("initialize")){e.initialize.call(a,e)}this.log(this+" initialized:",a)},buildUrl:function(f,e){return this.url+"&"+jQuery.param({start_val:f,max_vals:e})},ajaxErrorFn:function(g,e,f){},load:function(h){this.log(this+".load");if(!a.url){throw (a+" requires a url")}if(this.total===null){this.log("\t total is null (will load all)")}else{this.log("\t total:",this.total)}var g=a.size;if((a.total!==null)&&(a.total<a.size)){g=a.total}a.log(a+"\t beginning recursion");f(a.start,g);function f(k,j){a.log(a+".loadHelper, start:",k,"size:",j);var i=a.buildUrl(k,j);a.log("\t url:",i);jQuery.ajax({url:a.buildUrl(k,j),dataType:"json",error:function(n,l,m){a.log("\t ajax error, status:",l,"error:",m);if(a.currentIntervalId){clearInterval(a.currentIntervalId)}$(a).trigger(ERROR_EVENT,[l,m]);a.ajaxErrorFn(n,l,m)},success:function(l){a.log("\t ajax success, response:",l,"next:",m,"remainder:",n);if(l!==null){a.data.push(l);$(a).trigger(d,[l,k,j]);var m=k+j,n=a.size;if(a.total!==null){n=Math.min(a.total-m,a.size)}a.log("\t next recursion, start:",m,"size:",n);if(a.total===null||n>0){a.currentIntervalId=setTimeout(function(){f(m,n)},a.delay);a.log("\t currentIntervalId:",a.currentIntervalId)}else{e()}}else{e()}}})}function e(){a.log(a+".loadHelper, has finished:",a.data);$(a).trigger(b,[a.data,a.total]);if(h){h(a.data)}}},toString:function(){return"LazyDataLoader"}});a.initialize(c);return a};
\ No newline at end of file
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/packed/viz/scatterplot.js
--- a/static/scripts/packed/viz/scatterplot.js
+++ b/static/scripts/packed/viz/scatterplot.js
@@ -1,1 +1,1 @@
-function TwoVarScatterplot(d){var b=10,f=7,e=10,c=8,a=5;this.log=function(){if(this.debugging&&console&&console.debug){var g=Array.prototype.slice.call(arguments);g.unshift(this.toString());console.debug.apply(console,g)}};this.log("new TwoVarScatterplot:",d);this.defaults={id:"TwoVarScatterplot",containerSelector:"body",maxDataPoints:30000,datapointSize:4,animDuration:500,xNumTicks:10,yNumTicks:10,xAxisLabelBumpY:40,yAxisLabelBumpX:-35,width:400,height:400,marginTop:50,marginRight:50,marginBottom:50,marginLeft:50,xMin:null,xMax:null,yMin:null,yMax:null,xLabel:"X",yLabel:"Y"};this.config=_.extend({},this.defaults,d);this.log("intial config:",this.config);this.updateConfig=function(g,h){_.extend(this.config,g);this.log(this+".updateConfig:",this.config)};this.toString=function(){return this.config.id};this.translateStr=function(g,h){return"translate("+g+","+h+")"};this.rotateStr=function(h,g,i){return"rotate("+h+","+g+","+i+")"};this.adjustChartDimensions=function(j,h,g,i){j=j||0;h=h||0;g=g||0;i=i||0;this.svg.attr("width",this.config.width+(this.config.marginRight+h)+(this.config.marginLeft+i)).attr("height",this.config.height+(this.config.marginTop+j)+(this.config.marginBottom+g)).style("display","block");this.content=this.svg.select("g.content").attr("transform",this.translateStr(this.config.marginLeft+i,this.config.marginTop+j))};this.preprocessData=function(i,h,g){return(i.length>this.config.maxDataPoints)?(i.slice(0,this.config.maxDataPoints)):(i)};this.findMinMaxes=function(g,i,h){this.xMin=this.config.xMin||(h)?(h[0].min):(d3.min(g));this.xMax=this.config.xMax||(h)?(h[0].max):(d3.max(g));this.yMin=this.config.yMin||(h)?(h[1].min):(d3.min(i));this.yMax=this.config.yMax||(h)?(h[1].max):(d3.max(i))};this.setUpScales=function(){this.xScale=d3.scale.linear().domain([this.xMin,this.xMax]).range([0,this.config.width]),this.yScale=d3.scale.linear().domain([this.yMin,this.yMax]).range([this.config.height,0])};this.setUpXAxis=function(){this.xAxisFn=d3.svg.axis().scale(this.xScale).ticks(this.config.xNumTicks).orient("bottom");this.xAxis.attr("transform",this.translateStr(0,this.config.height)).call(this.xAxisFn);var g=d3.max(_.map([this.xMin,this.xMax],function(h){return(String(h)).length}));if(g>=a){this.xAxis.selectAll("g").filter(":nth-child(odd)").style("display","none")}this.log("this.config.xLabel:",this.config.xLabel);this.xAxisLabel.attr("x",this.config.width/2).attr("y",this.config.xAxisLabelBumpY).attr("text-anchor","middle").text(this.config.xLabel);this.log("xAxisLabel:",this.xAxisLabel)};this.setUpYAxis=function(){this.yAxisFn=d3.svg.axis().scale(this.yScale).ticks(this.config.yNumTicks).orient("left");this.yAxis.call(this.yAxisFn);var g=this.yAxis.selectAll("text").filter(function(k,j){return j!==0});this.log("yTickLabels:",g);this.yLongestLabel=d3.max(g[0].map(function(k,j){return(d3.select(k).text()).length}))||0;var h=b+(this.yLongestLabel*f)+c+e;this.config.yAxisLabelBumpX=-(h-e);if(this.config.marginLeft<h){var i=(h)-this.config.marginLeft;i=(i<0)?(0):(i);this.adjustChartDimensions(0,0,0,i)}this.yAxisLabel.attr("x",this.config.yAxisLabelBumpX).attr("y",this.config.height/2).attr("text-anchor","middle").attr("transform",this.rotateStr(-90,this.config.yAxisLabelBumpX,this.config.height/2)).text(this.config.yLabel)};this.renderGrid=function(){this.vGridLines=this.content.selectAll("line.v-grid-line").data(this.xScale.ticks(this.xAxisFn.ticks()[0]));this.vGridLines.enter().append("svg:line").classed("grid-line v-grid-line",true);this.vGridLines.attr("x1",this.xScale).attr("y1",0).attr("x2",this.xScale).attr("y2",this.config.height);this.vGridLines.exit().remove();this.hGridLines=this.content.selectAll("line.h-grid-line").data(this.yScale.ticks(this.yAxisFn.ticks()[0]));this.hGridLines.enter().append("svg:line").classed("grid-line h-grid-line",true);this.hGridLines.attr("x1",0).attr("y1",this.yScale).attr("x2",this.config.width).attr("y2",this.yScale);this.hGridLines.exit().remove()};this.glyphEnterState=function(g){};this.glyphFinalState=function(g){};this.glyphExitState=function(g){};this.renderDatapoints=function(g,j,h){this.log(this+".renderDatapoints",arguments);var i=0;this.datapoints=this.addDatapoints(g,j,h,".glyph");this.datapoints.exit().each(function(){i+=1}).transition().duration(this.config.animDuration).attr("cy",this.config.height).attr("r",0).remove();this.log(i," glyphs removed")};this.addDatapoints=function(k,i,g,j){this.log(this+".addDatapoints",arguments);var n=this,m=0,o=function(q,p){return n.xScale(k[p])},h=function(q,p){return n.yScale(i[p])};var l=this.content.selectAll(j);this.log("existing datapoints:",l);l=l.data(k);m=0;l.enter().append("svg:circle").each(function(){m+=1}).classed("glyph",true).attr("cx",o).attr("cy",h).attr("r",0);this.log(m," new glyphs created");m=0;l.transition().duration(this.config.animDuration).each(function(){m+=1}).attr("cx",o).attr("cy",h).attr("r",n.config.datapointSize);this.log(m," existing glyphs transitioned");if(g){l.attr("data",function(q,p){return(g[p])})}l.attr("svg:title",function(q,p){return((g)?(g[p]+": "):(""))+k[p]+", "+i[p]});l.on("mouseover",function(r,p){var q=d3.select(this);q.style("fill","red").style("fill-opacity",1);n.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",q.attr("cx")).attr("y1",q.attr("cy")).attr("x2",0).attr("y2",q.attr("cy")).classed("hoverline",true);n.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",q.attr("cx")).attr("y1",q.attr("cy")).attr("x2",q.attr("cx")).attr("y2",n.config.height).classed("hoverline",true)}).on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",0.2);d3.selectAll(".hoverline").remove()});return l};this.render=function(i,j){this.log(this+".render",arguments);this.log("\t config:",this.config);var g=i[0],k=i[1],h=(i.length>2)?(i[2]):(undefined);g=this.preprocessData(g);k=this.preprocessData(k);this.log("xCol len",g.length,"yCol len",k.length);this.findMinMaxes(g,k,j);this.setUpScales();if(!this.svg){this.svg=d3.select("svg").attr("class","chart")}if(!this.content){this.content=this.svg.append("svg:g").attr("class","content").attr("id",this.config.id)}this.adjustChartDimensions();if(!this.xAxis){this.xAxis=this.content.append("g").attr("class","axis").attr("id","x-axis")}if(!this.xAxisLabel){this.xAxisLabel=this.xAxis.append("text").attr("class","axis-label").attr("id","x-axis-label")}if(!this.yAxis){this.yAxis=this.content.append("g").attr("class","axis").attr("id","y-axis")}if(!this.yAxisLabel){this.yAxisLabel=this.yAxis.append("text").attr("class","axis-label").attr("id","y-axis-label")}this.setUpXAxis();this.setUpYAxis();this.renderGrid();this.renderDatapoints(g,k,h)}};
\ No newline at end of file
+function TwoVarScatterplot(d){var b=10,f=7,e=10,c=8,a=5;this.log=function(){if(this.debugging&&console&&console.debug){var g=Array.prototype.slice.call(arguments);g.unshift(this.toString());console.debug.apply(console,g)}};this.log("new TwoVarScatterplot:",d);this.defaults={id:"TwoVarScatterplot",containerSelector:"body",maxDataPoints:30000,datapointSize:4,animDuration:500,xNumTicks:10,yNumTicks:10,xAxisLabelBumpY:40,yAxisLabelBumpX:-40,width:400,height:400,marginTop:50,marginRight:50,marginBottom:50,marginLeft:50,xMin:null,xMax:null,yMin:null,yMax:null,xLabel:"X",yLabel:"Y"};this.config=_.extend({},this.defaults,d);this.log("intial config:",this.config);this.updateConfig=function(g,h){_.extend(this.config,g);this.log(this+".updateConfig:",this.config)};this.toString=function(){return this.config.id};this.translateStr=function(g,h){return"translate("+g+","+h+")"};this.rotateStr=function(h,g,i){return"rotate("+h+","+g+","+i+")"};this.adjustChartDimensions=function(j,h,g,i){j=j||0;h=h||0;g=g||0;i=i||0;this.svg.attr("width",this.config.width+(this.config.marginRight+h)+(this.config.marginLeft+i)).attr("height",this.config.height+(this.config.marginTop+j)+(this.config.marginBottom+g)).style("display","block");this.content=this.svg.select("g.content").attr("transform",this.translateStr(this.config.marginLeft+i,this.config.marginTop+j))};this.preprocessData=function(i,h,g){return(i.length>this.config.maxDataPoints)?(i.slice(0,this.config.maxDataPoints)):(i)};this.findMinMaxes=function(g,i,h){this.xMin=this.config.xMin||(h)?(h[0].min):(d3.min(g));this.xMax=this.config.xMax||(h)?(h[0].max):(d3.max(g));this.yMin=this.config.yMin||(h)?(h[1].min):(d3.min(i));this.yMax=this.config.yMax||(h)?(h[1].max):(d3.max(i))};this.setUpScales=function(){this.xScale=d3.scale.linear().domain([this.xMin,this.xMax]).range([0,this.config.width]),this.yScale=d3.scale.linear().domain([this.yMin,this.yMax]).range([this.config.height,0])};this.setUpXAxis=function(){this.xAxisFn=d3.svg.axis().scale(this.xScale).ticks(this.config.xNumTicks).orient("bottom");this.xAxis.attr("transform",this.translateStr(0,this.config.height)).call(this.xAxisFn);var g=d3.max(_.map([this.xMin,this.xMax],function(h){return(String(h)).length}));if(g>=a){this.xAxis.selectAll("g").filter(":nth-child(odd)").style("display","none")}this.log("this.config.xLabel:",this.config.xLabel);this.xAxisLabel.attr("x",this.config.width/2).attr("y",this.config.xAxisLabelBumpY).attr("text-anchor","middle").text(this.config.xLabel);this.log("xAxisLabel:",this.xAxisLabel)};this.setUpYAxis=function(){this.yAxisFn=d3.svg.axis().scale(this.yScale).ticks(this.config.yNumTicks).orient("left");this.yAxis.call(this.yAxisFn);var g=this.yAxis.selectAll("text").filter(function(k,j){return j!==0});this.log("yTickLabels:",g);this.yLongestLabel=d3.max(g[0].map(function(k,j){return(d3.select(k).text()).length}))||0;var h=b+(this.yLongestLabel*f)+c+e;this.config.yAxisLabelBumpX=-(h-e);if(this.config.marginLeft<h){var i=(h)-this.config.marginLeft;i=(i<0)?(0):(i);this.adjustChartDimensions(0,0,0,i)}this.yAxisLabel.attr("x",this.config.yAxisLabelBumpX).attr("y",this.config.height/2).attr("text-anchor","middle").attr("transform",this.rotateStr(-90,this.config.yAxisLabelBumpX,this.config.height/2)).text(this.config.yLabel)};this.renderGrid=function(){this.vGridLines=this.content.selectAll("line.v-grid-line").data(this.xScale.ticks(this.xAxisFn.ticks()[0]));this.vGridLines.enter().append("svg:line").classed("grid-line v-grid-line",true);this.vGridLines.attr("x1",this.xScale).attr("y1",0).attr("x2",this.xScale).attr("y2",this.config.height);this.vGridLines.exit().remove();this.hGridLines=this.content.selectAll("line.h-grid-line").data(this.yScale.ticks(this.yAxisFn.ticks()[0]));this.hGridLines.enter().append("svg:line").classed("grid-line h-grid-line",true);this.hGridLines.attr("x1",0).attr("y1",this.yScale).attr("x2",this.config.width).attr("y2",this.yScale);this.hGridLines.exit().remove()};this.renderDatapoints=function(g,n,j){this.log(this+".renderDatapoints",arguments);var k=0,m=this,i=function(p,o){return m.xScale(g[o])},h=function(p,o){return m.yScale(n[o])};var l=this.content.selectAll(".glyph").data(g);k=0;l.enter().append("svg:circle").each(function(){k+=1}).classed("glyph",true).attr("cx",0).attr("cy",this.config.height).attr("r",0);this.log(k," new glyphs created");k=0;l.transition().duration(this.config.animDuration).each(function(){k+=1}).attr("cx",i).attr("cy",h).attr("r",m.config.datapointSize);this.log(k," existing glyphs transitioned");l.exit().each(function(){k+=1}).transition().duration(this.config.animDuration).attr("cy",this.config.height).attr("r",0).remove();this.log(k," glyphs removed");this._addDatapointEventhandlers(l,g,n,j)};this._addDatapointEventhandlers=function(j,g,k,h){var i=this;j.on("mouseover",function(o,l){var n=d3.select(this);n.style("fill","red").style("fill-opacity",1);i.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",n.attr("cx")-i.config.datapointSize).attr("y1",n.attr("cy")).attr("x2",0).attr("y2",n.attr("cy")).classed("hoverline",true);if(n.attr("cy")<i.config.height){i.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",n.attr("cx")).attr("y1",n.attr("cy")+i.config.datapointSize).attr("x2",n.attr("cx")).attr("y2",i.config.height).classed("hoverline",true)}var m=$(this).offset();i.datapointInfoBox=i.infoBox(m.top,m.left,i.infoHtml(g[l],k[l],(h)?(h[l]):(undefined)));$("body").append(i.datapointInfoBox)}).on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",0.2);i.content.selectAll(".hoverline").remove();if(i.datapointInfoBox){i.datapointInfoBox.remove()}})},this.render=function(i,j){this.log(this+".render",arguments);this.log("\t config:",this.config);var g=i[0],k=i[1],h=(i.length>2)?(i[2]):(undefined);g=this.preprocessData(g);k=this.preprocessData(k);this.log("xCol len",g.length,"yCol len",k.length);this.findMinMaxes(g,k,j);this.setUpScales();if(!this.svg){this.svg=d3.select("svg").attr("class","chart")}if(!this.content){this.content=this.svg.append("svg:g").attr("class","content").attr("id",this.config.id)}this.adjustChartDimensions();if(!this.xAxis){this.xAxis=this.content.append("g").attr("class","axis").attr("id","x-axis")}if(!this.xAxisLabel){this.xAxisLabel=this.xAxis.append("text").attr("class","axis-label").attr("id","x-axis-label")}if(!this.yAxis){this.yAxis=this.content.append("g").attr("class","axis").attr("id","y-axis")}if(!this.yAxisLabel){this.yAxisLabel=this.yAxis.append("text").attr("class","axis-label").attr("id","y-axis-label")}this.setUpXAxis();this.setUpYAxis();this.renderGrid();this.renderDatapoints(g,k,h)};this.infoHtml=function(g,j,i){var h=$("<div/>");if(i){$("<div/>").text(i).css("font-weight","bold").appendTo(h)}$("<div/>").text(g).appendTo(h);$("<div/>").text(j).appendTo(h);return h.html()};this.infoBox=function(l,k,i,h,g){h=h||0;g=g||20;var j=$("<div />").addClass("chart-info-box").css({position:"absolute",top:l+h,left:k+g});j.html(i);return j}};
\ No newline at end of file
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/templates/compiled/template-visualization-chartControl.js
--- a/static/scripts/templates/compiled/template-visualization-chartControl.js
+++ b/static/scripts/templates/compiled/template-visualization-chartControl.js
@@ -13,19 +13,19 @@
foundHelper = helpers.datapointSize;
if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
else { stack1 = depth0.datapointSize; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id=\"animDuration\" class=\"form-input checkbox-input\">\n <label for=\"animated\">Animate graph transitions?: </label>\n <input type=\"checkbox\" id=\"animated\"\n class=\"checkbox control\"";
+ buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id=\"animDuration\" class=\"form-input checkbox-input\">\n <label for=\"animate-chart\">Animate chart transitions?: </label>\n <input type=\"checkbox\" id=\"animate-chart\"\n class=\"checkbox control\"";
stack1 = depth0.animDuration;
stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)});
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += " />\n <p class=\"form-help help-text-small\">\n Uncheck this to disable the animations used on the graph\n </p>\n </div>\n\n <div id=\"width\" class=\"form-input numeric-slider-input\">\n <label for=\"width\">Graph width: </label>\n <div class=\"slider-output\">";
+ buffer += " />\n <p class=\"form-help help-text-small\">\n Uncheck this to disable the animations used on the chart\n </p>\n </div>\n\n <div id=\"width\" class=\"form-input numeric-slider-input\">\n <label for=\"width\">Chart width: </label>\n <div class=\"slider-output\">";
foundHelper = helpers.width;
if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
else { stack1 = depth0.width; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id=\"height\" class=\"form-input numeric-slider-input\">\n <label for=\"height\">Graph height: </label>\n <div class=\"slider-output\">";
+ buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including chart margins and axes)\n </p>\n </div>\n\n <div id=\"height\" class=\"form-input numeric-slider-input\">\n <label for=\"height\">Chart height: </label>\n <div class=\"slider-output\">";
foundHelper = helpers.height;
if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
else { stack1 = depth0.height; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id=\"X-axis-label\"class=\"text-input form-input\">\n <label for=\"X-axis-label\">Re-label the X axis: </label>\n <input type=\"text\" name=\"X-axis-label\" id=\"X-axis-label\" value=\"";
+ buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including chart margins and axes)\n </p>\n </div>\n\n <div id=\"X-axis-label\"class=\"text-input form-input\">\n <label for=\"X-axis-label\">Re-label the X axis: </label>\n <input type=\"text\" name=\"X-axis-label\" id=\"X-axis-label\" value=\"";
foundHelper = helpers.xLabel;
if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
else { stack1 = depth0.xLabel; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/templates/compiled/template-visualization-dataControl.js
--- a/static/scripts/templates/compiled/template-visualization-dataControl.js
+++ b/static/scripts/templates/compiled/template-visualization-dataControl.js
@@ -2,7 +2,7 @@
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates['template-visualization-dataControl'] = template(function (Handlebars,depth0,helpers,partials,data) {
helpers = helpers || Handlebars.helpers;
- var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
+ var buffer = "", stack1, foundHelper, functionType="function", escapeExpression=this.escapeExpression, self=this;
function program1(depth0,data) {
@@ -46,6 +46,11 @@
buffer += escapeExpression(stack1) + "</option>\n ";
return buffer;}
+function program7(depth0,data) {
+
+
+ return "checked=\"true\"";}
+
buffer += "<p class=\"help-text\">\n Use the following controls to change the data used by the chart.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n </p>\n\n ";
buffer += "\n <div class=\"column-select\">\n <label for=\"X-select\">Data column for X: </label>\n <select name=\"X\" id=\"X-select\">\n ";
stack1 = depth0.numericColumns;
@@ -60,6 +65,15 @@
stack1 = depth0.allColumns;
stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(5, program5, data)});
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n </select>\n </div>\n\n <input id=\"render-button\" type=\"button\" value=\"Draw\" />\n <div class=\"clear\"></div>";
+ buffer += "\n </select>\n </div>\n\n ";
+ buffer += "\n <div id=\"first-line-header\" style=\"display: none;\">\n <p>Possible headers: ";
+ foundHelper = helpers.possibleHeaders;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.possibleHeaders; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\n </p>\n <label for=\"first-line-header-checkbox\">Use the above as column headers?</label>\n <input type=\"checkbox\" name=\"include-id\" id=\"first-line-header-checkbox\"\n ";
+ stack1 = depth0.usePossibleHeaders;
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(7, program7, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "/>\n <p class=\"help-text-small\">\n It looks like Galaxy couldn't get proper column headers for this data.\n Would you like to use the column headers above as column names to select columns?\n </p>\n </div>\n\n <input id=\"render-button\" type=\"button\" value=\"Draw\" />\n <div class=\"clear\"></div>";
return buffer;});
})();
\ No newline at end of file
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/templates/visualization-templates.html
--- a/static/scripts/templates/visualization-templates.html
+++ b/static/scripts/templates/visualization-templates.html
@@ -90,6 +90,19 @@
</select></div>
+ {{! if we're using generic column selection names ('column 1') - allow the user to use the first line }}
+ <div id="first-line-header" style="display: none;">
+ <p>Possible headers: {{ possibleHeaders }}
+ </p>
+ <label for="first-line-header-checkbox">Use the above as column headers?</label>
+ <input type="checkbox" name="include-id" id="first-line-header-checkbox"
+ {{#if usePossibleHeaders }}checked="true"{{/if}}/>
+ <p class="help-text-small">
+ It looks like Galaxy couldn't get proper column headers for this data.
+ Would you like to use the column headers above as column names to select columns?
+ </p>
+ </div>
+
<input id="render-button" type="button" value="Draw" /><div class="clear"></div></script>
@@ -112,29 +125,29 @@
</div><div id="animDuration" class="form-input checkbox-input">
- <label for="animated">Animate graph transitions?: </label>
- <input type="checkbox" id="animated"
+ <label for="animate-chart">Animate chart transitions?: </label>
+ <input type="checkbox" id="animate-chart"
class="checkbox control"{{#if animDuration}} checked="true"{{/if}} /><p class="form-help help-text-small">
- Uncheck this to disable the animations used on the graph
+ Uncheck this to disable the animations used on the chart
</p></div><div id="width" class="form-input numeric-slider-input">
- <label for="width">Graph width: </label>
+ <label for="width">Chart width: </label><div class="slider-output">{{width}}</div><div class="slider"></div><p class="form-help help-text-small">
- (not including graph margins and axes)
+ (not including chart margins and axes)
</p></div><div id="height" class="form-input numeric-slider-input">
- <label for="height">Graph height: </label>
+ <label for="height">Chart height: </label><div class="slider-output">{{height}}</div><div class="slider"></div><p class="form-help help-text-small">
- (not including graph margins and axes)
+ (not including chart margins and axes)
</p></div>
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/utils/LazyDataLoader.js
--- a/static/scripts/utils/LazyDataLoader.js
+++ b/static/scripts/utils/LazyDataLoader.js
@@ -81,11 +81,11 @@
// it's the responsibility of the code using this to combine them properly
data : [],
// ms btwn recursive loads
- delay : 500,
+ delay : 4000,
// starting line, element, whatever
start : 0,
// size to fetch per load
- size : 1000,
+ size : 4000,
// loader init func: extends loader with config and calls config.init if there
//@param {object} config : object containing variables to override (or additional)
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa static/scripts/viz/scatterplot.js
--- a/static/scripts/viz/scatterplot.js
+++ b/static/scripts/viz/scatterplot.js
@@ -90,7 +90,7 @@
xNumTicks : 10,
yNumTicks : 10,
xAxisLabelBumpY : 40,
- yAxisLabelBumpX : -35,
+ yAxisLabelBumpX : -40,
width : 400,
height : 400,
//TODO: anyway to make this a sub-obj?
@@ -304,75 +304,37 @@
};
// ........................................................ data points
- //TODO: these to config ...somehow
- //TODO: use these in renderDatapoints ...somehow
- this.glyphEnterState = function( d3Elem ){
-
- };
- this.glyphFinalState = function( d3Elem ){
-
- };
- this.glyphExitState = function( d3Elem ){
-
- };
-
- // initial render or complete re-render (REPLACE datapoints)
this.renderDatapoints = function( xCol, yCol, ids ){
this.log( this + '.renderDatapoints', arguments );
- var count = 0;
-
- this.datapoints = this.addDatapoints( xCol, yCol, ids, ".glyph" );
-
- // glyphs that need to be removed: transition to from normal state to 'exit' state, remove from DOM
- this.datapoints.exit()
- .each( function(){ count += 1; } )
- .transition().duration( this.config.animDuration )
- .attr( "cy", this.config.height )
- .attr( "r", 0 )
- .remove();
- this.log( count, ' glyphs removed' );
-
- //this.log( this.datapoints.length, ' glyphs in the graph' );
- };
-
- // adding points to existing
- this.addDatapoints = function( newXCol, newYCol, ids, selectorForExisting ){
- this.log( this + '.addDatapoints', arguments );
- // ADD datapoints to plot that's already rendered
- // if selectorForExisting === undefined (as in not passed), addDatapoints won't update existing
- // pass in the class ( '.glyph' ) to update exising datapoints
- var plot = this,
- count = 0,
+ var count = 0,
+ plot = this,
xPosFn = function( d, i ){
//if( d ){ this.log( 'x.data:', newXCol[ i ], 'plotted:', plot.xScale( newXCol[ i ] ) ); }
- return plot.xScale( newXCol[ i ] );
+ return plot.xScale( xCol[ i ] );
},
yPosFn = function( d, i ){
//if( d ){ this.log( 'y.data:', newYCol[ i ], 'plotted:', plot.yScale( newYCol[ i ] ) ); }
- return plot.yScale( newYCol[ i ] );
+ return plot.yScale( yCol[ i ] );
};
-
- // select all existing glyphs and compare to incoming data
- // enter() will yield those glyphs that need to be added
- var newDatapoints = this.content.selectAll( selectorForExisting );
- this.log( 'existing datapoints:', newDatapoints );
- newDatapoints = newDatapoints.data( newXCol );
-
- // enter - new data to be added as glyphs: give them a 'entry' position and style
+
+ //this.datapoints = this.addDatapoints( xCol, yCol, ids, ".glyph" );
+ var datapoints = this.content.selectAll( '.glyph' ).data( xCol );
+
+ // enter - NEW data to be added as glyphs: give them a 'entry' position and style
count = 0;
- newDatapoints.enter()
+ datapoints.enter()
.append( 'svg:circle' )
.each( function(){ count += 1; } )
.classed( "glyph", true )
- .attr( "cx", xPosFn )
- .attr( "cy", yPosFn )
+ .attr( "cx", 0 )
+ .attr( "cy", this.config.height )
// start all bubbles small...
.attr( "r", 0 );
this.log( count, ' new glyphs created' );
- // for all existing glyphs and those that need to be added: transition anim to final state
+ // for all EXISTING glyphs and those that need to be added: transition anim to final state
count = 0;
- newDatapoints
+ datapoints
// ...animate to final position
.transition().duration( this.config.animDuration )
.each( function(){ count += 1; } )
@@ -381,18 +343,22 @@
.attr( "r", plot.config.datapointSize );
this.log( count, ' existing glyphs transitioned' );
- // attach ids
- if( ids ){
- newDatapoints.attr( 'data', function( d, i ){ return ( ids[ i ] ); } );
- }
+ // events
+ // glyphs that need to be removed: transition to from normal state to 'exit' state, remove from DOM
+ datapoints.exit()
+ .each( function(){ count += 1; } )
+ .transition().duration( this.config.animDuration )
+ .attr( "cy", this.config.height )
+ .attr( "r", 0 )
+ .remove();
+ this.log( count, ' glyphs removed' );
- // titles
- newDatapoints.attr( 'svg:title', function( d, i ){
- return (( ids )?( ids[ i ] + ': ' ):( '' )) + newXCol[ i ] + ', ' + newYCol[ i ];
- });
-
- // events
- newDatapoints
+ this._addDatapointEventhandlers( datapoints, xCol, yCol, ids );
+ };
+
+ this._addDatapointEventhandlers = function( datapoints, xCol, yCol, ids ){
+ var plot = this;
+ datapoints
//TODO: remove magic numbers
.on( 'mouseover', function( d, i ){
var datapoint = d3.select( this );
@@ -404,36 +370,42 @@
plot.content.append( 'line' )
.attr( 'stroke', 'red' )
.attr( 'stroke-width', 1 )
- .attr( 'x1', datapoint.attr( 'cx' ) ).attr( 'y1', datapoint.attr( 'cy' ) )
- .attr( 'x2', 0 ).attr( 'y2', datapoint.attr( 'cy' ) )
- .classed( 'hoverline', true );
- plot.content.append( 'line' )
- .attr( 'stroke', 'red' )
- .attr( 'stroke-width', 1 )
- .attr( 'x1', datapoint.attr( 'cx' ) ).attr( 'y1', datapoint.attr( 'cy' ) )
- .attr( 'x2', datapoint.attr( 'cx' ) ).attr( 'y2', plot.config.height )
+ // start not at center, but at the edge of the circle - to prevent mouseover thrashing
+ .attr( 'x1', datapoint.attr( 'cx' ) - plot.config.datapointSize )
+ .attr( 'y1', datapoint.attr( 'cy' ) )
+ .attr( 'x2', 0 )
+ .attr( 'y2', datapoint.attr( 'cy' ) )
.classed( 'hoverline', true );
- //var datapointWindowPos = $( this ).position();
- //var datapointWindowPos = $( this ).offset();
- //window.dot = this;
- ////this.popup = make_abs_box( datapointWindowPos.top, datapointWindowPos.left + datapoint.attr( 'r' ),
- //this.popup = make_abs_box( datapointWindowPos.top, datapointWindowPos.left,
- // newXCol[ i ], newYCol[ i ], ( ids )?( ids[ i ] ):( undefined ) );
- //$( 'body' ).append( this.popup );
+ // if the vertical hoverline
+ if( datapoint.attr( 'cy' ) < plot.config.height ){
+ plot.content.append( 'line' )
+ .attr( 'stroke', 'red' )
+ .attr( 'stroke-width', 1 )
+ .attr( 'x1', datapoint.attr( 'cx' ) )
+ .attr( 'y1', datapoint.attr( 'cy' ) + plot.config.datapointSize )
+ .attr( 'x2', datapoint.attr( 'cx' ) )
+ .attr( 'y2', plot.config.height )
+ .classed( 'hoverline', true );
+ }
+
+ var datapointWindowPos = $( this ).offset();
+ plot.datapointInfoBox = plot.infoBox(
+ datapointWindowPos.top, datapointWindowPos.left,
+ plot.infoHtml( xCol[ i ], yCol[ i ], ( ids )?( ids[ i ] ):( undefined ) )
+ );
+ $( 'body' ).append( plot.datapointInfoBox );
})
.on( 'mouseout', function(){
d3.select( this )
.style( 'fill', 'black' )
.style( 'fill-opacity', 0.2 );
- d3.selectAll( '.hoverline' ).remove();
- //if( this.popup ){
- // this.popup.remove();
- //}
+ plot.content.selectAll( '.hoverline' ).remove();
+ if( plot.datapointInfoBox ){
+ plot.datapointInfoBox.remove();
+ }
});
-
- return newDatapoints;
- };
+ },
this.render = function( columnData, meta ){
this.log( this + '.render', arguments );
@@ -457,7 +429,7 @@
//this.log( 'xMin, xMax, yMin, yMax:', this.xMin, this.xMax, this.yMin, this.yMax );
this.setUpScales();
- // build the svg dom infrastructure
+ // find (or build if it doesn't exist) the svg dom infrastructure
if( !this.svg ){ this.svg = d3.select( 'svg' ).attr( "class", "chart" ); }
if( !this.content ){
this.content = this.svg.append( "svg:g" ).attr( "class", "content" ).attr( 'id', this.config.id );
@@ -485,6 +457,32 @@
this.renderGrid();
this.renderDatapoints( xCol, yCol, ids );
};
+
+ this.infoHtml = function( x, y, id ){
+ var retDiv = $( '<div/>' );
+ if( id ){
+ $( '<div/>' ).text( id ).css( 'font-weight', 'bold' ).appendTo( retDiv );
+ }
+ $( '<div/>' ).text( x ).appendTo( retDiv );
+ $( '<div/>' ).text( y ).appendTo( retDiv );
+ return retDiv.html();
+ };
+
+ //TODO: html for now
+ this.infoBox = function( top, left, html, adjTop, adjLeft ){
+ adjTop = adjTop || 0;
+ adjLeft = adjLeft || 20;
+ var infoBox = $( '<div />' )
+ .addClass( 'chart-info-box' )
+ .css({
+ 'position' : 'absolute',
+ 'top' : top + adjTop,
+ 'left' : left + adjLeft
+ });
+ infoBox.html( html );
+ return infoBox;
+ };
+
}
//==============================================================================
diff -r 3c4696f6af0d9c6c7885568c1fee02df53849322 -r 4eaa644dd47877c40ee7090d56379998d7aecafa templates/visualization/scatterplot.mako
--- a/templates/visualization/scatterplot.mako
+++ b/templates/visualization/scatterplot.mako
@@ -26,6 +26,7 @@
padding : 8px;
background-color: #ebd9b2;
margin-bottom: 16px;
+ overflow: auto;
}
#chart-header .subtitle {
@@ -182,12 +183,11 @@
}
/* -------------------------------------------- info box */
-.zero-dimensions {
- width: 0;
- height: 0;
- border-top: 8px solid transparent;
- border-bottom: 8px solid transparent;
- border-right: 8px solid grey;
+.chart-info-box {
+ border-radius: 4px;
+ padding: 4px;
+ background-color: white;
+ border: 1px solid black;
}
</style>
@@ -255,63 +255,3 @@
</div><div id="scatterplot" class="scatterplot-control-form"></div></%def>
-
-
-<script type="text/javascript">
-function make_abs_box( top, left, x, y, id ){
- var ARROW_SIZE = 8,
- ARROW_COLOR = 'grey',
- DIST_TO_POINT = 4,
- halfArrowSize = ARROW_SIZE / 2;
-
- var boxContainer = $( '<div />' )
- .attr( 'id', 'abs-box-container' )
- // top left arrow
- .css({
- 'position' : 'absolute',
- 'top' : top - halfArrowSize,
- 'left' : left + ARROW_SIZE + DIST_TO_POINT,
- 'background-color': 'transparent',
- });
- window.boxContainer = boxContainer;
-
- var arrowLeft = $( '<div />' )
- .attr( 'id', 'abs-box-arrow' )
- .addClass( 'zero-dimensions' )
- .css({
- 'border-top' : ARROW_SIZE + 'px solid transparent',
- 'border-bottom' : ARROW_SIZE + 'px solid transparent',
- 'border-right' : ARROW_SIZE + 'px solid ' + ARROW_COLOR,
- });
- boxContainer.append( arrowLeft );
- window.arrow = arrowLeft;
-
- console.debug( 'arrow height:', arrowLeft.height() );
- var boxInfo = $( '<div />' )
- .attr( 'id', 'abs-box' )
- .css({
- 'position' : 'relative',
- //TODO: 4 here is the border-radius
- 'top' : -( 2 * ARROW_SIZE + 6 ),
- 'left' : ARROW_SIZE,
- 'border' : '2px solid grey',
- 'border-radius' : '4px',
- 'padding' : '4px',
- 'background-color': 'white',
- 'box-shadow' : '4px 4px 4px black'
- });
-
- // remove
- if( id ){
- $( '<div />' ).addClass( 'abs-box-id' ).css( 'font-weight', 'bold' ).text( id ).appendTo( boxInfo );
- }
- $( '<div />' ).addClass( 'abs-box-x' ).text( x ).appendTo( boxInfo );
- $( '<div />' ).addClass( 'abs-box-y' ).text( y ).appendTo( boxInfo );
- boxContainer.append( boxInfo );
- boxContainer.append( '<div style="clear:both"></div>' );
- window.boxInfo = boxContainer;
-
- //console.debug( boxContainer );
- return boxContainer;
-}
-</script>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: natefoo: Fix check_galaxy for recent framework changes.
by Bitbucket 27 Nov '12
by Bitbucket 27 Nov '12
27 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/3c4696f6af0d/
changeset: 3c4696f6af0d
user: natefoo
date: 2012-11-27 21:57:05
summary: Fix check_galaxy for recent framework changes.
affected #: 1 file
diff -r 51ffca8538ff769f54f379d6e070494fc2c14833 -r 3c4696f6af0d9c6c7885568c1fee02df53849322 contrib/nagios/check_galaxy.py
--- a/contrib/nagios/check_galaxy.py
+++ b/contrib/nagios/check_galaxy.py
@@ -4,7 +4,7 @@
via the check_galaxy.sh script in Galaxy's cron/ directory.
"""
-import socket, sys, os, time, tempfile, filecmp, htmllib, formatter, getopt
+import socket, sys, os, time, tempfile, filecmp, htmllib, formatter, getopt, json
from user import home
import warnings
@@ -63,13 +63,6 @@
username = args[1]
password = args[2]
-if server.endswith(".g2.bx.psu.edu"):
- if debug:
- print "Checking a PSU Galaxy server, using maint file"
- maint = "/errordocument/502/%s/maint" % args[0].split('.', 1)[0]
-else:
- maint = None
-
new_history = False
for o, a in opts:
if o == "-n":
@@ -95,13 +88,12 @@
def __init__(self):
self.server = server
- self.maint = maint
self.tool = None
self.tool_opts = None
- self.id = None
- self.status = None
+ self._hda_id = None
+ self._hda_state = None
+ self._history_id = None
self.check_file = None
- self.hid = None
self.cookie_jar = os.path.join( var_dir, "cookie_jar" )
dprint("cookie jar path: %s" % self.cookie_jar)
if not os.access(self.cookie_jar, os.R_OK):
@@ -116,19 +108,14 @@
def reset(self):
self.tool = None
self.tool_opts = None
- self.id = None
- self.status = None
+ self._hda_id = None
+ self._hda_state = None
+ self._history_id = None
self.check_file = None
- self.delete_datasets()
- self.get("/root/history")
- p = didParser()
- p.feed(tc.browser.get_html())
- if len(p.dids) > 0:
- print "Remaining datasets ids:", " ".join( p.dids )
- raise Exception, "History still contains datasets after attempting to delete them"
if new_history:
self.get("/history/delete_current")
tc.save_cookies(self.cookie_jar)
+ self.delete_datasets()
def check_redir(self, url):
try:
@@ -143,25 +130,9 @@
dprint( "%s is not returning redirect (302): %s" % (url, e) )
code = tc.browser.get_code()
if code == 502:
- is_maint = self.check_maint()
- if is_maint:
- dprint( "Galaxy is down, but a maint file was found, so not sending alert" )
- sys.exit(0)
- else:
- print "Galaxy is down (code 502)"
- sys.exit(1)
- return(False)
-
- # checks for a maint file
- def check_maint(self):
- if self.maint is None:
- #dprint( "Warning: unable to check maint file for %s" % self.server )
- return(False)
- try:
- self.get(self.maint)
- return(True)
- except twill.errors.TwillAssertionError, e:
- return(False)
+ print "Galaxy is down (code 502)"
+ sys.exit(1)
+ return False
def login(self, user, pw):
self.get("/user/login")
@@ -210,15 +181,64 @@
tc.submit("runtool_btn")
tc.code(200)
+ @property
+ def history_id(self):
+ if self._history_id is None:
+ self.get('/api/histories')
+ self._history_id = json.loads(tc.browser.get_html())[0]['id']
+ return self._history_id
+
+ @property
+ def history_contents(self):
+ self.get('/api/histories/%s/contents' % self.history_id)
+ return json.loads(tc.browser.get_html())
+
+ @property
+ def hda_id(self):
+ if self._hda_id is None:
+ self.set_top_hda()
+ return self._hda_id
+
+ @property
+ def hda_state(self):
+ if self._hda_state is None:
+ self.set_top_hda()
+ return self._hda_state
+
+ def set_top_hda(self):
+ self.get(self.history_contents[-1]['url'])
+ hda = json.loads(tc.browser.get_html())
+ self._hda_id = hda['id']
+ self._hda_state = hda['state']
+
+ @property
+ def undeleted_hdas(self):
+ rval = []
+ for item in self.history_contents:
+ self.get(item['url'])
+ hda = json.loads(tc.browser.get_html())
+ if hda['deleted'] == False:
+ rval.append(hda)
+ return rval
+
+ @property
+ def history_state(self):
+ self.get('/api/histories/%s' % self.history_id)
+ return json.loads(tc.browser.get_html())['state']
+
+ @property
+ def history_state_terminal(self):
+ if self.history_state not in ['queued', 'running', 'paused']:
+ return True
+ return False
+
def wait(self):
sleep_amount = 1
count = 0
maxiter = 16
while count < maxiter:
count += 1
- self.get("/root/history")
- page = tc.browser.get_html()
- if page.find( '<!-- running: do not change this comment, used by TwillTestCase.wait -->' ) > -1:
+ if not self.history_state_terminal:
time.sleep( sleep_amount )
sleep_amount += 1
else:
@@ -226,20 +246,14 @@
if count == maxiter:
raise Exception, "Tool never finished"
- def check_status(self):
- self.get("/root/history")
- p = historyParser()
- p.feed(tc.browser.get_html())
- if p.status != "ok":
- self.get("/datasets/%s/stderr" % p.id)
+ def check_state(self):
+ if self.hda_state != "ok":
+ self.get("/datasets/%s/stderr" % self.hda_id)
print tc.browser.get_html()
- raise Exception, "HDA %s NOT OK: %s" % (p.id, p.status)
- self.id = p.id
- self.status = p.status
- #return((p.id, p.status))
+ raise Exception, "HDA %s NOT OK: %s" % (self.hda_id, self.hda_state)
def diff(self):
- self.get("/datasets/%s/display?to_ext=%s" % (self.id, self.tool_opts.get('out_format', 'fasta')))
+ self.get("/datasets/%s/display?to_ext=%s" % (self.hda_id, self.tool_opts.get('out_format', 'fasta')))
data = tc.browser.get_html()
tmp = tempfile.mkstemp()
dprint("tmp file: %s" % tmp[1])
@@ -256,12 +270,12 @@
os.remove(tmp[1])
def delete_datasets(self):
- self.get("/root/history")
- p = didParser()
- p.feed(tc.browser.get_html())
- dids = p.dids
- for did in dids:
- self.get("/datasets/%s/delete" % did)
+ for hda in self.undeleted_hdas:
+ self.get('/datasets/%s/delete' % hda['id'])
+ hdas = [hda['id'] for hda in self.undeleted_hdas]
+ if hdas:
+ print "Remaining datasets ids:", " ".join(hdas)
+ raise Exception, "History still contains datasets after attempting to delete them"
def check_if_logged_in(self):
self.get("/user?cntrller=user")
@@ -294,33 +308,6 @@
elif data == "User with that email already exists":
self.already_exists = True
-class historyParser(htmllib.HTMLParser):
- def __init__(self):
- htmllib.HTMLParser.__init__(self, formatter.NullFormatter())
- self.status = None
- self.id = None
- def start_div(self, attrs):
- # find the top history item
- for i in attrs:
- if i[0] == "class" and i[1].startswith("historyItemWrapper historyItem historyItem-"):
- self.status = i[1].rsplit("historyItemWrapper historyItem historyItem-", 1)[1]
- dprint("status: %s" % self.status)
- if i[0] == "id" and i[1].startswith("historyItem-"):
- self.id = i[1].rsplit("historyItem-", 1)[1]
- dprint("id: %s" % self.id)
- if self.status is not None:
- self.reset()
-
-class didParser(htmllib.HTMLParser):
- def __init__(self):
- htmllib.HTMLParser.__init__(self, formatter.NullFormatter())
- self.dids = []
- def start_div(self, attrs):
- for i in attrs:
- if i[0] == "id" and i[1].startswith("historyItemContainer-"):
- self.dids.append( i[1].rsplit("historyItemContainer-", 1)[1] )
- dprint("got a dataset id: %s" % self.dids[-1])
-
class loggedinParser(htmllib.HTMLParser):
def __init__(self):
htmllib.HTMLParser.__init__(self, formatter.NullFormatter())
@@ -379,15 +366,9 @@
b.runtool()
b.wait()
- b.check_status()
+ b.check_state()
b.diff()
b.delete_datasets()
- # by this point, everything else has succeeded. there should be no maint.
- is_maint = b.check_maint()
- if is_maint:
- print "Galaxy is up and fully functional, but a maint file is in place."
- sys.exit(1)
-
print "OK"
sys.exit(0)
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: inithello: Fixed error when granting write access to a repository.
by Bitbucket 27 Nov '12
by Bitbucket 27 Nov '12
27 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/51ffca8538ff/
changeset: 51ffca8538ff
user: inithello
date: 2012-11-27 19:53:17
summary: Fixed error when granting write access to a repository.
affected #: 1 file
diff -r 2aae11d62620b6c839b513d68a06d2cc57c5fa21 -r 51ffca8538ff769f54f379d6e070494fc2c14833 lib/galaxy/webapps/community/model/__init__.py
--- a/lib/galaxy/webapps/community/model/__init__.py
+++ b/lib/galaxy/webapps/community/model/__init__.py
@@ -143,7 +143,7 @@
if username not in allow_push:
allow_push.append( username )
allow_push = '%s\n' % ','.join( allow_push )
- repo = hg.repository( ui.ui(), path=self.repo_path )
+ repo = hg.repository( ui.ui(), path=self.repo_path( app ) )
# Why doesn't the following work?
#repo.ui.setconfig( 'web', 'allow_push', allow_push )
lines = repo.opener( 'hgrc', 'rb' ).readlines()
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

27 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/2aae11d62620/
changeset: 2aae11d62620
user: natefoo
date: 2012-11-27 19:50:54
summary: Split job limits on ' = ' rather than '='.
affected #: 1 file
diff -r 4a95ae9a26d96f0dc9a0fe3b083a2c7b99b0466b -r 2aae11d62620b6c839b513d68a06d2cc57c5fa21 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -224,8 +224,8 @@
try:
job_limits = global_conf_parser.items( 'galaxy:job_limits' )
for k, v in job_limits:
- # ConfigParser considers the first colon to be the delimiter, undo this behavior
- more_k, v = v.split('=', 1)
+ # Since the URL contains a colon and possibly an equals sign, consider ' = ' the delimiter
+ more_k, v = v.split(' = ', 1)
k = '%s:%s' % (k, more_k.strip())
v = v.strip().rsplit(None, 1)
v[1] = int(v[1])
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

27 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/4a95ae9a26d9/
changeset: 4a95ae9a26d9
user: natefoo
date: 2012-11-27 18:18:08
summary: Handle invalid job ids in the drmaa runner.
affected #: 1 file
diff -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff -r 4a95ae9a26d96f0dc9a0fe3b083a2c7b99b0466b lib/galaxy/jobs/runners/drmaa.py
--- a/lib/galaxy/jobs/runners/drmaa.py
+++ b/lib/galaxy/jobs/runners/drmaa.py
@@ -293,6 +293,7 @@
galaxy_job_id = drm_job_state.job_wrapper.job_id
old_state = drm_job_state.old_state
try:
+ assert job_id not in ( None, 'None' ), 'Invalid job id: %s' % job_id
state = self.ds.jobStatus( job_id )
# InternalException was reported to be necessary on some DRMs, but
# this could cause failures to be detected as completion! Please
@@ -308,7 +309,7 @@
continue
except Exception, e:
# so we don't kill the monitor thread
- log.exception("(%s/%s) Unable to check job status" % ( galaxy_job_id, job_id ) )
+ log.exception("(%s/%s) Unable to check job status: %s" % ( galaxy_job_id, job_id, str( e ) ) )
log.warning("(%s/%s) job will now be errored" % ( galaxy_job_id, job_id ) )
drm_job_state.fail_message = "Cluster could not complete job"
self.work_queue.put( ( 'fail', drm_job_state ) )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0