galaxy-commits
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/2a7b890c09c1/
Changeset: 2a7b890c09c1
Branch: next-stable
User: dannon
Date: 2014-05-29 23:23:51
Summary: Improve assertion logging for upload_store mismatch
Affected #: 1 file
diff -r b8fef1dfa8e8eb5afdeaa96236a0a9990f7c532b -r 2a7b890c09c1fb677378e099a5f0637c2036b310 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -498,7 +498,7 @@
# Check that the file is in the right location
local_filename = os.path.abspath( value['path'] )
assert local_filename.startswith( upload_store ), \
- "Filename provided by nginx is not in correct directory"
+ "Filename provided by nginx (%s) is not in correct directory (%s)" % (local_filename, upload_store)
value = dict(
filename=value["name"],
local_filename=local_filename
https://bitbucket.org/galaxy/galaxy-central/commits/3409e283965f/
Changeset: 3409e283965f
User: dannon
Date: 2014-05-29 23:24:03
Summary: Merge.
Affected #: 1 file
diff -r 675be4213255a51c84ff52973bc199fa6d2152a1 -r 3409e283965f58ec4bf96594f980a8e6ee2cb058 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -498,7 +498,7 @@
# Check that the file is in the right location
local_filename = os.path.abspath( value['path'] )
assert local_filename.startswith( upload_store ), \
- "Filename provided by nginx is not in correct directory"
+ "Filename provided by nginx (%s) is not in correct directory (%s)" % (local_filename, upload_store)
value = dict(
filename=value["name"],
local_filename=local_filename
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/b8fef1dfa8e8/
Changeset: b8fef1dfa8e8
Branch: next-stable
User: carlfeberhard
Date: 2014-05-29 22:25:44
Summary: Silence log warnings when class names are not found in the visualizations registry applicability tests
Affected #: 1 file
diff -r 3edca70df09ded514c1e6a46701372638c4596a9 -r b8fef1dfa8e8eb5afdeaa96236a0a9990f7c532b lib/galaxy/visualization/registry.py
--- a/lib/galaxy/visualization/registry.py
+++ b/lib/galaxy/visualization/registry.py
@@ -243,10 +243,11 @@
datatype_class_name = test_result
test_result = trans.app.datatypes_registry.get_datatype_class_by_name( datatype_class_name )
if not test_result:
- # warn if can't find class, but continue (with other tests)
- log.warn( 'visualizations_registry cannot find class (%s)' +
- ' for applicability test on: %s, id: %s', datatype_class_name,
- target_object, getattr( target_object, 'id', '' ) )
+ # but continue (with other tests) if can't find class by that name
+ #if self.debug:
+ # log.warn( 'visualizations_registry cannot find class (%s)' +
+ # ' for applicability test on: %s, id: %s', datatype_class_name,
+ # target_object, getattr( target_object, 'id', '' ) )
continue
#NOTE: tests are OR'd, if any test passes - the visualization can be applied
https://bitbucket.org/galaxy/galaxy-central/commits/675be4213255/
Changeset: 675be4213255
User: carlfeberhard
Date: 2014-05-29 22:25:59
Summary: merge
Affected #: 1 file
diff -r 34d179b55c859f9c669c73733bde2684cf9d4a9f -r 675be4213255a51c84ff52973bc199fa6d2152a1 lib/galaxy/visualization/registry.py
--- a/lib/galaxy/visualization/registry.py
+++ b/lib/galaxy/visualization/registry.py
@@ -243,10 +243,11 @@
datatype_class_name = test_result
test_result = trans.app.datatypes_registry.get_datatype_class_by_name( datatype_class_name )
if not test_result:
- # warn if can't find class, but continue (with other tests)
- log.warn( 'visualizations_registry cannot find class (%s)' +
- ' for applicability test on: %s, id: %s', datatype_class_name,
- target_object, getattr( target_object, 'id', '' ) )
+ # but continue (with other tests) if can't find class by that name
+ #if self.debug:
+ # log.warn( 'visualizations_registry cannot find class (%s)' +
+ # ' for applicability test on: %s, id: %s', datatype_class_name,
+ # target_object, getattr( target_object, 'id', '' ) )
continue
#NOTE: tests are OR'd, if any test passes - the visualization can be applied
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e69024c183a7/
Changeset: e69024c183a7
User: jmchilton
Date: 2014-05-29 07:15:38
Summary: Unit tests for RunnerParams.
Small changes to make this easier - mostly just defining constants.
Affected #: 2 files
diff -r cc0f9182fcb0b18730dd79c6889de4c8a58d4446 -r e69024c183a7403bae7b0801faa8f0db5fc1e5c0 lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -24,20 +24,27 @@
STOP_SIGNAL = object()
+JOB_RUNNER_PARAMETER_UNKNOWN_MESSAGE = "Invalid job runner parameter for this plugin: %s"
+JOB_RUNNER_PARAMETER_MAP_PROBLEM_MESSAGE = "Job runner parameter '%s' value '%s' could not be converted to the correct type"
+JOB_RUNNER_PARAMETER_VALIDATION_FAILED_MESSAGE = "Job runner parameter %s failed validation"
+
+
class RunnerParams( object ):
def __init__( self, specs=None, params=None ):
self.specs = specs or dict()
self.params = params or dict()
for name, value in self.params.items():
- assert name in self.specs, 'Invalid job runner parameter for this plugin: %s' % name
+ assert name in self.specs, JOB_RUNNER_PARAMETER_UNKNOWN_MESSAGE % name
if 'map' in self.specs[ name ]:
try:
self.params[ name ] = self.specs[ name ][ 'map' ]( value )
- except Exception, e:
- raise Exception( 'Job runner parameter "%s" value "%s" could not be converted to the correct type: %s' % ( name, value, e ) )
+ except Exception:
+ message = JOB_RUNNER_PARAMETER_MAP_PROBLEM_MESSAGE % ( name, value )
+ log.exception(message)
+ raise Exception( message )
if 'valid' in self.specs[ name ]:
- assert self.specs[ name ][ 'valid' ]( value ), 'Job runner parameter %s failed validation' % name
+ assert self.specs[ name ][ 'valid' ]( value ), JOB_RUNNER_PARAMETER_VALIDATION_FAILED_MESSAGE % name
def __getattr__( self, name ):
return self.params.get( name, self.specs[ name ][ 'default' ] )
@@ -46,13 +53,15 @@
class BaseJobRunner( object ):
+ DEFAULT_SPECS = dict( recheck_missing_job_retries=dict( map=int, valid=lambda x: x >= 0, default=0 ) )
+
def __init__( self, app, nworkers, **kwargs ):
"""Start the job runner
"""
self.app = app
self.sa_session = app.model.context
self.nworkers = nworkers
- runner_param_specs = dict( recheck_missing_job_retries=dict( map=int, valid=lambda x: x >= 0, default=0 ) )
+ runner_param_specs = self.DEFAULT_SPECS.copy()
if 'runner_param_specs' in kwargs:
runner_param_specs.update( kwargs.pop( 'runner_param_specs' ) )
if kwargs:
diff -r cc0f9182fcb0b18730dd79c6889de4c8a58d4446 -r e69024c183a7403bae7b0801faa8f0db5fc1e5c0 test/unit/jobs/test_runner_params.py
--- /dev/null
+++ b/test/unit/jobs/test_runner_params.py
@@ -0,0 +1,48 @@
+from galaxy.jobs import runners
+
+
+def test_default_specs():
+ # recheck_missing_job_retries is integer >= 0
+ params = runners.RunnerParams( specs=runners.BaseJobRunner.DEFAULT_SPECS, params=dict( recheck_missing_job_retries="1" ) )
+ assert params.recheck_missing_job_retries == 1
+ assert params["recheck_missing_job_retries"] == 1
+
+ exception_raised = False
+ try:
+ runners.RunnerParams( specs=runners.BaseJobRunner.DEFAULT_SPECS, params=dict( recheck_missing_job_retries=-1 ) )
+ except Exception:
+ exception_raised = True
+ assert exception_raised
+
+
+def test_missing_parameter():
+ exception = None
+ try:
+ runners.RunnerParams( specs={}, params=dict( foo="bar" ) )
+ except Exception as e:
+ exception = e
+ assert exception.message == runners.JOB_RUNNER_PARAMETER_UNKNOWN_MESSAGE % "foo"
+
+
+def test_invalid_parameter():
+ exception = None
+ try:
+ runners.RunnerParams( specs=dict( foo=dict( valid=lambda x: x != "bar", defualt="baz" ) ), params=dict( foo="bar" ) )
+ except Exception as e:
+ exception = e
+ assert exception.message == runners.JOB_RUNNER_PARAMETER_VALIDATION_FAILED_MESSAGE % "foo"
+
+
+def test_map_problem():
+ exception = None
+ try:
+ runners.RunnerParams( specs=dict( foo=dict( map=lambda x: 1 / 0, default="baz" ) ), params=dict( foo="bar" ) )
+ except Exception as e:
+ exception = e
+ assert exception.message == runners.JOB_RUNNER_PARAMETER_MAP_PROBLEM_MESSAGE % ( "foo", "bar" )
+
+
+def test_param_default():
+ runner_params = runners.RunnerParams( specs=dict( foo=dict( default="baz" ) ), params={} )
+ assert runner_params["foo"] == "baz"
+ assert runner_params.foo == "baz"
https://bitbucket.org/galaxy/galaxy-central/commits/34d179b55c85/
Changeset: 34d179b55c85
User: jmchilton
Date: 2014-05-29 07:15:38
Summary: Generalize RunnerParams.
Create ParamsWithSpecs class in galaxy.util. If we are going to build out specs for all LWR client parameters - would be nice if they could be reused on the LWR server side where relevant (e.g. AMQP connection parameters).
Slightly modified implementation that extends collections.defaultdict so that operations like iterating over parameters, fetching keys, etc... are available so LWR runner param handling code can look like destination param handling code.
Affected #: 2 files
diff -r e69024c183a7403bae7b0801faa8f0db5fc1e5c0 -r 34d179b55c859f9c669c73733bde2684cf9d4a9f lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -16,6 +16,7 @@
from galaxy import model
from galaxy.util import DATABASE_MAX_STRING_SIZE, shrink_stream_by_size
from galaxy.util import in_directory
+from galaxy.util import ParamsWithSpecs
from galaxy.jobs.runners.util.job_script import job_script
from galaxy.jobs.runners.util.env import env_to_statement
@@ -29,27 +30,16 @@
JOB_RUNNER_PARAMETER_VALIDATION_FAILED_MESSAGE = "Job runner parameter %s failed validation"
-class RunnerParams( object ):
+class RunnerParams( ParamsWithSpecs ):
- def __init__( self, specs=None, params=None ):
- self.specs = specs or dict()
- self.params = params or dict()
- for name, value in self.params.items():
- assert name in self.specs, JOB_RUNNER_PARAMETER_UNKNOWN_MESSAGE % name
- if 'map' in self.specs[ name ]:
- try:
- self.params[ name ] = self.specs[ name ][ 'map' ]( value )
- except Exception:
- message = JOB_RUNNER_PARAMETER_MAP_PROBLEM_MESSAGE % ( name, value )
- log.exception(message)
- raise Exception( message )
- if 'valid' in self.specs[ name ]:
- assert self.specs[ name ][ 'valid' ]( value ), JOB_RUNNER_PARAMETER_VALIDATION_FAILED_MESSAGE % name
+ def _param_unknown_error( self, name ):
+ raise Exception( JOB_RUNNER_PARAMETER_UNKNOWN_MESSAGE % name )
- def __getattr__( self, name ):
- return self.params.get( name, self.specs[ name ][ 'default' ] )
+ def _param_map_error( self, name, value ):
+ raise Exception( JOB_RUNNER_PARAMETER_MAP_PROBLEM_MESSAGE % ( name, value ) )
- __getitem__ = __getattr__
+ def _param_vaildation_error( self, name, value ):
+ raise Exception( JOB_RUNNER_PARAMETER_VALIDATION_FAILED_MESSAGE % name )
class BaseJobRunner( object ):
diff -r e69024c183a7403bae7b0801faa8f0db5fc1e5c0 -r 34d179b55c859f9c669c73733bde2684cf9d4a9f lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -7,6 +7,7 @@
from __future__ import absolute_import
import binascii
+import collections
import errno
import grp
import json
@@ -693,6 +694,44 @@
def string_to_object( s ):
return pickle.loads( binascii.unhexlify( s ) )
+
+class ParamsWithSpecs( collections.defaultdict ):
+ """
+ """
+
+ def __init__( self, specs=None, params=None ):
+ self.specs = specs or dict()
+ self.params = params or dict()
+ for name, value in self.params.items():
+ if name not in self.specs:
+ self._param_unknown_error( name )
+ if 'map' in self.specs[ name ]:
+ try:
+ self.params[ name ] = self.specs[ name ][ 'map' ]( value )
+ except Exception:
+ self._param_map_error( name, value )
+ if 'valid' in self.specs[ name ]:
+ if not self.specs[ name ][ 'valid' ]( value ):
+ self._param_vaildation_error( name, value )
+
+ self.update( self.params )
+
+ def __missing__( self, name ):
+ return self.specs[ name ][ 'default' ]
+
+ def __getattr__( self, name ):
+ return self[ name ]
+
+ def _param_unknown_error( self, name ):
+ raise NotImplementedError()
+
+ def _param_map_error( self, name, value ):
+ raise NotImplementedError()
+
+ def _param_vaildation_error( self, name, value ):
+ raise NotImplementedError()
+
+
def compare_urls( url1, url2, compare_scheme=True, compare_hostname=True, compare_path=True ):
url1 = urlparse( url1 )
url2 = urlparse( url2 )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3edca70df09d/
Changeset: 3edca70df09d
Branch: next-stable
User: jmchilton
Date: 2014-05-29 04:14:52
Summary: More MQ-LWR documentation.
Mention pycurl dependency when using LWR client in this fashion.
Affected #: 1 file
diff -r eed45027e219df9c4fc87fcfc788380ecc500b1a -r 3edca70df09ded514c1e6a46701372638c4596a9 job_conf.xml.sample_advanced
--- a/job_conf.xml.sample_advanced
+++ b/job_conf.xml.sample_advanced
@@ -204,6 +204,10 @@
using message queues (in the more traditional mode Galaxy sends
files to and pull files from the LWR - this is obviously less
appropriate when using a message queue).
+
+ The default_file_action currently requires pycurl be available
+ to Galaxy (presumably in its virtualenv). Making this dependency
+ optional is an open task.
--><param id="default_file_action">remote_transfer</param></destination>
https://bitbucket.org/galaxy/galaxy-central/commits/cc0f9182fcb0/
Changeset: cc0f9182fcb0
User: jmchilton
Date: 2014-05-29 04:15:08
Summary: Merge latest next-stable.
Affected #: 1 file
diff -r f415f6c5ecf22720f5bc8d8ae983cbc713831890 -r cc0f9182fcb0b18730dd79c6889de4c8a58d4446 job_conf.xml.sample_advanced
--- a/job_conf.xml.sample_advanced
+++ b/job_conf.xml.sample_advanced
@@ -204,6 +204,10 @@
using message queues (in the more traditional mode Galaxy sends
files to and pull files from the LWR - this is obviously less
appropriate when using a message queue).
+
+ The default_file_action currently requires pycurl be available
+ to Galaxy (presumably in its virtualenv). Making this dependency
+ optional is an open task.
--><param id="default_file_action">remote_transfer</param></destination>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/fa67fbfa58e1/
Changeset: fa67fbfa58e1
User: jmchilton
Date: 2014-05-29 03:25:53
Summary: Bugfix for a06c6c9.
With improved testing :).
Affected #: 2 files
diff -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 -r fa67fbfa58e16ad2f1a18c9e53fac2b02c7ab7f0 lib/galaxy/dataset_collections/__init__.py
--- a/lib/galaxy/dataset_collections/__init__.py
+++ b/lib/galaxy/dataset_collections/__init__.py
@@ -147,7 +147,10 @@
):
assert source == "hdca" # for now
source_hdca = self.__get_history_collection_instance( trans, encoded_source_id )
- parent.add_dataset_collection( source_hdca.copy() )
+ new_hdca = source_hdca.copy()
+ parent.add_dataset_collection( new_hdca )
+ trans.sa_session.add( new_hdca )
+ trans.sa_session.flush()
return source_hdca
def _set_from_dict( self, trans, dataset_collection_instance, new_data ):
diff -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 -r fa67fbfa58e16ad2f1a18c9e53fac2b02c7ab7f0 test/api/test_history_contents.py
--- a/test/api/test_history_contents.py
+++ b/test/api/test_history_contents.py
@@ -150,8 +150,10 @@
source='hdca',
content=hdca_id,
)
+ assert len( self._get( "histories/%s/contents/dataset_collections" % second_history_id ).json() ) == 0
create_response = self._post( "histories/%s/contents/dataset_collections" % second_history_id, create_data )
self.__check_create_collection_response( create_response )
+ assert len( self._get( "histories/%s/contents/dataset_collections" % second_history_id ).json() ) == 1
def __check_create_collection_response( self, response ):
self._assert_status_code_is( response, 200 )
https://bitbucket.org/galaxy/galaxy-central/commits/f415f6c5ecf2/
Changeset: f415f6c5ecf2
User: jmchilton
Date: 2014-05-29 03:25:53
Summary: More extract functional tests.
Add test for extracting copied, mapped datasets and copied input dataset collections from history.
Affected #: 2 files
diff -r fa67fbfa58e16ad2f1a18c9e53fac2b02c7ab7f0 -r f415f6c5ecf22720f5bc8d8ae983cbc713831890 lib/galaxy/workflow/extract.py
--- a/lib/galaxy/workflow/extract.py
+++ b/lib/galaxy/workflow/extract.py
@@ -2,6 +2,7 @@
histories.
"""
from galaxy.util.odict import odict
+from galaxy import exceptions
from galaxy import model
from galaxy.tools.parameters.basic import (
DataToolParameter,
@@ -88,6 +89,8 @@
for hid in dataset_collection_ids:
step = model.WorkflowStep()
step.type = 'data_collection_input'
+ if hid not in summary.collection_types:
+ raise exceptions.RequestParameterInvalidException( "hid %s does not appear to be a collection" % hid )
collection_type = summary.collection_types[ hid ]
step.tool_inputs = dict( name="Input Dataset Collection", collection_type=collection_type )
hid_to_output_pair[ hid ] = ( step, 'output' )
diff -r fa67fbfa58e16ad2f1a18c9e53fac2b02c7ab7f0 -r f415f6c5ecf22720f5bc8d8ae983cbc713831890 test/api/test_workflows.py
--- a/test/api/test_workflows.py
+++ b/test/api/test_workflows.py
@@ -106,12 +106,7 @@
offset = 0
old_contents = self._get( "histories/%s/contents" % old_history_id ).json()
for old_dataset in old_contents:
- payload = dict(
- source="hda",
- content=old_dataset["id"]
- )
- response = self._post( "histories/%s/contents/datasets" % history_id, payload )
- self._assert_status_code_is( response, 200 )
+ self.__copy_content_to_history( history_id, old_dataset )
new_contents = self._get( "histories/%s/contents" % history_id ).json()
input_hids = map( lambda c: c[ "hid" ], new_contents[ (offset + 0):(offset + 2) ] )
cat1_job_id = self.__job_id( history_id, new_contents[ (offset + 2) ][ "id" ] )
@@ -186,40 +181,33 @@
@skip_without_tool( "random_lines1" )
def test_extract_mapping_workflow_from_history( self ):
history_id = self.dataset_populator.new_history()
- hdca = self.dataset_collection_populator.create_pair_in_history( history_id, contents=["1 2 3\n4 5 6", "7 8 9\n10 11 10"] ).json()
- hdca_id = hdca[ "id" ]
- inputs1 = {
- "input|__collection_multirun__": hdca_id,
- "num_lines": 2
- }
- implicit_hdca1, job_id1 = self._run_tool_get_collection_and_job_id( history_id, "random_lines1", inputs1 )
- inputs2 = {
- "input|__collection_multirun__": implicit_hdca1[ "id" ],
- "num_lines": 1
- }
- _, job_id2 = self._run_tool_get_collection_and_job_id( history_id, "random_lines1", inputs2 )
+ hdca, job_id1, job_id2 = self.__run_random_lines_mapped_over_pair( history_id )
downloaded_workflow = self._extract_and_download_workflow(
from_history_id=history_id,
dataset_collection_ids=dumps( [ hdca[ "hid" ] ] ),
job_ids=dumps( [ job_id1, job_id2 ] ),
workflow_name="test import from mapping history",
)
- # Assert workflow is input connected to a tool step with one output
- # connected to another tool step.
- assert len( downloaded_workflow[ "steps" ] ) == 3
- collect_step_idx = self._assert_first_step_is_paired_input( downloaded_workflow )
- tool_steps = self._get_steps_of_type( downloaded_workflow, "tool", expected_len=2 )
- tool_step_idxs = []
- tool_input_step_idxs = []
- for tool_step in tool_steps:
- self._assert_has_key( tool_step[ "input_connections" ], "input" )
- input_step_idx = tool_step[ "input_connections" ][ "input" ][ "id" ]
- tool_step_idxs.append( tool_step[ "id" ] )
- tool_input_step_idxs.append( input_step_idx )
+ self.__assert_looks_like_randomlines_mapping_workflow( downloaded_workflow )
- assert collect_step_idx not in tool_step_idxs
- assert tool_input_step_idxs[ 0 ] == collect_step_idx
- assert tool_input_step_idxs[ 1 ] == tool_step_idxs[ 0 ]
+ def test_extract_copied_mapping_from_history( self ):
+ old_history_id = self.dataset_populator.new_history()
+ hdca, job_id1, job_id2 = self.__run_random_lines_mapped_over_pair( old_history_id )
+
+ history_id = self.dataset_populator.new_history()
+ old_contents = self._get( "histories/%s/contents" % old_history_id ).json()
+ for old_content in old_contents:
+ self.__copy_content_to_history( history_id, old_content )
+ # API test is somewhat contrived since there is no good way
+ # to retrieve job_id1, job_id2 like this for copied dataset
+ # collections I don't think.
+ downloaded_workflow = self._extract_and_download_workflow(
+ from_history_id=history_id,
+ dataset_collection_ids=dumps( [ hdca[ "hid" ] ] ),
+ job_ids=dumps( [ job_id1, job_id2 ] ),
+ workflow_name="test import from history",
+ )
+ self.__assert_looks_like_randomlines_mapping_workflow( downloaded_workflow )
@skip_without_tool( "random_lines1" )
@skip_without_tool( "multi_data_param" )
@@ -259,6 +247,56 @@
reduction_step_input = reduction_step[ "input_connections" ][ "f1" ]
assert reduction_step_input[ "id"] == random_lines_map_step[ "id" ]
+ def __copy_content_to_history( self, history_id, content ):
+ if content[ "history_content_type" ] == "dataset":
+ payload = dict(
+ source="hda",
+ content=content["id"]
+ )
+ response = self._post( "histories/%s/contents/datasets" % history_id, payload )
+
+ else:
+ payload = dict(
+ source="hdca",
+ content=content["id"]
+ )
+ response = self._post( "histories/%s/contents/dataset_collections" % history_id, payload )
+ self._assert_status_code_is( response, 200 )
+ return response.json()
+
+ def __run_random_lines_mapped_over_pair( self, history_id ):
+ hdca = self.dataset_collection_populator.create_pair_in_history( history_id, contents=["1 2 3\n4 5 6", "7 8 9\n10 11 10"] ).json()
+ hdca_id = hdca[ "id" ]
+ inputs1 = {
+ "input|__collection_multirun__": hdca_id,
+ "num_lines": 2
+ }
+ implicit_hdca1, job_id1 = self._run_tool_get_collection_and_job_id( history_id, "random_lines1", inputs1 )
+ inputs2 = {
+ "input|__collection_multirun__": implicit_hdca1[ "id" ],
+ "num_lines": 1
+ }
+ _, job_id2 = self._run_tool_get_collection_and_job_id( history_id, "random_lines1", inputs2 )
+ return hdca, job_id1, job_id2
+
+ def __assert_looks_like_randomlines_mapping_workflow( self, downloaded_workflow ):
+ # Assert workflow is input connected to a tool step with one output
+ # connected to another tool step.
+ assert len( downloaded_workflow[ "steps" ] ) == 3
+ collect_step_idx = self._assert_first_step_is_paired_input( downloaded_workflow )
+ tool_steps = self._get_steps_of_type( downloaded_workflow, "tool", expected_len=2 )
+ tool_step_idxs = []
+ tool_input_step_idxs = []
+ for tool_step in tool_steps:
+ self._assert_has_key( tool_step[ "input_connections" ], "input" )
+ input_step_idx = tool_step[ "input_connections" ][ "input" ][ "id" ]
+ tool_step_idxs.append( tool_step[ "id" ] )
+ tool_input_step_idxs.append( input_step_idx )
+
+ assert collect_step_idx not in tool_step_idxs
+ assert tool_input_step_idxs[ 0 ] == collect_step_idx
+ assert tool_input_step_idxs[ 1 ] == tool_step_idxs[ 0 ]
+
def _run_tool_get_collection_and_job_id( self, history_id, tool_id, inputs ):
run_output1 = self.dataset_populator.run_tool(
tool_id=tool_id,
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/f8c6577665c3/
Changeset: f8c6577665c3
User: jgoecks
Date: 2014-05-28 23:20:15
Summary: Enhance TabularChunkedView to load first chunk from server if it's not already available. This enables TabularChunkedView to be loaded dynamically rather than relying on bootstrapped data.
Affected #: 1 file
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r f8c6577665c383645e934629784ece516b498d9b static/scripts/mvc/data.js
--- a/static/scripts/mvc/data.js
+++ b/static/scripts/mvc/data.js
@@ -145,17 +145,24 @@
header_row.append('<th>' + column_names.join('</th><th>') + '</th>');
}
- // Add first chunk.
- var first_chunk = this.model.get('first_data_chunk');
+ // Render first chunk.
+ var self = this,
+ first_chunk = this.model.get('first_data_chunk');
if (first_chunk) {
+ // First chunk is bootstrapped, so render now.
this._renderChunk(first_chunk);
}
+ else {
+ // No bootstrapping, so get first chunk and then render.
+ $.when(self.model.get_next_chunk()).then(function(result) {
+ self._renderChunk(result);
+ });
+ }
// -- Show new chunks during scrolling. --
- var self = this,
- // Flag to ensure that only one chunk is loaded at a time.
- loading_chunk = false;
+ // Flag to ensure that only one chunk is loaded at a time.
+ var loading_chunk = false;
// Set up chunk loading when scrolling using the scrolling element.
this.scroll_elt.scroll(function() {
https://bitbucket.org/galaxy/galaxy-central/commits/85aa79e9ab9a/
Changeset: 85aa79e9ab9a
User: jgoecks
Date: 2014-05-28 23:21:04
Summary: Automated merge
Affected #: 19 files
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/galaxy/dataset_collections/__init__.py
--- a/lib/galaxy/dataset_collections/__init__.py
+++ b/lib/galaxy/dataset_collections/__init__.py
@@ -138,6 +138,18 @@
changed = self._set_from_dict( trans, dataset_collection_instance, payload )
return changed
+ def copy(
+ self,
+ trans,
+ parent, # PRECONDITION: security checks on ability to add to parent occurred during load.
+ source,
+ encoded_source_id,
+ ):
+ assert source == "hdca" # for now
+ source_hdca = self.__get_history_collection_instance( trans, encoded_source_id )
+ parent.add_dataset_collection( source_hdca.copy() )
+ return source_hdca
+
def _set_from_dict( self, trans, dataset_collection_instance, new_data ):
# Blatantly stolen from UsesHistoryDatasetAssociationMixin.set_hda_from_dict.
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/galaxy/webapps/galaxy/api/history_contents.py
--- a/lib/galaxy/webapps/galaxy/api/history_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/history_contents.py
@@ -292,9 +292,28 @@
return hda_dict
def __create_dataset_collection( self, trans, history, payload, **kwd ):
- create_params = api_payload_to_create_params( payload )
+ source = kwd.get("source", "new_collection")
service = trans.app.dataset_collections_service
- dataset_collection_instance = service.create( trans, parent=history, **create_params )
+ if source == "new_collection":
+ create_params = api_payload_to_create_params( payload )
+ dataset_collection_instance = service.create(
+ trans,
+ parent=history,
+ **create_params
+ )
+ elif source == "hdca":
+ content = payload.get( 'content', None )
+ if content is None:
+ raise exceptions.RequestParameterMissingException( "'content' id of target to copy is missing" )
+ dataset_collection_instance = service.copy(
+ trans=trans,
+ parent=history,
+ source="hdca",
+ encoded_source_id=content,
+ )
+ else:
+ message = "Invalid 'source' parameter in request %s" % source
+ raise exceptions.RequestParameterInvalidException(message)
return self.__collection_dict( trans, dataset_collection_instance, view="element" )
@expose_api_anonymous
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -3120,12 +3120,12 @@
options_dict = hg_util.get_mercurial_default_options_dict( 'diff' )
# Not quite sure if the following settings make any difference, but with a combination of them and the size check on each
# diff, we don't run out of memory when viewing the changelog of the cisortho2 repository on the test tool shed.
- options_dict[ 'maxfile' ] = suc.MAXDIFFSIZE
- options_dict[ 'maxtotal' ] = suc.MAXDIFFSIZE
+ options_dict[ 'maxfile' ] = basic_util.MAXDIFFSIZE
+ options_dict[ 'maxtotal' ] = basic_util.MAXDIFFSIZE
diffopts = mdiff.diffopts( **options_dict )
for diff in patch.diff( repo, node1=ctx_parent.node(), node2=ctx.node(), opts=diffopts ):
- if len( diff ) > suc.MAXDIFFSIZE:
- diff = util.shrink_string_by_size( diff, suc.MAXDIFFSIZE )
+ if len( diff ) > basic_util.MAXDIFFSIZE:
+ diff = util.shrink_string_by_size( diff, basic_util.MAXDIFFSIZE )
diffs.append( basic_util.to_html_string( diff ) )
modified, added, removed, deleted, unknown, ignored, clean = repo.status( node1=ctx_parent.node(), node2=ctx.node() )
anchors = modified + added + removed + deleted + unknown + ignored + clean
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/galaxy_install/install_manager.py
--- a/lib/tool_shed/galaxy_install/install_manager.py
+++ b/lib/tool_shed/galaxy_install/install_manager.py
@@ -1,5 +1,7 @@
import logging
import os
+import sys
+import traceback
from galaxy import eggs
@@ -11,7 +13,6 @@
from tool_shed.util import tool_dependency_util
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
from tool_shed.galaxy_install.tool_dependencies.recipe.env_file_builder import EnvFileBuilder
from tool_shed.galaxy_install.tool_dependencies.recipe.install_environment import InstallEnvironment
from tool_shed.galaxy_install.tool_dependencies.recipe.recipe_manager import StepManager
@@ -25,6 +26,10 @@
class InstallManager( object ):
+ def format_traceback( self ):
+ ex_type, ex, tb = sys.exc_info()
+ return ''.join( traceback.format_tb( tb ) )
+
def get_tool_shed_repository_install_dir( self, app, tool_shed_repository ):
return os.path.abspath( tool_shed_repository.repo_files_directory( app ) )
@@ -112,7 +117,7 @@
log.exception( 'Error installing tool dependency %s version %s.', str( tool_dependency.name ), str( tool_dependency.version ) )
# Since there was an installation error, update the tool dependency status to Error. The remove_installation_path option must
# be left False here.
- error_message = '%s\n%s' % ( td_common_util.format_traceback(), str( e ) )
+ error_message = '%s\n%s' % ( self.format_traceback(), str( e ) )
tool_dependency = tool_dependency_util.handle_tool_dependency_installation_error( app,
tool_dependency,
error_message,
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/galaxy_install/tool_dependencies/env_manager.py
--- /dev/null
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/env_manager.py
@@ -0,0 +1,147 @@
+import logging
+import os
+import sys
+from tool_shed.util import common_util
+import tool_shed.util.shed_util_common as suc
+
+log = logging.getLogger( __name__ )
+
+
+class EnvManager( object ):
+
+ def __init__( self, app ):
+ self.app = app
+
+ def create_env_var_dict( self, elem, install_environment ):
+ env_var_name = elem.get( 'name', 'PATH' )
+ env_var_action = elem.get( 'action', 'prepend_to' )
+ env_var_text = None
+ tool_dependency_install_dir = install_environment.install_dir
+ tool_shed_repository_install_dir = install_environment.tool_shed_repository_install_dir
+ if elem.text and elem.text.find( 'REPOSITORY_INSTALL_DIR' ) >= 0:
+ if tool_shed_repository_install_dir and elem.text.find( '$REPOSITORY_INSTALL_DIR' ) != -1:
+ env_var_text = elem.text.replace( '$REPOSITORY_INSTALL_DIR', tool_shed_repository_install_dir )
+ return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ else:
+ env_var_text = elem.text.replace( '$REPOSITORY_INSTALL_DIR', tool_dependency_install_dir )
+ return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ if elem.text and elem.text.find( 'INSTALL_DIR' ) >= 0:
+ if tool_dependency_install_dir:
+ env_var_text = elem.text.replace( '$INSTALL_DIR', tool_dependency_install_dir )
+ return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ else:
+ env_var_text = elem.text.replace( '$INSTALL_DIR', tool_shed_repository_install_dir )
+ return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ if elem.text:
+ # Allow for environment variables that contain neither REPOSITORY_INSTALL_DIR nor INSTALL_DIR
+ # since there may be command line parameters that are tuned for a Galaxy instance. Allowing them
+ # to be set in one location rather than being hard coded into each tool config is the best approach.
+ # For example:
+ # <environment_variable name="GATK2_SITE_OPTIONS" action="set_to">
+ # "--num_threads 4 --num_cpu_threads_per_data_thread 3 --phone_home STANDARD"
+ # </environment_variable>
+ return dict( name=env_var_name, action=env_var_action, value=elem.text)
+ return None
+
+ def get_env_shell_file_path( self, installation_directory ):
+ env_shell_file_name = 'env.sh'
+ default_location = os.path.abspath( os.path.join( installation_directory, env_shell_file_name ) )
+ if os.path.exists( default_location ):
+ return default_location
+ for root, dirs, files in os.walk( installation_directory ):
+ for name in files:
+ if name == env_shell_file_name:
+ return os.path.abspath( os.path.join( root, name ) )
+ return None
+
+ def get_env_shell_file_paths( self, elem ):
+ # Currently only the following tag set is supported.
+ # <repository toolshed="http://localhost:9009/" name="package_numpy_1_7" owner="test" changeset_revision="c84c6a8be056">
+ # <package name="numpy" version="1.7.1" />
+ # </repository>
+ env_shell_file_paths = []
+ toolshed = elem.get( 'toolshed', None )
+ repository_name = elem.get( 'name', None )
+ repository_owner = elem.get( 'owner', None )
+ changeset_revision = elem.get( 'changeset_revision', None )
+ if toolshed and repository_name and repository_owner and changeset_revision:
+ # The protocol is not stored, but the port is if it exists.
+ toolshed = common_util.remove_protocol_from_tool_shed_url( toolshed )
+ repository = suc.get_repository_for_dependency_relationship( self.app,
+ toolshed,
+ repository_name,
+ repository_owner,
+ changeset_revision )
+ if repository:
+ for sub_elem in elem:
+ tool_dependency_type = sub_elem.tag
+ tool_dependency_name = sub_elem.get( 'name' )
+ tool_dependency_version = sub_elem.get( 'version' )
+ if tool_dependency_type and tool_dependency_name and tool_dependency_version:
+ # Get the tool_dependency so we can get its installation directory.
+ tool_dependency = None
+ for tool_dependency in repository.tool_dependencies:
+ if tool_dependency.type == tool_dependency_type and \
+ tool_dependency.name == tool_dependency_name and \
+ tool_dependency.version == tool_dependency_version:
+ break
+ if tool_dependency:
+ tool_dependency_key = '%s/%s' % ( tool_dependency_name, tool_dependency_version )
+ installation_directory = tool_dependency.installation_directory( self.app )
+ env_shell_file_path = self.get_env_shell_file_path( installation_directory )
+ if env_shell_file_path:
+ env_shell_file_paths.append( env_shell_file_path )
+ else:
+ error_message = "Skipping tool dependency definition because unable to locate env.sh file for tool dependency "
+ error_message += "type %s, name %s, version %s for repository %s" % \
+ ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ), str( repository.name ) )
+ log.debug( error_message )
+ continue
+ else:
+ error_message = "Skipping tool dependency definition because unable to locate tool dependency "
+ error_message += "type %s, name %s, version %s for repository %s" % \
+ ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ), str( repository.name ) )
+ log.debug( error_message )
+ continue
+ else:
+ error_message = "Skipping invalid tool dependency definition: type %s, name %s, version %s." % \
+ ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ) )
+ log.debug( error_message )
+ continue
+ else:
+ error_message = "Skipping set_environment_for_install definition because unable to locate required installed tool shed repository: "
+ error_message += "toolshed %s, name %s, owner %s, changeset_revision %s." % \
+ ( str( toolshed ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
+ log.debug( error_message )
+ else:
+ error_message = "Skipping invalid set_environment_for_install definition: toolshed %s, name %s, owner %s, changeset_revision %s." % \
+ ( str( toolshed ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
+ log.debug( error_message )
+ return env_shell_file_paths
+
+ def get_env_shell_file_paths_from_setup_environment_elem( self, all_env_shell_file_paths, elem, action_dict ):
+ """
+ Parse an XML tag set to discover all child repository dependency tags and define the path to an env.sh file associated
+ with the repository (this requires the repository dependency to be in an installed state). The received action_dict
+ will be updated with these discovered paths and returned to the caller. This method handles tool dependency definition
+ tag sets <setup_r_environment>, <setup_ruby_environment> and <setup_perl_environment>.
+ """
+ # An example elem is:
+ # <action type="setup_perl_environment">
+ # <repository name="package_perl_5_18" owner="iuc">
+ # <package name="perl" version="5.18.1" />
+ # </repository>
+ # <repository name="package_expat_2_1" owner="iuc" prior_installation_required="True">
+ # <package name="expat" version="2.1.0" />
+ # </repository>
+ # <package>http://search.cpan.org/CPAN/authors/id/T/TO/TODDR/XML-Parser-2.41.tar.gz</package>
+ # <package>http://search.cpan.org/CPAN/authors/id/L/LD/LDS/CGI.pm-3.43.tar.gz</package>
+ # </action>
+ for action_elem in elem:
+ if action_elem.tag == 'repository':
+ env_shell_file_paths = self.get_env_shell_file_paths( action_elem )
+ all_env_shell_file_paths.extend( env_shell_file_paths )
+ if all_env_shell_file_paths:
+ action_dict[ 'env_shell_file_paths' ] = all_env_shell_file_paths
+ action_dict[ 'action_shell_file_paths' ] = env_shell_file_paths
+ return action_dict
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/galaxy_install/tool_dependencies/recipe/install_environment.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/install_environment.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/install_environment.py
@@ -24,11 +24,13 @@
from galaxy.util import shrink_string_by_size
from galaxy.util import unicodify
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
from tool_shed.galaxy_install.tool_dependencies.recipe import asynchronous_reader
+from tool_shed.util import basic_util
+
log = logging.getLogger( __name__ )
+
class InstallEnvironment( object ):
"""Object describing the environment built up as part of the process of building and installing a package."""
@@ -44,7 +46,7 @@
self.tool_shed_repository_install_dir = tool_shed_repository_install_dir
def __call__( self ):
- with settings( warn_only=True, **td_common_util.get_env_var_values( self ) ):
+ with settings( warn_only=True, **basic_util.get_env_var_values( self ) ):
with prefix( self.__setup_environment() ):
yield
@@ -125,7 +127,7 @@
context = app.install_model.context
command = str( cmd )
output = self.handle_complex_command( command )
- self.log_results( cmd, output, os.path.join( self.install_dir, td_common_util.INSTALLATION_LOG ) )
+ self.log_results( cmd, output, os.path.join( self.install_dir, basic_util.INSTALLATION_LOG ) )
stdout = output.stdout
stderr = output.stderr
if len( stdout ) > DATABASE_MAX_STRING_SIZE:
@@ -214,18 +216,18 @@
# Sleep a bit before asking the readers again.
time.sleep( .1 )
current_wait_time = time.time() - start_timer
- if stdout_queue.empty() and stderr_queue.empty() and current_wait_time > td_common_util.NO_OUTPUT_TIMEOUT:
+ if stdout_queue.empty() and stderr_queue.empty() and current_wait_time > basic_util.NO_OUTPUT_TIMEOUT:
err_msg = "\nShutting down process id %s because it generated no output for the defined timeout period of %.1f seconds.\n" % \
- ( pid, td_common_util.NO_OUTPUT_TIMEOUT )
+ ( pid, basic_util.NO_OUTPUT_TIMEOUT )
stderr_reader.lines.append( err_msg )
process_handle.kill()
break
thread_lock.release()
# Wait until each of the threads we've started terminate. The following calls will block each thread
# until it terminates either normally, through an unhandled exception, or until the timeout occurs.
- stdio_thread.join( td_common_util.NO_OUTPUT_TIMEOUT )
- stdout_reader.join( td_common_util.NO_OUTPUT_TIMEOUT )
- stderr_reader.join( td_common_util.NO_OUTPUT_TIMEOUT )
+ stdio_thread.join( basic_util.NO_OUTPUT_TIMEOUT )
+ stdout_reader.join( basic_util.NO_OUTPUT_TIMEOUT )
+ stderr_reader.join( basic_util.NO_OUTPUT_TIMEOUT )
# Close subprocess' file descriptors.
error = self.close_file_descriptor( process_handle.stdout )
error = self.close_file_descriptor( process_handle.stderr )
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
@@ -4,12 +4,17 @@
import stat
from string import Template
import sys
+import tarfile
+import time
+import urllib2
+import zipfile
from galaxy.util import asbool
from galaxy.util.template import fill_template
+from tool_shed.util import basic_util
from tool_shed.util import tool_dependency_util
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
+from tool_shed.galaxy_install.tool_dependencies.env_manager import EnvManager
# TODO: eliminate the use of fabric here.
from galaxy import eggs
@@ -26,6 +31,152 @@
VIRTUALENV_URL = 'https://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.9.1.tar.gz'
+class CompressedFile( object ):
+
+ def __init__( self, file_path, mode='r' ):
+ if tarfile.is_tarfile( file_path ):
+ self.file_type = 'tar'
+ elif zipfile.is_zipfile( file_path ) and not file_path.endswith( '.jar' ):
+ self.file_type = 'zip'
+ self.file_name = os.path.splitext( os.path.basename( file_path ) )[ 0 ]
+ if self.file_name.endswith( '.tar' ):
+ self.file_name = os.path.splitext( self.file_name )[ 0 ]
+ self.type = self.file_type
+ method = 'open_%s' % self.file_type
+ if hasattr( self, method ):
+ self.archive = getattr( self, method )( file_path, mode )
+ else:
+ raise NameError( 'File type %s specified, no open method found.' % self.file_type )
+
+ def extract( self, path ):
+ '''Determine the path to which the archive should be extracted.'''
+ contents = self.getmembers()
+ extraction_path = path
+ if len( contents ) == 1:
+ # The archive contains a single file, return the extraction path.
+ if self.isfile( contents[ 0 ] ):
+ extraction_path = os.path.join( path, self.file_name )
+ if not os.path.exists( extraction_path ):
+ os.makedirs( extraction_path )
+ self.archive.extractall( extraction_path )
+ else:
+ # Get the common prefix for all the files in the archive. If the common prefix ends with a slash,
+ # or self.isdir() returns True, the archive contains a single directory with the desired contents.
+ # Otherwise, it contains multiple files and/or directories at the root of the archive.
+ common_prefix = os.path.commonprefix( [ self.getname( item ) for item in contents ] )
+ if len( common_prefix ) >= 1 and not common_prefix.endswith( os.sep ) and self.isdir( self.getmember( common_prefix ) ):
+ common_prefix += os.sep
+ if common_prefix.endswith( os.sep ):
+ self.archive.extractall( os.path.join( path ) )
+ extraction_path = os.path.join( path, common_prefix )
+ else:
+ extraction_path = os.path.join( path, self.file_name )
+ if not os.path.exists( extraction_path ):
+ os.makedirs( extraction_path )
+ self.archive.extractall( os.path.join( extraction_path ) )
+ return os.path.abspath( extraction_path )
+
+ def getmembers_tar( self ):
+ return self.archive.getmembers()
+
+ def getmembers_zip( self ):
+ return self.archive.infolist()
+
+ def getname_tar( self, item ):
+ return item.name
+
+ def getname_zip( self, item ):
+ return item.filename
+
+ def getmember( self, name ):
+ for member in self.getmembers():
+ if self.getname( member ) == name:
+ return member
+
+ def getmembers( self ):
+ return getattr( self, 'getmembers_%s' % self.type )()
+
+ def getname( self, member ):
+ return getattr( self, 'getname_%s' % self.type )( member )
+
+ def isdir( self, member ):
+ return getattr( self, 'isdir_%s' % self.type )( member )
+
+ def isdir_tar( self, member ):
+ return member.isdir()
+
+ def isdir_zip( self, member ):
+ if member.filename.endswith( os.sep ):
+ return True
+ return False
+
+ def isfile( self, member ):
+ if not self.isdir( member ):
+ return True
+ return False
+
+ def open_tar( self, filepath, mode ):
+ return tarfile.open( filepath, mode, errorlevel=0 )
+
+ def open_zip( self, filepath, mode ):
+ return zipfile.ZipFile( filepath, mode )
+
+ def zipfile_ok( self, path_to_archive ):
+ """
+ This function is a bit pedantic and not functionally necessary. It checks whether there is
+ no file pointing outside of the extraction, because ZipFile.extractall() has some potential
+ security holes. See python zipfile documentation for more details.
+ """
+ basename = os.path.realpath( os.path.dirname( path_to_archive ) )
+ zip_archive = zipfile.ZipFile( path_to_archive )
+ for member in zip_archive.namelist():
+ member_path = os.path.realpath( os.path.join( basename, member ) )
+ if not member_path.startswith( basename ):
+ return False
+ return True
+
+
+class Download( object ):
+
+ def url_download( self, install_dir, downloaded_file_name, download_url, extract=True ):
+ file_path = os.path.join( install_dir, downloaded_file_name )
+ src = None
+ dst = None
+ # Set a timer so we don't sit here forever.
+ start_time = time.time()
+ try:
+ src = urllib2.urlopen( download_url )
+ dst = open( file_path, 'wb' )
+ while True:
+ chunk = src.read( basic_util.CHUNK_SIZE )
+ if chunk:
+ dst.write( chunk )
+ else:
+ break
+ time_taken = time.time() - start_time
+ if time_taken > basic_util.NO_OUTPUT_TIMEOUT:
+ err_msg = 'Downloading from URL %s took longer than the defined timeout period of %.1f seconds.' % \
+ ( str( download_url ), basic_util.NO_OUTPUT_TIMEOUT )
+ raise Exception( err_msg )
+ except Exception, e:
+ err_msg = err_msg = 'Error downloading from URL\n%s:\n%s' % ( str( download_url ), str( e ) )
+ raise Exception( err_msg )
+ finally:
+ if src:
+ src.close()
+ if dst:
+ dst.close()
+ if extract:
+ if tarfile.is_tarfile( file_path ) or ( zipfile.is_zipfile( file_path ) and not file_path.endswith( '.jar' ) ):
+ archive = CompressedFile( file_path )
+ extraction_path = archive.extract( install_dir )
+ else:
+ extraction_path = os.path.abspath( install_dir )
+ else:
+ extraction_path = os.path.abspath( install_dir )
+ return extraction_path
+
+
class RecipeStep( object ):
"""Abstract class that defines a standard format for handling recipe steps when installing packages."""
@@ -42,6 +193,22 @@
def __init__( self ):
self.type = 'assert_directory_executable'
+ def assert_directory_executable( self, full_path ):
+ """
+ Return True if a symbolic link or directory exists and is executable, but if
+ full_path is a file, return False.
+ """
+ if full_path is None:
+ return False
+ if os.path.isfile( full_path ):
+ return False
+ if os.path.isdir( full_path ):
+ # Make sure the owner has execute permission on the directory.
+ # See http://docs.python.org/2/library/stat.html
+ if stat.S_IXUSR & os.stat( full_path )[ stat.ST_MODE ] == 64:
+ return True
+ return False
+
def execute_step( self, app, tool_dependency, package_name, actions, action_dict, filtered_actions, env_file_builder,
install_environment, work_dir, current_dir=None, initial_download=False ):
"""
@@ -53,7 +220,7 @@
full_path = action_dict[ 'full_path' ]
else:
full_path = os.path.join( current_dir, action_dict[ 'full_path' ] )
- if not td_common_util.assert_directory_executable( full_path=full_path ):
+ if not self.assert_directory_executable( full_path=full_path ):
status = app.install_model.ToolDependency.installation_status.ERROR
error_message = 'The path %s is not a directory or is not executable by the owner.' % str( full_path )
tool_dependency = tool_dependency_util.set_tool_dependency_attributes( app,
@@ -66,7 +233,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="assert_executable">$INSTALL_DIR/mira/my_file</action>
if action_elem.text:
- action_dict[ 'full_path' ] = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_dict[ 'full_path' ] = basic_util.evaluate_template( action_elem.text, install_environment )
return action_dict
@@ -75,6 +242,18 @@
def __init__( self ):
self.type = 'assert_directory_exists'
+ def assert_directory_exists( self, full_path ):
+ """
+ Return True if a symbolic link or directory exists, but if full_path is a file,
+ return False. """
+ if full_path is None:
+ return False
+ if os.path.isfile( full_path ):
+ return False
+ if os.path.isdir( full_path ):
+ return True
+ return False
+
def execute_step( self, app, tool_dependency, package_name, actions, action_dict, filtered_actions, env_file_builder,
install_environment, work_dir, current_dir=None, initial_download=False ):
"""
@@ -86,7 +265,7 @@
full_path = action_dict[ 'full_path' ]
else:
full_path = os.path.join( current_dir, action_dict[ 'full_path' ] )
- if not td_common_util.assert_directory_exists( full_path=full_path ):
+ if not self.assert_directory_exists( full_path=full_path ):
status = app.install_model.ToolDependency.installation_status.ERROR
error_message = 'The path %s is not a directory or does not exist.' % str( full_path )
tool_dependency = tool_dependency_util.set_tool_dependency_attributes( app,
@@ -99,7 +278,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="make_directory">$INSTALL_DIR/mira</action>
if action_elem.text:
- action_dict[ 'full_path' ] = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_dict[ 'full_path' ] = basic_util.evaluate_template( action_elem.text, install_environment )
return action_dict
@@ -108,6 +287,22 @@
def __init__( self ):
self.type = 'assert_file_executable'
+ def assert_file_executable( self, full_path ):
+ """
+ Return True if a symbolic link or file exists and is executable, but if full_path
+ is a directory, return False.
+ """
+ if full_path is None:
+ return False
+ if os.path.isdir( full_path ):
+ return False
+ if os.path.exists( full_path ):
+ # Make sure the owner has execute permission on the file.
+ # See http://docs.python.org/2/library/stat.html
+ if stat.S_IXUSR & os.stat( full_path )[ stat.ST_MODE ] == 64:
+ return True
+ return False
+
def execute_step( self, app, tool_dependency, package_name, actions, action_dict, filtered_actions, env_file_builder,
install_environment, work_dir, current_dir=None, initial_download=False ):
"""
@@ -119,7 +314,7 @@
full_path = action_dict[ 'full_path' ]
else:
full_path = os.path.join( current_dir, action_dict[ 'full_path' ] )
- if not td_common_util.assert_file_executable( full_path=full_path ):
+ if not self.assert_file_executable( full_path=full_path ):
status = app.install_model.ToolDependency.installation_status.ERROR
error_message = 'The path %s is not a file or is not executable by the owner.' % str( full_path )
tool_dependency = tool_dependency_util.set_tool_dependency_attributes( app,
@@ -132,7 +327,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="assert_executable">$INSTALL_DIR/mira/my_file</action>
if action_elem.text:
- action_dict[ 'full_path' ] = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_dict[ 'full_path' ] = basic_util.evaluate_template( action_elem.text, install_environment )
return action_dict
@@ -141,6 +336,19 @@
def __init__( self ):
self.type = 'assert_file_exists'
+ def assert_file_exists( self, full_path ):
+ """
+ Return True if a symbolic link or file exists, but if full_path is a directory,
+ return False.
+ """
+ if full_path is None:
+ return False
+ if os.path.isdir( full_path ):
+ return False
+ if os.path.exists( full_path ):
+ return True
+ return False
+
def execute_step( self, app, tool_dependency, package_name, actions, action_dict, filtered_actions, env_file_builder,
install_environment, work_dir, current_dir=None, initial_download=False ):
"""
@@ -152,7 +360,7 @@
full_path = action_dict[ 'full_path' ]
else:
full_path = os.path.join( current_dir, action_dict[ 'full_path' ] )
- if not td_common_util.assert_file_exists( full_path=full_path ):
+ if not self.assert_file_exists( full_path=full_path ):
status = app.install_model.ToolDependency.installation_status.ERROR
error_message = 'The path %s is not a file or does not exist.' % str( full_path )
tool_dependency = tool_dependency_util.set_tool_dependency_attributes( app,
@@ -165,7 +373,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="assert_on_path">$INSTALL_DIR/mira/my_file</action>
if action_elem.text:
- action_dict[ 'full_path' ] = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_dict[ 'full_path' ] = basic_util.evaluate_template( action_elem.text, install_environment )
return action_dict
@@ -187,7 +395,7 @@
pre_cmd = './configure %s && make && make install' % configure_opts
else:
pre_cmd = './configure --prefix=$INSTALL_DIR %s && make && make install' % configure_opts
- cmd = install_environment.build_command( td_common_util.evaluate_template( pre_cmd, install_environment ) )
+ cmd = install_environment.build_command( basic_util.evaluate_template( pre_cmd, install_environment ) )
return_code = install_environment.handle_command( app=app,
tool_dependency=tool_dependency,
cmd=cmd,
@@ -199,7 +407,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# Handle configure, make and make install allow providing configuration options
if action_elem.text:
- configure_opts = td_common_util.evaluate_template( action_elem.text, install_environment )
+ configure_opts = basic_util.evaluate_template( action_elem.text, install_environment )
action_dict[ 'configure_opts' ] = configure_opts
return action_dict
@@ -274,7 +482,7 @@
received_mode = int( file_elem.get( 'mode', 600 ), base=8 )
# For added security, ensure that the setuid and setgid bits are not set.
mode = received_mode & ~( stat.S_ISUID | stat.S_ISGID )
- file = td_common_util.evaluate_template( file_elem.text, install_environment )
+ file = basic_util.evaluate_template( file_elem.text, install_environment )
chmod_tuple = ( file, mode )
chmod_actions.append( chmod_tuple )
if chmod_actions:
@@ -282,11 +490,17 @@
return action_dict
-class DownloadBinary( RecipeStep ):
+class DownloadBinary( Download, RecipeStep ):
def __init__( self ):
self.type = 'download_binary'
+ def download_binary( self, url, work_dir ):
+ """Download a pre-compiled binary from the specified URL."""
+ downloaded_filename = os.path.split( url )[ -1 ]
+ dir = self.url_download( work_dir, downloaded_filename, url, extract=False )
+ return downloaded_filename
+
def filter_actions_after_binary_installation( self, actions ):
'''Filter out actions that should not be processed if a binary download succeeded.'''
filtered_actions = []
@@ -311,7 +525,7 @@
log.debug( 'Attempting to download from %s to %s', url, str( target_directory ) )
downloaded_filename = None
try:
- downloaded_filename = td_common_util.download_binary( url, work_dir )
+ downloaded_filename = self.download_binary( url, work_dir )
if initial_download:
# Filter out any actions that are not download_binary, chmod, or set_environment.
filtered_actions = self.filter_actions_after_binary_installation( actions[ 1: ] )
@@ -338,9 +552,9 @@
full_path_to_dir = os.path.abspath( install_environment.install_dir )
else:
full_path_to_dir = os.path.abspath( install_environment.install_dir )
- td_common_util.move_file( current_dir=work_dir,
- source=downloaded_filename,
- destination=full_path_to_dir )
+ basic_util.move_file( current_dir=work_dir,
+ source=downloaded_filename,
+ destination=full_path_to_dir )
# Not sure why dir is ignored in this method, need to investigate...
dir = None
if initial_download:
@@ -368,7 +582,7 @@
return action_dict
-class DownloadByUrl( RecipeStep ):
+class DownloadByUrl( Download, RecipeStep ):
def __init__( self ):
self.type = 'download_by_url'
@@ -394,9 +608,9 @@
downloaded_filename = action_dict[ 'target_filename' ]
else:
downloaded_filename = os.path.split( url )[ -1 ]
- dir = td_common_util.url_download( work_dir, downloaded_filename, url, extract=True )
+ dir = self.url_download( work_dir, downloaded_filename, url, extract=True )
if is_binary:
- log_file = os.path.join( install_environment.install_dir, td_common_util.INSTALLATION_LOG )
+ log_file = os.path.join( install_environment.install_dir, basic_util.INSTALLATION_LOG )
if os.path.exists( log_file ):
logfile = open( log_file, 'ab' )
else:
@@ -422,7 +636,7 @@
return action_dict
-class DownloadFile( RecipeStep ):
+class DownloadFile( Download, RecipeStep ):
def __init__( self ):
self.type = 'download_file'
@@ -447,7 +661,7 @@
filename = action_dict[ 'target_filename' ]
else:
filename = url.split( '/' )[ -1 ]
- td_common_util.url_download( work_dir, filename, url )
+ self.url_download( work_dir, filename, url )
if initial_download:
dir = os.path.curdir
return tool_dependency, filtered_actions, dir
@@ -479,13 +693,17 @@
full_path = action_dict[ 'full_path' ]
else:
full_path = os.path.join( current_dir, action_dict[ 'full_path' ] )
- td_common_util.make_directory( full_path=full_path )
+ self.make_directory( full_path=full_path )
return tool_dependency, None, None
+ def make_directory( self, full_path ):
+ if not os.path.exists( full_path ):
+ os.makedirs( full_path )
+
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="make_directory">$INSTALL_DIR/lib/python</action>
if action_elem.text:
- action_dict[ 'full_path' ] = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_dict[ 'full_path' ] = basic_util.evaluate_template( action_elem.text, install_environment )
return action_dict
@@ -515,7 +733,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# make; make install; allow providing make options
if action_elem.text:
- make_opts = td_common_util.evaluate_template( action_elem.text, install_environment )
+ make_opts = basic_util.evaluate_template( action_elem.text, install_environment )
action_dict[ 'make_opts' ] = make_opts
return action_dict
@@ -531,18 +749,38 @@
Move a directory of files. Since this class is not used in the initial download stage, no recipe step
filtering is performed here, and None values are always returned for filtered_actions and dir.
"""
- td_common_util.move_directory_files( current_dir=current_dir,
- source_dir=os.path.join( action_dict[ 'source_directory' ] ),
- destination_dir=os.path.join( action_dict[ 'destination_directory' ] ) )
+ self.move_directory_files( current_dir=current_dir,
+ source_dir=os.path.join( action_dict[ 'source_directory' ] ),
+ destination_dir=os.path.join( action_dict[ 'destination_directory' ] ) )
return tool_dependency, None, None
+ def move_directory_files( self, current_dir, source_dir, destination_dir ):
+ source_directory = os.path.abspath( os.path.join( current_dir, source_dir ) )
+ destination_directory = os.path.join( destination_dir )
+ if not os.path.isdir( destination_directory ):
+ os.makedirs( destination_directory )
+ symlinks = []
+ regular_files = []
+ for file_name in os.listdir( source_directory ):
+ source_file = os.path.join( source_directory, file_name )
+ destination_file = os.path.join( destination_directory, file_name )
+ files_tuple = ( source_file, destination_file )
+ if os.path.islink( source_file ):
+ symlinks.append( files_tuple )
+ else:
+ regular_files.append( files_tuple )
+ for source_file, destination_file in symlinks:
+ shutil.move( source_file, destination_file )
+ for source_file, destination_file in regular_files:
+ shutil.move( source_file, destination_file )
+
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="move_directory_files">
# <source_directory>bin</source_directory>
# <destination_directory>$INSTALL_DIR/bin</destination_directory>
# </action>
for move_elem in action_elem:
- move_elem_text = td_common_util.evaluate_template( move_elem.text, install_environment )
+ move_elem_text = basic_util.evaluate_template( move_elem.text, install_environment )
if move_elem_text:
action_dict[ move_elem.tag ] = move_elem_text
return action_dict
@@ -559,10 +797,10 @@
Move a file on disk. Since this class is not used in the initial download stage, no recipe step
filtering is performed here, and None values are always returned for filtered_actions and dir.
"""
- td_common_util.move_file( current_dir=current_dir,
- source=os.path.join( action_dict[ 'source' ] ),
- destination=os.path.join( action_dict[ 'destination' ] ),
- rename_to=action_dict[ 'rename_to' ] )
+ basic_util.move_file( current_dir=current_dir,
+ source=os.path.join( action_dict[ 'source' ] ),
+ destination=os.path.join( action_dict[ 'destination' ] ),
+ rename_to=action_dict[ 'rename_to' ] )
return tool_dependency, None, None
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
@@ -570,8 +808,8 @@
# <source>misc/some_file</source>
# <destination>$INSTALL_DIR/bin</destination>
# </action>
- action_dict[ 'source' ] = td_common_util.evaluate_template( action_elem.find( 'source' ).text, install_environment )
- action_dict[ 'destination' ] = td_common_util.evaluate_template( action_elem.find( 'destination' ).text, install_environment )
+ action_dict[ 'source' ] = basic_util.evaluate_template( action_elem.find( 'source' ).text, install_environment )
+ action_dict[ 'destination' ] = basic_util.evaluate_template( action_elem.find( 'destination' ).text, install_environment )
action_dict[ 'rename_to' ] = action_elem.get( 'rename_to' )
return action_dict
@@ -717,12 +955,12 @@
# <action type="set_environment">
# <environment_variable name="PATH" action="prepend_to">$INSTALL_DIR</environment_variable>
# </action>
+ env_manager = EnvManager( app )
env_var_dicts = []
for env_elem in action_elem:
if env_elem.tag == 'environment_variable':
- env_var_dict = \
- td_common_util.create_env_var_dict( elem=env_elem,
- install_environment=install_environment )
+ env_var_dict = env_manager.create_env_var_dict( elem=env_elem,
+ install_environment=install_environment )
if env_var_dict:
env_var_dicts.append( env_var_dict )
if env_var_dicts:
@@ -764,16 +1002,17 @@
# the current tool dependency package. See the package_matplotlib_1_2 repository in the test tool
# shed for a real-world example.
all_env_shell_file_paths = []
+ env_manager = EnvManager( app )
for env_elem in action_elem:
if env_elem.tag == 'repository':
- env_shell_file_paths = td_common_util.get_env_shell_file_paths( app, env_elem )
+ env_shell_file_paths = env_manager.get_env_shell_file_paths( env_elem )
if env_shell_file_paths:
all_env_shell_file_paths.extend( env_shell_file_paths )
action_dict[ 'env_shell_file_paths' ] = all_env_shell_file_paths
return action_dict
-class SetupPerlEnvironment( RecipeStep ):
+class SetupPerlEnvironment( Download, RecipeStep ):
def __init__( self ):
self.type = 'setup_purl_environment'
@@ -822,7 +1061,7 @@
# We assume a URL to a gem file.
url = perl_package
perl_package_name = url.split( '/' )[ -1 ]
- dir = td_common_util.url_download( work_dir, perl_package_name, url, extract=True )
+ dir = self.url_download( work_dir, perl_package_name, url, extract=True )
# Search for Build.PL or Makefile.PL (ExtUtils::MakeMaker vs. Module::Build).
tmp_work_dir = os.path.join( work_dir, dir )
if os.path.exists( os.path.join( tmp_work_dir, 'Makefile.PL' ) ):
@@ -836,7 +1075,7 @@
return tool_dependency, filtered_actions, dir
return tool_dependency, None, None
with lcd( tmp_work_dir ):
- cmd = install_environment.build_command( td_common_util.evaluate_template( cmd, install_environment ) )
+ cmd = install_environment.build_command( basic_util.evaluate_template( cmd, install_environment ) )
return_code = install_environment.handle_command( app=app,
tool_dependency=tool_dependency,
cmd=cmd,
@@ -849,7 +1088,7 @@
# perl package from CPAN without version number.
# cpanm should be installed with the parent perl distribution, otherwise this will not work.
cmd += '''cpanm --local-lib=$INSTALL_DIR %s''' % ( perl_package )
- cmd = install_environment.build_command( td_common_util.evaluate_template( cmd, install_environment ) )
+ cmd = install_environment.build_command( basic_util.evaluate_template( cmd, install_environment ) )
return_code = install_environment.handle_command( app=app,
tool_dependency=tool_dependency,
cmd=cmd,
@@ -890,10 +1129,11 @@
# with each repository. This will potentially update the value of the 'env_shell_file_paths' entry
# in action_dict.
all_env_shell_file_paths = []
- action_dict = td_common_util.get_env_shell_file_paths_from_setup_environment_elem( app,
- all_env_shell_file_paths,
- action_elem,
- action_dict )
+ env_manager = EnvManager( app )
+ action_dict = env_manager.get_env_shell_file_paths_from_setup_environment_elem( app,
+ all_env_shell_file_paths,
+ action_elem,
+ action_dict )
perl_packages = []
for env_elem in action_elem:
if env_elem.tag == 'package':
@@ -908,7 +1148,7 @@
return action_dict
-class SetupREnvironment( RecipeStep ):
+class SetupREnvironment( Download, RecipeStep ):
def __init__( self ):
self.type = 'setup_r_environment'
@@ -947,7 +1187,7 @@
for url in action_dict[ 'r_packages' ]:
filename = url.split( '/' )[ -1 ]
tarball_names.append( filename )
- td_common_util.url_download( work_dir, filename, url, extract=False )
+ self.url_download( work_dir, filename, url, extract=False )
dir = os.path.curdir
current_dir = os.path.abspath( os.path.join( work_dir, dir ) )
with lcd( current_dir ):
@@ -958,7 +1198,7 @@
cmd = r'''PATH=$PATH:$R_HOME/bin; export PATH; R_LIBS=$INSTALL_DIR; export R_LIBS;
Rscript -e "install.packages(c('%s'),lib='$INSTALL_DIR', repos=NULL, dependencies=FALSE)"''' % \
( str( tarball_name ) )
- cmd = install_environment.build_command( td_common_util.evaluate_template( cmd, install_environment ) )
+ cmd = install_environment.build_command( basic_util.evaluate_template( cmd, install_environment ) )
return_code = install_environment.handle_command( app=app,
tool_dependency=tool_dependency,
cmd=cmd,
@@ -993,10 +1233,11 @@
# associated with each repository. This will potentially update the value of the
# 'env_shell_file_paths' entry in action_dict.
all_env_shell_file_paths = []
- action_dict = td_common_util.get_env_shell_file_paths_from_setup_environment_elem( app,
- all_env_shell_file_paths,
- action_elem,
- action_dict )
+ env_manager = EnvManager( app )
+ action_dict = env_manager.get_env_shell_file_paths_from_setup_environment_elem( app,
+ all_env_shell_file_paths,
+ action_elem,
+ action_dict )
r_packages = list()
for env_elem in action_elem:
if env_elem.tag == 'package':
@@ -1006,7 +1247,7 @@
return action_dict
-class SetupRubyEnvironment( RecipeStep ):
+class SetupRubyEnvironment( Download, RecipeStep ):
def __init__( self ):
self.type = 'setup_ruby_environment'
@@ -1058,7 +1299,7 @@
# We assume a URL to a gem file.
url = gem
gem_name = url.split( '/' )[ -1 ]
- td_common_util.url_download( work_dir, gem_name, url, extract=False )
+ self.url_download( work_dir, gem_name, url, extract=False )
cmd = '''PATH=$PATH:$RUBY_HOME/bin; export PATH; GEM_HOME=$INSTALL_DIR; export GEM_HOME;
gem install --local %s ''' % ( gem_name )
else:
@@ -1073,7 +1314,7 @@
# no version number given
cmd = '''PATH=$PATH:$RUBY_HOME/bin; export PATH; GEM_HOME=$INSTALL_DIR; export GEM_HOME;
gem install %s''' % ( gem )
- cmd = install_environment.build_command( td_common_util.evaluate_template( cmd, install_environment ) )
+ cmd = install_environment.build_command( basic_util.evaluate_template( cmd, install_environment ) )
return_code = install_environment.handle_command( app=app,
tool_dependency=tool_dependency,
cmd=cmd,
@@ -1114,10 +1355,11 @@
# associated with each repository. This will potentially update the value of the
# 'env_shell_file_paths' entry in action_dict.
all_env_shell_file_paths = []
- action_dict = td_common_util.get_env_shell_file_paths_from_setup_environment_elem( app,
- all_env_shell_file_paths,
- action_elem,
- action_dict )
+ env_manager = EnvManager( app )
+ action_dict = env_manager.get_env_shell_file_paths_from_setup_environment_elem( app,
+ all_env_shell_file_paths,
+ action_elem,
+ action_dict )
ruby_package_tups = []
for env_elem in action_elem:
if env_elem.tag == 'package':
@@ -1140,7 +1382,7 @@
return action_dict
-class SetupVirtualEnv( RecipeStep ):
+class SetupVirtualEnv( Download, RecipeStep ):
def __init__( self ):
self.type = 'setup_virtualenv'
@@ -1228,9 +1470,10 @@
with install_environment.make_tmp_dir() as work_dir:
downloaded_filename = VIRTUALENV_URL.rsplit('/', 1)[-1]
try:
- dir = td_common_util.url_download( work_dir, downloaded_filename, VIRTUALENV_URL )
+ dir = self.url_download( work_dir, downloaded_filename, VIRTUALENV_URL )
except:
- log.error( "Failed to download virtualenv: td_common_util.url_download( '%s', '%s', '%s' ) threw an exception", work_dir, downloaded_filename, VIRTUALENV_URL )
+ log.error( "Failed to download virtualenv: url_download( '%s', '%s', '%s' ) threw an exception",
+ work_dir, downloaded_filename, VIRTUALENV_URL )
return False
full_path_to_dir = os.path.abspath( os.path.join( work_dir, dir ) )
shutil.move( full_path_to_dir, venv_dir )
@@ -1245,7 +1488,7 @@
# lxml==2.3.0</action>
## Manually specify contents of requirements.txt file to create dynamically.
action_dict[ 'use_requirements_file' ] = asbool( action_elem.get( 'use_requirements_file', True ) )
- action_dict[ 'requirements' ] = td_common_util.evaluate_template( action_elem.text or 'requirements.txt', install_environment )
+ action_dict[ 'requirements' ] = basic_util.evaluate_template( action_elem.text or 'requirements.txt', install_environment )
action_dict[ 'python' ] = action_elem.get( 'python', 'python' )
return action_dict
@@ -1316,7 +1559,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="shell_command">make</action>
- action_elem_text = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_elem_text = basic_util.evaluate_template( action_elem.text, install_environment )
if action_elem_text:
action_dict[ 'command' ] = action_elem_text
return action_dict
@@ -1338,7 +1581,7 @@
env_vars = dict()
env_vars = install_environment.environment_dict()
tool_shed_repository = tool_dependency.tool_shed_repository
- env_vars.update( td_common_util.get_env_var_values( install_environment ) )
+ env_vars.update( basic_util.get_env_var_values( install_environment ) )
language = action_dict[ 'language' ]
with settings( warn_only=True, **env_vars ):
if language == 'cheetah':
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
@@ -10,7 +10,7 @@
from tool_shed.util import tool_dependency_util
from tool_shed.util import xml_util
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
+from tool_shed.galaxy_install.tool_dependencies.env_manager import EnvManager
from tool_shed.galaxy_install.tool_dependencies.recipe.env_file_builder import EnvFileBuilder
from tool_shed.galaxy_install.tool_dependencies.recipe.install_environment import InstallEnvironment
@@ -85,9 +85,9 @@
platform_info_dict = tool_dependency_util.get_platform_info_dict()
if package_install_version == '1.0':
# Handle tool dependency installation using a fabric method included in the Galaxy framework.
- actions_elem_tuples = td_common_util.parse_package_elem( package_elem,
- platform_info_dict=platform_info_dict,
- include_after_install_actions=True )
+ actions_elem_tuples = tool_dependency_util.parse_package_elem( package_elem,
+ platform_info_dict=platform_info_dict,
+ include_after_install_actions=True )
if not actions_elem_tuples:
proceed_with_install = False
error_message = 'Version %s of the %s package cannot be installed because ' % ( str( package_version ), str( package_name ) )
@@ -491,6 +491,7 @@
# <set_environment version="1.0">
# <repository toolshed="<tool shed>" name="<repository name>" owner="<repository owner>" changeset_revision="<changeset revision>" />
# </set_environment>
+ env_manager = EnvManager( app )
tool_dependencies = []
env_var_version = elem.get( 'version', '1.0' )
tool_shed_repository_install_dir = os.path.abspath( tool_shed_repository.repo_files_directory( app ) )
@@ -514,8 +515,8 @@
tool_dependency_version=None )
install_environment = InstallEnvironment( tool_shed_repository_install_dir=tool_shed_repository_install_dir,
install_dir=install_dir )
- env_var_dict = td_common_util.create_env_var_dict( elem=env_var_elem,
- install_environment=install_environment )
+ env_var_dict = env_manager.create_env_var_dict( elem=env_var_elem,
+ install_environment=install_environment )
if env_var_dict:
if not os.path.exists( install_dir ):
os.makedirs( install_dir )
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
+++ /dev/null
@@ -1,578 +0,0 @@
-import logging
-import os
-import re
-import shutil
-import stat
-import sys
-import tarfile
-import time
-import traceback
-import urllib2
-import zipfile
-from string import Template
-from tool_shed.util import common_util
-import tool_shed.util.shed_util_common as suc
-from galaxy.datatypes import checkers
-
-log = logging.getLogger( __name__ )
-
-# Set no activity timeout to 20 minutes.
-NO_OUTPUT_TIMEOUT = 1200.0
-INSTALLATION_LOG = 'INSTALLATION.log'
-
-
-class CompressedFile( object ):
-
- def __init__( self, file_path, mode='r' ):
- if istar( file_path ):
- self.file_type = 'tar'
- elif iszip( file_path ) and not isjar( file_path ):
- self.file_type = 'zip'
- self.file_name = os.path.splitext( os.path.basename( file_path ) )[ 0 ]
- if self.file_name.endswith( '.tar' ):
- self.file_name = os.path.splitext( self.file_name )[ 0 ]
- self.type = self.file_type
- method = 'open_%s' % self.file_type
- if hasattr( self, method ):
- self.archive = getattr( self, method )( file_path, mode )
- else:
- raise NameError( 'File type %s specified, no open method found.' % self.file_type )
-
- def extract( self, path ):
- '''Determine the path to which the archive should be extracted.'''
- contents = self.getmembers()
- extraction_path = path
- if len( contents ) == 1:
- # The archive contains a single file, return the extraction path.
- if self.isfile( contents[ 0 ] ):
- extraction_path = os.path.join( path, self.file_name )
- if not os.path.exists( extraction_path ):
- os.makedirs( extraction_path )
- self.archive.extractall( extraction_path )
- else:
- # Get the common prefix for all the files in the archive. If the common prefix ends with a slash,
- # or self.isdir() returns True, the archive contains a single directory with the desired contents.
- # Otherwise, it contains multiple files and/or directories at the root of the archive.
- common_prefix = os.path.commonprefix( [ self.getname( item ) for item in contents ] )
- if len( common_prefix ) >= 1 and not common_prefix.endswith( os.sep ) and self.isdir( self.getmember( common_prefix ) ):
- common_prefix += os.sep
- if common_prefix.endswith( os.sep ):
- self.archive.extractall( os.path.join( path ) )
- extraction_path = os.path.join( path, common_prefix )
- else:
- extraction_path = os.path.join( path, self.file_name )
- if not os.path.exists( extraction_path ):
- os.makedirs( extraction_path )
- self.archive.extractall( os.path.join( extraction_path ) )
- return os.path.abspath( extraction_path )
-
- def getmembers_tar( self ):
- return self.archive.getmembers()
-
- def getmembers_zip( self ):
- return self.archive.infolist()
-
- def getname_tar( self, item ):
- return item.name
-
- def getname_zip( self, item ):
- return item.filename
-
- def getmember( self, name ):
- for member in self.getmembers():
- if self.getname( member ) == name:
- return member
-
- def getmembers( self ):
- return getattr( self, 'getmembers_%s' % self.type )()
-
- def getname( self, member ):
- return getattr( self, 'getname_%s' % self.type )( member )
-
- def isdir( self, member ):
- return getattr( self, 'isdir_%s' % self.type )( member )
-
- def isdir_tar( self, member ):
- return member.isdir()
-
- def isdir_zip( self, member ):
- if member.filename.endswith( os.sep ):
- return True
- return False
-
- def isfile( self, member ):
- if not self.isdir( member ):
- return True
- return False
-
- def open_tar( self, filepath, mode ):
- return tarfile.open( filepath, mode, errorlevel=0 )
-
- def open_zip( self, filepath, mode ):
- return zipfile.ZipFile( filepath, mode )
-
-def assert_directory_executable( full_path ):
- """
- Return True if a symbolic link or directory exists and is executable, but if
- full_path is a file, return False.
- """
- if full_path is None:
- return False
- if os.path.isfile( full_path ):
- return False
- if os.path.isdir( full_path ):
- # Make sure the owner has execute permission on the directory.
- # See http://docs.python.org/2/library/stat.html
- if stat.S_IXUSR & os.stat( full_path )[ stat.ST_MODE ] == 64:
- return True
- return False
-
-def assert_directory_exists( full_path ):
- """
- Return True if a symbolic link or directory exists, but if full_path is a file,
- return False. """
- if full_path is None:
- return False
- if os.path.isfile( full_path ):
- return False
- if os.path.isdir( full_path ):
- return True
- return False
-
-def assert_file_executable( full_path ):
- """
- Return True if a symbolic link or file exists and is executable, but if full_path
- is a directory, return False.
- """
- if full_path is None:
- return False
- if os.path.isdir( full_path ):
- return False
- if os.path.exists( full_path ):
- # Make sure the owner has execute permission on the file.
- # See http://docs.python.org/2/library/stat.html
- if stat.S_IXUSR & os.stat( full_path )[ stat.ST_MODE ] == 64:
- return True
- return False
-
-def assert_file_exists( full_path ):
- """
- Return True if a symbolic link or file exists, but if full_path is a directory,
- return False.
- """
- if full_path is None:
- return False
- if os.path.isdir( full_path ):
- return False
- if os.path.exists( full_path ):
- return True
- return False
-
-def create_env_var_dict( elem, install_environment ):
- env_var_name = elem.get( 'name', 'PATH' )
- env_var_action = elem.get( 'action', 'prepend_to' )
- env_var_text = None
- tool_dependency_install_dir = install_environment.install_dir
- tool_shed_repository_install_dir = install_environment.tool_shed_repository_install_dir
- if elem.text and elem.text.find( 'REPOSITORY_INSTALL_DIR' ) >= 0:
- if tool_shed_repository_install_dir and elem.text.find( '$REPOSITORY_INSTALL_DIR' ) != -1:
- env_var_text = elem.text.replace( '$REPOSITORY_INSTALL_DIR', tool_shed_repository_install_dir )
- return dict( name=env_var_name, action=env_var_action, value=env_var_text )
- else:
- env_var_text = elem.text.replace( '$REPOSITORY_INSTALL_DIR', tool_dependency_install_dir )
- return dict( name=env_var_name, action=env_var_action, value=env_var_text )
- if elem.text and elem.text.find( 'INSTALL_DIR' ) >= 0:
- if tool_dependency_install_dir:
- env_var_text = elem.text.replace( '$INSTALL_DIR', tool_dependency_install_dir )
- return dict( name=env_var_name, action=env_var_action, value=env_var_text )
- else:
- env_var_text = elem.text.replace( '$INSTALL_DIR', tool_shed_repository_install_dir )
- return dict( name=env_var_name, action=env_var_action, value=env_var_text )
- if elem.text:
- # Allow for environment variables that contain neither REPOSITORY_INSTALL_DIR nor INSTALL_DIR
- # since there may be command line parameters that are tuned for a Galaxy instance. Allowing them
- # to be set in one location rather than being hard coded into each tool config is the best approach.
- # For example:
- # <environment_variable name="GATK2_SITE_OPTIONS" action="set_to">
- # "--num_threads 4 --num_cpu_threads_per_data_thread 3 --phone_home STANDARD"
- # </environment_variable>
- return dict( name=env_var_name, action=env_var_action, value=elem.text)
- return None
-
-def download_binary( url, work_dir ):
- """Download a pre-compiled binary from the specified URL."""
- downloaded_filename = os.path.split( url )[ -1 ]
- dir = url_download( work_dir, downloaded_filename, url, extract=False )
- return downloaded_filename
-
-def egrep_escape( text ):
- """Escape ``text`` to allow literal matching using egrep."""
- regex = re.escape( text )
- # Seems like double escaping is needed for \
- regex = regex.replace( '\\\\', '\\\\\\' )
- # Triple-escaping seems to be required for $ signs
- regex = regex.replace( r'\$', r'\\\$' )
- # Whereas single quotes should not be escaped
- regex = regex.replace( r"\'", "'" )
- return regex
-
-def evaluate_template( text, install_environment ):
- """
- Substitute variables defined in XML blocks from dependencies file. The value of the received
- repository_install_dir is the root installation directory of the repository that contains the
- tool dependency. The value of the received install_dir is the root installation directory of
- the tool_dependency.
- """
- return Template( text ).safe_substitute( get_env_var_values( install_environment ) )
-
-def format_traceback():
- ex_type, ex, tb = sys.exc_info()
- return ''.join( traceback.format_tb( tb ) )
-
-def get_env_shell_file_path( installation_directory ):
- env_shell_file_name = 'env.sh'
- default_location = os.path.abspath( os.path.join( installation_directory, env_shell_file_name ) )
- if os.path.exists( default_location ):
- return default_location
- for root, dirs, files in os.walk( installation_directory ):
- for name in files:
- if name == env_shell_file_name:
- return os.path.abspath( os.path.join( root, name ) )
- return None
-
-def get_env_shell_file_paths( app, elem ):
- # Currently only the following tag set is supported.
- # <repository toolshed="http://localhost:9009/" name="package_numpy_1_7" owner="test" changeset_revision="c84c6a8be056">
- # <package name="numpy" version="1.7.1" />
- # </repository>
- env_shell_file_paths = []
- toolshed = elem.get( 'toolshed', None )
- repository_name = elem.get( 'name', None )
- repository_owner = elem.get( 'owner', None )
- changeset_revision = elem.get( 'changeset_revision', None )
- if toolshed and repository_name and repository_owner and changeset_revision:
- # The protocol is not stored, but the port is if it exists.
- toolshed = common_util.remove_protocol_from_tool_shed_url( toolshed )
- repository = suc.get_repository_for_dependency_relationship( app, toolshed, repository_name, repository_owner, changeset_revision )
- if repository:
- for sub_elem in elem:
- tool_dependency_type = sub_elem.tag
- tool_dependency_name = sub_elem.get( 'name' )
- tool_dependency_version = sub_elem.get( 'version' )
- if tool_dependency_type and tool_dependency_name and tool_dependency_version:
- # Get the tool_dependency so we can get its installation directory.
- tool_dependency = None
- for tool_dependency in repository.tool_dependencies:
- if tool_dependency.type == tool_dependency_type and \
- tool_dependency.name == tool_dependency_name and \
- tool_dependency.version == tool_dependency_version:
- break
- if tool_dependency:
- tool_dependency_key = '%s/%s' % ( tool_dependency_name, tool_dependency_version )
- installation_directory = tool_dependency.installation_directory( app )
- env_shell_file_path = get_env_shell_file_path( installation_directory )
- if env_shell_file_path:
- env_shell_file_paths.append( env_shell_file_path )
- else:
- error_message = "Skipping tool dependency definition because unable to locate env.sh file for tool dependency "
- error_message += "type %s, name %s, version %s for repository %s" % \
- ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ), str( repository.name ) )
- log.debug( error_message )
- continue
- else:
- error_message = "Skipping tool dependency definition because unable to locate tool dependency "
- error_message += "type %s, name %s, version %s for repository %s" % \
- ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ), str( repository.name ) )
- log.debug( error_message )
- continue
- else:
- error_message = "Skipping invalid tool dependency definition: type %s, name %s, version %s." % \
- ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ) )
- log.debug( error_message )
- continue
- else:
- error_message = "Skipping set_environment_for_install definition because unable to locate required installed tool shed repository: "
- error_message += "toolshed %s, name %s, owner %s, changeset_revision %s." % \
- ( str( toolshed ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
- log.debug( error_message )
- else:
- error_message = "Skipping invalid set_environment_for_install definition: toolshed %s, name %s, owner %s, changeset_revision %s." % \
- ( str( toolshed ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
- log.debug( error_message )
- return env_shell_file_paths
-
-def get_env_shell_file_paths_from_setup_environment_elem( app, all_env_shell_file_paths, elem, action_dict ):
- """
- Parse an XML tag set to discover all child repository dependency tags and define the path to an env.sh file associated
- with the repository (this requires the repository dependency to be in an installed state). The received action_dict
- will be updated with these discovered paths and returned to the caller. This method handles tool dependency definition
- tag sets <setup_r_environment>, <setup_ruby_environment> and <setup_perl_environment>.
- """
- # An example elem is:
- # <action type="setup_perl_environment">
- # <repository name="package_perl_5_18" owner="iuc">
- # <package name="perl" version="5.18.1" />
- # </repository>
- # <repository name="package_expat_2_1" owner="iuc" prior_installation_required="True">
- # <package name="expat" version="2.1.0" />
- # </repository>
- # <package>http://search.cpan.org/CPAN/authors/id/T/TO/TODDR/XML-Parser-2.41.tar.gz</package>
- # <package>http://search.cpan.org/CPAN/authors/id/L/LD/LDS/CGI.pm-3.43.tar.gz</package>
- # </action>
- for action_elem in elem:
- if action_elem.tag == 'repository':
- env_shell_file_paths = get_env_shell_file_paths( app, action_elem )
- all_env_shell_file_paths.extend( env_shell_file_paths )
- if all_env_shell_file_paths:
- action_dict[ 'env_shell_file_paths' ] = all_env_shell_file_paths
- action_dict[ 'action_shell_file_paths' ] = env_shell_file_paths
- return action_dict
-
-def get_env_var_values( install_environment ):
- """
- Return a dictionary of values, some of which enable substitution of reserved words for the values.
- The received install_enviroment object has 2 important attributes for reserved word substitution:
- install_environment.tool_shed_repository_install_dir is the root installation directory of the repository
- that contains the tool dependency being installed, and install_environment.install_dir is the root
- installation directory of the tool dependency.
- """
- env_var_dict = {}
- env_var_dict[ 'REPOSITORY_INSTALL_DIR' ] = install_environment.tool_shed_repository_install_dir
- env_var_dict[ 'INSTALL_DIR' ] = install_environment.install_dir
- env_var_dict[ 'system_install' ] = install_environment.install_dir
- # If the Python interpreter is 64bit then we can safely assume that the underlying system is also 64bit.
- env_var_dict[ '__is64bit__' ] = sys.maxsize > 2**32
- return env_var_dict
-
-def isbz2( file_path ):
- return checkers.is_bz2( file_path )
-
-def isgzip( file_path ):
- return checkers.is_gzip( file_path )
-
-def isjar( file_path ):
- return iszip( file_path ) and file_path.endswith( '.jar' )
-
-def istar( file_path ):
- return tarfile.is_tarfile( file_path )
-
-def iszip( file_path ):
- return checkers.check_zip( file_path )
-
-def is_compressed( file_path ):
- if isjar( file_path ):
- return False
- else:
- return iszip( file_path ) or isgzip( file_path ) or istar( file_path ) or isbz2( file_path )
-
-def make_directory( full_path ):
- if not os.path.exists( full_path ):
- os.makedirs( full_path )
-
-def move_directory_files( current_dir, source_dir, destination_dir ):
- source_directory = os.path.abspath( os.path.join( current_dir, source_dir ) )
- destination_directory = os.path.join( destination_dir )
- if not os.path.isdir( destination_directory ):
- os.makedirs( destination_directory )
- symlinks = []
- regular_files = []
- for file_name in os.listdir( source_directory ):
- source_file = os.path.join( source_directory, file_name )
- destination_file = os.path.join( destination_directory, file_name )
- files_tuple = ( source_file, destination_file )
- if os.path.islink( source_file ):
- symlinks.append( files_tuple )
- else:
- regular_files.append( files_tuple )
- for source_file, destination_file in symlinks:
- shutil.move( source_file, destination_file )
- for source_file, destination_file in regular_files:
- shutil.move( source_file, destination_file )
-
-def move_file( current_dir, source, destination, rename_to=None ):
- source_path = os.path.abspath( os.path.join( current_dir, source ) )
- source_file = os.path.basename( source_path )
- if rename_to is not None:
- destination_file = rename_to
- destination_directory = os.path.join( destination )
- destination_path = os.path.join( destination_directory, destination_file )
- else:
- destination_directory = os.path.join( destination )
- destination_path = os.path.join( destination_directory, source_file )
- if not os.path.exists( destination_directory ):
- os.makedirs( destination_directory )
- shutil.move( source_path, destination_path )
-
-def parse_package_elem( package_elem, platform_info_dict=None, include_after_install_actions=True ):
- """
- Parse a <package> element within a tool dependency definition and return a list of action tuples.
- This method is called when setting metadata on a repository that includes a tool_dependencies.xml
- file or when installing a repository that includes a tool_dependencies.xml file. If installing,
- platform_info_dict must be a valid dictionary and include_after_install_actions must be True.
- """
- # The actions_elem_tuples list contains <actions> tag sets (possibly inside of an <actions_group>
- # tag set) to be processed in the order they are defined in the tool_dependencies.xml file.
- actions_elem_tuples = []
- # The tag sets that will go into the actions_elem_list are those that install a compiled binary if
- # the architecture and operating system match its defined attributes. If compiled binary is not
- # installed, the first <actions> tag set [following those that have the os and architecture attributes]
- # that does not have os or architecture attributes will be processed. This tag set must contain the
- # recipe for downloading and compiling source.
- actions_elem_list = []
- for elem in package_elem:
- if elem.tag == 'actions':
- # We have an <actions> tag that should not be matched against a specific combination of
- # architecture and operating system.
- in_actions_group = False
- actions_elem_tuples.append( ( in_actions_group, elem ) )
- elif elem.tag == 'actions_group':
- # We have an actions_group element, and its child <actions> elements should therefore be compared
- # with the current operating system
- # and processor architecture.
- in_actions_group = True
- # Record the number of <actions> elements so we can filter out any <action> elements that precede
- # <actions> elements.
- actions_elem_count = len( elem.findall( 'actions' ) )
- # Record the number of <actions> elements that have both architecture and os specified, in order
- # to filter out any platform-independent <actions> elements that come before platform-specific
- # <actions> elements.
- platform_actions_elements = []
- for actions_elem in elem.findall( 'actions' ):
- if actions_elem.get( 'architecture' ) is not None and actions_elem.get( 'os' ) is not None:
- platform_actions_elements.append( actions_elem )
- platform_actions_element_count = len( platform_actions_elements )
- platform_actions_elements_processed = 0
- actions_elems_processed = 0
- # The tag sets that will go into the after_install_actions list are <action> tags instead of <actions>
- # tags. These will be processed only if they are at the very end of the <actions_group> tag set (after
- # all <actions> tag sets). See below for details.
- after_install_actions = []
- # Inspect the <actions_group> element and build the actions_elem_list and the after_install_actions list.
- for child_element in elem:
- if child_element.tag == 'actions':
- actions_elems_processed += 1
- system = child_element.get( 'os' )
- architecture = child_element.get( 'architecture' )
- # Skip <actions> tags that have only one of architecture or os specified, in order for the
- # count in platform_actions_elements_processed to remain accurate.
- if ( system and not architecture ) or ( architecture and not system ):
- log.debug( 'Error: Both architecture and os attributes must be specified in an <actions> tag.' )
- continue
- # Since we are inside an <actions_group> tag set, compare it with our current platform information
- # and filter the <actions> tag sets that don't match. Require both the os and architecture attributes
- # to be defined in order to find a match.
- if system and architecture:
- platform_actions_elements_processed += 1
- # If either the os or architecture do not match the platform, this <actions> tag will not be
- # considered a match. Skip it and proceed with checking the next one.
- if platform_info_dict:
- if platform_info_dict[ 'os' ] != system or platform_info_dict[ 'architecture' ] != architecture:
- continue
- else:
- # We must not be installing a repository into Galaxy, so determining if we can install a
- # binary is not necessary.
- continue
- else:
- # <actions> tags without both os and architecture attributes are only allowed to be specified
- # after platform-specific <actions> tags. If we find a platform-independent <actions> tag before
- # all platform-specific <actions> tags have been processed.
- if platform_actions_elements_processed < platform_actions_element_count:
- debug_msg = 'Error: <actions> tags without os and architecture attributes are only allowed '
- debug_msg += 'after all <actions> tags with os and architecture attributes have been defined. '
- debug_msg += 'Skipping the <actions> tag set with no os or architecture attributes that has '
- debug_msg += 'been defined between two <actions> tag sets that have these attributes defined. '
- log.debug( debug_msg )
- continue
- # If we reach this point, it means one of two things: 1) The system and architecture attributes are
- # not defined in this <actions> tag, or 2) The system and architecture attributes are defined, and
- # they are an exact match for the current platform. Append the child element to the list of elements
- # to process.
- actions_elem_list.append( child_element )
- elif child_element.tag == 'action':
- # Any <action> tags within an <actions_group> tag set must come after all <actions> tags.
- if actions_elems_processed == actions_elem_count:
- # If all <actions> elements have been processed, then this <action> element can be appended to the
- # list of actions to execute within this group.
- after_install_actions.append( child_element )
- else:
- # If any <actions> elements remain to be processed, then log a message stating that <action>
- # elements are not allowed to precede any <actions> elements within an <actions_group> tag set.
- debug_msg = 'Error: <action> tags are only allowed at the end of an <actions_group> tag set after '
- debug_msg += 'all <actions> tags. Skipping <%s> element with type %s.' % \
- ( child_element.tag, child_element.get( 'type', 'unknown' ) )
- log.debug( debug_msg )
- continue
- if platform_info_dict is None and not include_after_install_actions:
- # We must be setting metadata on a repository.
- if len( actions_elem_list ) >= 1:
- actions_elem_tuples.append( ( in_actions_group, actions_elem_list[ 0 ] ) )
- else:
- # We are processing a recipe that contains only an <actions_group> tag set for installing a binary,
- # but does not include an additional recipe for installing and compiling from source.
- actions_elem_tuples.append( ( in_actions_group, [] ) )
- elif platform_info_dict is not None and include_after_install_actions:
- # We must be installing a repository.
- if after_install_actions:
- actions_elem_list.extend( after_install_actions )
- actions_elem_tuples.append( ( in_actions_group, actions_elem_list ) )
- else:
- # Skip any element that is not <actions> or <actions_group> - this will skip comments, <repository> tags
- # and <readme> tags.
- in_actions_group = False
- continue
- return actions_elem_tuples
-
-def __shellquote( s ):
- """Quote and escape the supplied string for use in shell expressions."""
- return "'" + s.replace( "'", "'\\''" ) + "'"
-
-def url_download( install_dir, downloaded_file_name, download_url, extract=True ):
- file_path = os.path.join( install_dir, downloaded_file_name )
- src = None
- dst = None
- # Set a timer so we don't sit here forever.
- start_time = time.time()
- try:
- src = urllib2.urlopen( download_url )
- dst = open( file_path, 'wb' )
- while True:
- chunk = src.read( suc.CHUNK_SIZE )
- if chunk:
- dst.write( chunk )
- else:
- break
- time_taken = time.time() - start_time
- if time_taken > NO_OUTPUT_TIMEOUT:
- err_msg = 'Downloading from URL %s took longer than the defined timeout period of %.1f seconds.' % \
- ( str( download_url ), NO_OUTPUT_TIMEOUT )
- raise Exception( err_msg )
- except Exception, e:
- err_msg = err_msg = 'Error downloading from URL\n%s:\n%s' % ( str( download_url ), str( e ) )
- raise Exception( err_msg )
- finally:
- if src:
- src.close()
- if dst:
- dst.close()
- if extract:
- if istar( file_path ) or ( iszip( file_path ) and not isjar( file_path ) ):
- archive = CompressedFile( file_path )
- extraction_path = archive.extract( install_dir )
- else:
- extraction_path = os.path.abspath( install_dir )
- else:
- extraction_path = os.path.abspath( install_dir )
- return extraction_path
-
-def zipfile_ok( path_to_archive ):
- """
- This function is a bit pedantic and not functionally necessary. It checks whether there is no file pointing outside of the extraction,
- because ZipFile.extractall() has some potential security holes. See python zipfile documentation for more details.
- """
- basename = os.path.realpath( os.path.dirname( path_to_archive ) )
- zip_archive = zipfile.ZipFile( path_to_archive )
- for member in zip_archive.namelist():
- member_path = os.path.realpath( os.path.join( basename, member ) )
- if not member_path.startswith( basename ):
- return False
- return True
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/scripts/check_filesystem_for_empty_tool_dependency_installation_paths.py
--- a/lib/tool_shed/scripts/check_filesystem_for_empty_tool_dependency_installation_paths.py
+++ b/lib/tool_shed/scripts/check_filesystem_for_empty_tool_dependency_installation_paths.py
@@ -7,7 +7,7 @@
new_path.extend( sys.path[1:] )
sys.path = new_path
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
+from tool_shed.util.basic_util import INSTALLATION_LOG
def main( args ):
empty_installation_paths = []
@@ -31,13 +31,13 @@
no_files = False
if len( dirs ) == 0:
no_dirs = True
- if len( files ) == 0 or len( files ) == 1 and td_common_util.INSTALLATION_LOG in files:
+ if len( files ) == 0 or len( files ) == 1 and INSTALLATION_LOG in files:
no_files = True
if no_files and no_dirs and root not in empty_installation_paths:
empty_installation_paths.append( root )
if len( empty_installation_paths ) > 0:
print 'The following %d tool dependency installation directories were found to be empty or contain only the file %s.' % \
- ( len( empty_installation_paths ), td_common_util.INSTALLATION_LOG )
+ ( len( empty_installation_paths ), INSTALLATION_LOG )
if args.delete:
for path in empty_installation_paths:
if os.path.exists( path ):
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/scripts/check_s3_for_empty_tool_dependency_installation_paths.py
--- a/lib/tool_shed/scripts/check_s3_for_empty_tool_dependency_installation_paths.py
+++ b/lib/tool_shed/scripts/check_s3_for_empty_tool_dependency_installation_paths.py
@@ -12,8 +12,7 @@
import boto
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
-
+from tool_shed.util.basic_util import INSTALLATION_LOG
class BucketList( object ):
@@ -77,7 +76,7 @@
# This would not be the case in a Galaxy instance, since the Galaxy admin will need to verify the contents of
# the installation path in order to determine which action should be taken.
elif len( tool_dependency_path_contents ) == 2 and \
- tool_dependency_path_contents[1].name.endswith( td_common_util.INSTALLATION_LOG ):
+ tool_dependency_path_contents[1].name.endswith( INSTALLATION_LOG ):
empty_directories.append( tool_dependency_path_contents[ 0 ] )
return [ item.name for item in empty_directories ]
@@ -106,7 +105,7 @@
print 'No empty installation paths found, exiting.'
return 0
print 'The following %d tool dependency installation paths were found to be empty or contain only the file %s.' % \
- ( len( dependency_cleaner.empty_installation_paths ), td_common_util.INSTALLATION_LOG )
+ ( len( dependency_cleaner.empty_installation_paths ), INSTALLATION_LOG )
if asbool( args.delete ):
dependency_cleaner.delete_empty_installation_paths()
else:
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/util/basic_util.py
--- a/lib/tool_shed/util/basic_util.py
+++ b/lib/tool_shed/util/basic_util.py
@@ -1,5 +1,8 @@
import logging
import os
+import shutil
+import sys
+from string import Template
from galaxy.util import unicodify
@@ -10,8 +13,52 @@
log = logging.getLogger( __name__ )
+CHUNK_SIZE = 2**20 # 1Mb
+INSTALLATION_LOG = 'INSTALLATION.log'
+# Set no activity timeout to 20 minutes.
+NO_OUTPUT_TIMEOUT = 1200.0
+MAXDIFFSIZE = 8000
MAX_DISPLAY_SIZE = 32768
+def evaluate_template( text, install_environment ):
+ """
+ Substitute variables defined in XML blocks from dependencies file. The value of the received
+ repository_install_dir is the root installation directory of the repository that contains the
+ tool dependency. The value of the received install_dir is the root installation directory of
+ the tool_dependency.
+ """
+ return Template( text ).safe_substitute( get_env_var_values( install_environment ) )
+
+def get_env_var_values( install_environment ):
+ """
+ Return a dictionary of values, some of which enable substitution of reserved words for the values.
+ The received install_enviroment object has 2 important attributes for reserved word substitution:
+ install_environment.tool_shed_repository_install_dir is the root installation directory of the repository
+ that contains the tool dependency being installed, and install_environment.install_dir is the root
+ installation directory of the tool dependency.
+ """
+ env_var_dict = {}
+ env_var_dict[ 'REPOSITORY_INSTALL_DIR' ] = install_environment.tool_shed_repository_install_dir
+ env_var_dict[ 'INSTALL_DIR' ] = install_environment.install_dir
+ env_var_dict[ 'system_install' ] = install_environment.install_dir
+ # If the Python interpreter is 64bit then we can safely assume that the underlying system is also 64bit.
+ env_var_dict[ '__is64bit__' ] = sys.maxsize > 2**32
+ return env_var_dict
+
+def move_file( current_dir, source, destination, rename_to=None ):
+ source_path = os.path.abspath( os.path.join( current_dir, source ) )
+ source_file = os.path.basename( source_path )
+ if rename_to is not None:
+ destination_file = rename_to
+ destination_directory = os.path.join( destination )
+ destination_path = os.path.join( destination_directory, destination_file )
+ else:
+ destination_directory = os.path.join( destination )
+ destination_path = os.path.join( destination_directory, source_file )
+ if not os.path.exists( destination_directory ):
+ os.makedirs( destination_directory )
+ shutil.move( source_path, destination_path )
+
def remove_dir( dir ):
"""Attempt to remove a directory from disk."""
if dir:
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/util/commit_util.py
--- a/lib/tool_shed/util/commit_util.py
+++ b/lib/tool_shed/util/commit_util.py
@@ -12,6 +12,7 @@
from galaxy.util.odict import odict
from galaxy.web import url_for
import tool_shed.util.shed_util_common as suc
+from tool_shed.util import basic_util
from tool_shed.util import hg_util
from tool_shed.util import tool_util
from tool_shed.util import xml_util
@@ -126,7 +127,7 @@
bzipped_file = bz2.BZ2File( uploaded_file_name, 'rb' )
while 1:
try:
- chunk = bzipped_file.read( suc.CHUNK_SIZE )
+ chunk = bzipped_file.read( basic_util.CHUNK_SIZE )
except IOError:
os.close( fd )
os.remove( uncompressed )
@@ -239,7 +240,7 @@
gzipped_file = gzip.GzipFile( uploaded_file_name, 'rb' )
while 1:
try:
- chunk = gzipped_file.read( suc.CHUNK_SIZE )
+ chunk = gzipped_file.read( basic_util.CHUNK_SIZE )
except IOError, e:
os.close( fd )
os.remove( uncompressed )
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/util/hg_util.py
--- a/lib/tool_shed/util/hg_util.py
+++ b/lib/tool_shed/util/hg_util.py
@@ -4,6 +4,7 @@
from datetime import datetime
from time import gmtime
from time import strftime
+import tempfile
from galaxy.util import listify
from galaxy import eggs
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/util/metadata_util.py
--- a/lib/tool_shed/util/metadata_util.py
+++ b/lib/tool_shed/util/metadata_util.py
@@ -22,11 +22,12 @@
from tool_shed.util import tool_dependency_util
from tool_shed.util import tool_util
from tool_shed.util import xml_util
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
import tool_shed.repository_types.util as rt_util
log = logging.getLogger( __name__ )
+REPOSITORY_DATA_MANAGER_CONFIG_FILENAME = 'data_manager_conf.xml'
+
# Repository metadata comparisons for changeset revisions.
EQUAL = 'equal'
NO_METADATA = 'no metadata'
@@ -37,7 +38,7 @@
NOT_TOOL_CONFIGS = [ suc.DATATYPES_CONFIG_FILENAME,
rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME,
rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME,
- suc.REPOSITORY_DATA_MANAGER_CONFIG_FILENAME ]
+ REPOSITORY_DATA_MANAGER_CONFIG_FILENAME ]
def add_tool_versions( trans, id, repository_metadata, changeset_revisions ):
# Build a dictionary of { 'tool id' : 'parent tool id' } pairs for each tool in repository_metadata.
@@ -750,7 +751,7 @@
metadata_dict = generate_data_manager_metadata( app,
repository,
files_dir,
- hg_util.get_config_from_disk( suc.REPOSITORY_DATA_MANAGER_CONFIG_FILENAME, files_dir ),
+ hg_util.get_config_from_disk( REPOSITORY_DATA_MANAGER_CONFIG_FILENAME, files_dir ),
metadata_dict,
shed_config_dict=shed_config_dict )
@@ -809,10 +810,10 @@
if package_install_version == '1.0':
# Complex repository dependencies can be defined within the last <actions> tag set contained in an
# <actions_group> tag set. Comments, <repository> tag sets and <readme> tag sets will be skipped
- # in td_common_util.parse_package_elem().
- actions_elem_tuples = td_common_util.parse_package_elem( sub_elem,
- platform_info_dict=None,
- include_after_install_actions=False )
+ # in tool_dependency_util.parse_package_elem().
+ actions_elem_tuples = tool_dependency_util.parse_package_elem( sub_elem,
+ platform_info_dict=None,
+ include_after_install_actions=False )
if actions_elem_tuples:
# We now have a list of a single tuple that looks something like:
# [(True, <Element 'actions' at 0x104017850>)]
diff -r f8c6577665c383645e934629784ece516b498d9b -r 85aa79e9ab9a87a5b5fea0ed1039aeb9a5c23642 lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -24,11 +24,8 @@
log = logging.getLogger( __name__ )
-CHUNK_SIZE = 2**20 # 1Mb
MAX_CONTENT_SIZE = 1048576
-MAXDIFFSIZE = 8000
DATATYPES_CONFIG_FILENAME = 'datatypes_conf.xml'
-REPOSITORY_DATA_MANAGER_CONFIG_FILENAME = 'data_manager_conf.xml'
new_repo_email_alert_template = """
Sharable link: ${sharable_link}
This diff is so big that we needed to truncate the remainder.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jmchilton: Collections: Add API ability to copy HDCA mirroring HDA operations.
by commits-noreply@bitbucket.org 28 May '14
by commits-noreply@bitbucket.org 28 May '14
28 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a06c6c99d74f/
Changeset: a06c6c99d74f
User: jmchilton
Date: 2014-05-28 22:28:55
Summary: Collections: Add API ability to copy HDCA mirroring HDA operations.
Affected #: 3 files
diff -r baeea4ce794df61d9e6531023b8e4c105b26a8bb -r a06c6c99d74f7aa902fb2c23e8c904df32a49791 lib/galaxy/dataset_collections/__init__.py
--- a/lib/galaxy/dataset_collections/__init__.py
+++ b/lib/galaxy/dataset_collections/__init__.py
@@ -138,6 +138,18 @@
changed = self._set_from_dict( trans, dataset_collection_instance, payload )
return changed
+ def copy(
+ self,
+ trans,
+ parent, # PRECONDITION: security checks on ability to add to parent occurred during load.
+ source,
+ encoded_source_id,
+ ):
+ assert source == "hdca" # for now
+ source_hdca = self.__get_history_collection_instance( trans, encoded_source_id )
+ parent.add_dataset_collection( source_hdca.copy() )
+ return source_hdca
+
def _set_from_dict( self, trans, dataset_collection_instance, new_data ):
# Blatantly stolen from UsesHistoryDatasetAssociationMixin.set_hda_from_dict.
diff -r baeea4ce794df61d9e6531023b8e4c105b26a8bb -r a06c6c99d74f7aa902fb2c23e8c904df32a49791 lib/galaxy/webapps/galaxy/api/history_contents.py
--- a/lib/galaxy/webapps/galaxy/api/history_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/history_contents.py
@@ -292,9 +292,28 @@
return hda_dict
def __create_dataset_collection( self, trans, history, payload, **kwd ):
- create_params = api_payload_to_create_params( payload )
+ source = kwd.get("source", "new_collection")
service = trans.app.dataset_collections_service
- dataset_collection_instance = service.create( trans, parent=history, **create_params )
+ if source == "new_collection":
+ create_params = api_payload_to_create_params( payload )
+ dataset_collection_instance = service.create(
+ trans,
+ parent=history,
+ **create_params
+ )
+ elif source == "hdca":
+ content = payload.get( 'content', None )
+ if content is None:
+ raise exceptions.RequestParameterMissingException( "'content' id of target to copy is missing" )
+ dataset_collection_instance = service.copy(
+ trans=trans,
+ parent=history,
+ source="hdca",
+ encoded_source_id=content,
+ )
+ else:
+ message = "Invalid 'source' parameter in request %s" % source
+ raise exceptions.RequestParameterInvalidException(message)
return self.__collection_dict( trans, dataset_collection_instance, view="element" )
@expose_api_anonymous
diff -r baeea4ce794df61d9e6531023b8e4c105b26a8bb -r a06c6c99d74f7aa902fb2c23e8c904df32a49791 test/api/test_history_contents.py
--- a/test/api/test_history_contents.py
+++ b/test/api/test_history_contents.py
@@ -97,9 +97,7 @@
dataset_collection_response = self._post( "histories/%s/contents" % self.history_id, payload )
- self._assert_status_code_is( dataset_collection_response, 200 )
- dataset_collection = dataset_collection_response.json()
- self._assert_has_keys( dataset_collection, "url", "name", "deleted" )
+ dataset_collection = self.__check_create_collection_response( dataset_collection_response )
post_collection_count = self.__count_contents( type="dataset_collection" )
post_dataset_count = self.__count_contents( type="dataset" )
@@ -144,6 +142,23 @@
show_response = self.__show( hdca )
assert str( show_response.json()[ "name" ] ) == "newnameforpair"
+ def test_hdca_copy( self ):
+ hdca = self.dataset_collection_populator.create_pair_in_history( self.history_id ).json()
+ hdca_id = hdca[ "id" ]
+ second_history_id = self._new_history()
+ create_data = dict(
+ source='hdca',
+ content=hdca_id,
+ )
+ create_response = self._post( "histories/%s/contents/dataset_collections" % second_history_id, create_data )
+ self.__check_create_collection_response( create_response )
+
+ def __check_create_collection_response( self, response ):
+ self._assert_status_code_is( response, 200 )
+ dataset_collection = response.json()
+ self._assert_has_keys( dataset_collection, "url", "name", "deleted", "visible", "elements" )
+ return dataset_collection
+
def __show( self, contents ):
show_response = self._get( "histories/%s/contents/%ss/%s" % ( self.history_id, contents["history_content_type"], contents[ "id" ] ) )
return show_response
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Import fix in the Tool Shed's hg_util.py module.
by commits-noreply@bitbucket.org 28 May '14
by commits-noreply@bitbucket.org 28 May '14
28 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/baeea4ce794d/
Changeset: baeea4ce794d
User: greg
Date: 2014-05-28 22:06:46
Summary: Import fix in the Tool Shed's hg_util.py module.
Affected #: 1 file
diff -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c -r baeea4ce794df61d9e6531023b8e4c105b26a8bb lib/tool_shed/util/hg_util.py
--- a/lib/tool_shed/util/hg_util.py
+++ b/lib/tool_shed/util/hg_util.py
@@ -4,6 +4,7 @@
from datetime import datetime
from time import gmtime
from time import strftime
+import tempfile
from galaxy.util import listify
from galaxy import eggs
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Eliminate the Tool Shed's td_common_util.py module by moving the functions to appropriate classes.
by commits-noreply@bitbucket.org 28 May '14
by commits-noreply@bitbucket.org 28 May '14
28 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/b1b1e5cefca5/
Changeset: b1b1e5cefca5
User: greg
Date: 2014-05-28 21:49:02
Summary: Eliminate the Tool Shed's td_common_util.py module by moving the functions to appropriate classes.
Affected #: 15 files
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -3120,12 +3120,12 @@
options_dict = hg_util.get_mercurial_default_options_dict( 'diff' )
# Not quite sure if the following settings make any difference, but with a combination of them and the size check on each
# diff, we don't run out of memory when viewing the changelog of the cisortho2 repository on the test tool shed.
- options_dict[ 'maxfile' ] = suc.MAXDIFFSIZE
- options_dict[ 'maxtotal' ] = suc.MAXDIFFSIZE
+ options_dict[ 'maxfile' ] = basic_util.MAXDIFFSIZE
+ options_dict[ 'maxtotal' ] = basic_util.MAXDIFFSIZE
diffopts = mdiff.diffopts( **options_dict )
for diff in patch.diff( repo, node1=ctx_parent.node(), node2=ctx.node(), opts=diffopts ):
- if len( diff ) > suc.MAXDIFFSIZE:
- diff = util.shrink_string_by_size( diff, suc.MAXDIFFSIZE )
+ if len( diff ) > basic_util.MAXDIFFSIZE:
+ diff = util.shrink_string_by_size( diff, basic_util.MAXDIFFSIZE )
diffs.append( basic_util.to_html_string( diff ) )
modified, added, removed, deleted, unknown, ignored, clean = repo.status( node1=ctx_parent.node(), node2=ctx.node() )
anchors = modified + added + removed + deleted + unknown + ignored + clean
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/galaxy_install/install_manager.py
--- a/lib/tool_shed/galaxy_install/install_manager.py
+++ b/lib/tool_shed/galaxy_install/install_manager.py
@@ -1,5 +1,7 @@
import logging
import os
+import sys
+import traceback
from galaxy import eggs
@@ -11,7 +13,6 @@
from tool_shed.util import tool_dependency_util
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
from tool_shed.galaxy_install.tool_dependencies.recipe.env_file_builder import EnvFileBuilder
from tool_shed.galaxy_install.tool_dependencies.recipe.install_environment import InstallEnvironment
from tool_shed.galaxy_install.tool_dependencies.recipe.recipe_manager import StepManager
@@ -25,6 +26,10 @@
class InstallManager( object ):
+ def format_traceback( self ):
+ ex_type, ex, tb = sys.exc_info()
+ return ''.join( traceback.format_tb( tb ) )
+
def get_tool_shed_repository_install_dir( self, app, tool_shed_repository ):
return os.path.abspath( tool_shed_repository.repo_files_directory( app ) )
@@ -112,7 +117,7 @@
log.exception( 'Error installing tool dependency %s version %s.', str( tool_dependency.name ), str( tool_dependency.version ) )
# Since there was an installation error, update the tool dependency status to Error. The remove_installation_path option must
# be left False here.
- error_message = '%s\n%s' % ( td_common_util.format_traceback(), str( e ) )
+ error_message = '%s\n%s' % ( self.format_traceback(), str( e ) )
tool_dependency = tool_dependency_util.handle_tool_dependency_installation_error( app,
tool_dependency,
error_message,
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/galaxy_install/tool_dependencies/env_manager.py
--- /dev/null
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/env_manager.py
@@ -0,0 +1,147 @@
+import logging
+import os
+import sys
+from tool_shed.util import common_util
+import tool_shed.util.shed_util_common as suc
+
+log = logging.getLogger( __name__ )
+
+
+class EnvManager( object ):
+
+ def __init__( self, app ):
+ self.app = app
+
+ def create_env_var_dict( self, elem, install_environment ):
+ env_var_name = elem.get( 'name', 'PATH' )
+ env_var_action = elem.get( 'action', 'prepend_to' )
+ env_var_text = None
+ tool_dependency_install_dir = install_environment.install_dir
+ tool_shed_repository_install_dir = install_environment.tool_shed_repository_install_dir
+ if elem.text and elem.text.find( 'REPOSITORY_INSTALL_DIR' ) >= 0:
+ if tool_shed_repository_install_dir and elem.text.find( '$REPOSITORY_INSTALL_DIR' ) != -1:
+ env_var_text = elem.text.replace( '$REPOSITORY_INSTALL_DIR', tool_shed_repository_install_dir )
+ return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ else:
+ env_var_text = elem.text.replace( '$REPOSITORY_INSTALL_DIR', tool_dependency_install_dir )
+ return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ if elem.text and elem.text.find( 'INSTALL_DIR' ) >= 0:
+ if tool_dependency_install_dir:
+ env_var_text = elem.text.replace( '$INSTALL_DIR', tool_dependency_install_dir )
+ return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ else:
+ env_var_text = elem.text.replace( '$INSTALL_DIR', tool_shed_repository_install_dir )
+ return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ if elem.text:
+ # Allow for environment variables that contain neither REPOSITORY_INSTALL_DIR nor INSTALL_DIR
+ # since there may be command line parameters that are tuned for a Galaxy instance. Allowing them
+ # to be set in one location rather than being hard coded into each tool config is the best approach.
+ # For example:
+ # <environment_variable name="GATK2_SITE_OPTIONS" action="set_to">
+ # "--num_threads 4 --num_cpu_threads_per_data_thread 3 --phone_home STANDARD"
+ # </environment_variable>
+ return dict( name=env_var_name, action=env_var_action, value=elem.text)
+ return None
+
+ def get_env_shell_file_path( self, installation_directory ):
+ env_shell_file_name = 'env.sh'
+ default_location = os.path.abspath( os.path.join( installation_directory, env_shell_file_name ) )
+ if os.path.exists( default_location ):
+ return default_location
+ for root, dirs, files in os.walk( installation_directory ):
+ for name in files:
+ if name == env_shell_file_name:
+ return os.path.abspath( os.path.join( root, name ) )
+ return None
+
+ def get_env_shell_file_paths( self, elem ):
+ # Currently only the following tag set is supported.
+ # <repository toolshed="http://localhost:9009/" name="package_numpy_1_7" owner="test" changeset_revision="c84c6a8be056">
+ # <package name="numpy" version="1.7.1" />
+ # </repository>
+ env_shell_file_paths = []
+ toolshed = elem.get( 'toolshed', None )
+ repository_name = elem.get( 'name', None )
+ repository_owner = elem.get( 'owner', None )
+ changeset_revision = elem.get( 'changeset_revision', None )
+ if toolshed and repository_name and repository_owner and changeset_revision:
+ # The protocol is not stored, but the port is if it exists.
+ toolshed = common_util.remove_protocol_from_tool_shed_url( toolshed )
+ repository = suc.get_repository_for_dependency_relationship( self.app,
+ toolshed,
+ repository_name,
+ repository_owner,
+ changeset_revision )
+ if repository:
+ for sub_elem in elem:
+ tool_dependency_type = sub_elem.tag
+ tool_dependency_name = sub_elem.get( 'name' )
+ tool_dependency_version = sub_elem.get( 'version' )
+ if tool_dependency_type and tool_dependency_name and tool_dependency_version:
+ # Get the tool_dependency so we can get its installation directory.
+ tool_dependency = None
+ for tool_dependency in repository.tool_dependencies:
+ if tool_dependency.type == tool_dependency_type and \
+ tool_dependency.name == tool_dependency_name and \
+ tool_dependency.version == tool_dependency_version:
+ break
+ if tool_dependency:
+ tool_dependency_key = '%s/%s' % ( tool_dependency_name, tool_dependency_version )
+ installation_directory = tool_dependency.installation_directory( self.app )
+ env_shell_file_path = self.get_env_shell_file_path( installation_directory )
+ if env_shell_file_path:
+ env_shell_file_paths.append( env_shell_file_path )
+ else:
+ error_message = "Skipping tool dependency definition because unable to locate env.sh file for tool dependency "
+ error_message += "type %s, name %s, version %s for repository %s" % \
+ ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ), str( repository.name ) )
+ log.debug( error_message )
+ continue
+ else:
+ error_message = "Skipping tool dependency definition because unable to locate tool dependency "
+ error_message += "type %s, name %s, version %s for repository %s" % \
+ ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ), str( repository.name ) )
+ log.debug( error_message )
+ continue
+ else:
+ error_message = "Skipping invalid tool dependency definition: type %s, name %s, version %s." % \
+ ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ) )
+ log.debug( error_message )
+ continue
+ else:
+ error_message = "Skipping set_environment_for_install definition because unable to locate required installed tool shed repository: "
+ error_message += "toolshed %s, name %s, owner %s, changeset_revision %s." % \
+ ( str( toolshed ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
+ log.debug( error_message )
+ else:
+ error_message = "Skipping invalid set_environment_for_install definition: toolshed %s, name %s, owner %s, changeset_revision %s." % \
+ ( str( toolshed ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
+ log.debug( error_message )
+ return env_shell_file_paths
+
+ def get_env_shell_file_paths_from_setup_environment_elem( self, all_env_shell_file_paths, elem, action_dict ):
+ """
+ Parse an XML tag set to discover all child repository dependency tags and define the path to an env.sh file associated
+ with the repository (this requires the repository dependency to be in an installed state). The received action_dict
+ will be updated with these discovered paths and returned to the caller. This method handles tool dependency definition
+ tag sets <setup_r_environment>, <setup_ruby_environment> and <setup_perl_environment>.
+ """
+ # An example elem is:
+ # <action type="setup_perl_environment">
+ # <repository name="package_perl_5_18" owner="iuc">
+ # <package name="perl" version="5.18.1" />
+ # </repository>
+ # <repository name="package_expat_2_1" owner="iuc" prior_installation_required="True">
+ # <package name="expat" version="2.1.0" />
+ # </repository>
+ # <package>http://search.cpan.org/CPAN/authors/id/T/TO/TODDR/XML-Parser-2.41.tar.gz</package>
+ # <package>http://search.cpan.org/CPAN/authors/id/L/LD/LDS/CGI.pm-3.43.tar.gz</package>
+ # </action>
+ for action_elem in elem:
+ if action_elem.tag == 'repository':
+ env_shell_file_paths = self.get_env_shell_file_paths( action_elem )
+ all_env_shell_file_paths.extend( env_shell_file_paths )
+ if all_env_shell_file_paths:
+ action_dict[ 'env_shell_file_paths' ] = all_env_shell_file_paths
+ action_dict[ 'action_shell_file_paths' ] = env_shell_file_paths
+ return action_dict
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/galaxy_install/tool_dependencies/recipe/install_environment.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/install_environment.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/install_environment.py
@@ -24,11 +24,13 @@
from galaxy.util import shrink_string_by_size
from galaxy.util import unicodify
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
from tool_shed.galaxy_install.tool_dependencies.recipe import asynchronous_reader
+from tool_shed.util import basic_util
+
log = logging.getLogger( __name__ )
+
class InstallEnvironment( object ):
"""Object describing the environment built up as part of the process of building and installing a package."""
@@ -44,7 +46,7 @@
self.tool_shed_repository_install_dir = tool_shed_repository_install_dir
def __call__( self ):
- with settings( warn_only=True, **td_common_util.get_env_var_values( self ) ):
+ with settings( warn_only=True, **basic_util.get_env_var_values( self ) ):
with prefix( self.__setup_environment() ):
yield
@@ -125,7 +127,7 @@
context = app.install_model.context
command = str( cmd )
output = self.handle_complex_command( command )
- self.log_results( cmd, output, os.path.join( self.install_dir, td_common_util.INSTALLATION_LOG ) )
+ self.log_results( cmd, output, os.path.join( self.install_dir, basic_util.INSTALLATION_LOG ) )
stdout = output.stdout
stderr = output.stderr
if len( stdout ) > DATABASE_MAX_STRING_SIZE:
@@ -214,18 +216,18 @@
# Sleep a bit before asking the readers again.
time.sleep( .1 )
current_wait_time = time.time() - start_timer
- if stdout_queue.empty() and stderr_queue.empty() and current_wait_time > td_common_util.NO_OUTPUT_TIMEOUT:
+ if stdout_queue.empty() and stderr_queue.empty() and current_wait_time > basic_util.NO_OUTPUT_TIMEOUT:
err_msg = "\nShutting down process id %s because it generated no output for the defined timeout period of %.1f seconds.\n" % \
- ( pid, td_common_util.NO_OUTPUT_TIMEOUT )
+ ( pid, basic_util.NO_OUTPUT_TIMEOUT )
stderr_reader.lines.append( err_msg )
process_handle.kill()
break
thread_lock.release()
# Wait until each of the threads we've started terminate. The following calls will block each thread
# until it terminates either normally, through an unhandled exception, or until the timeout occurs.
- stdio_thread.join( td_common_util.NO_OUTPUT_TIMEOUT )
- stdout_reader.join( td_common_util.NO_OUTPUT_TIMEOUT )
- stderr_reader.join( td_common_util.NO_OUTPUT_TIMEOUT )
+ stdio_thread.join( basic_util.NO_OUTPUT_TIMEOUT )
+ stdout_reader.join( basic_util.NO_OUTPUT_TIMEOUT )
+ stderr_reader.join( basic_util.NO_OUTPUT_TIMEOUT )
# Close subprocess' file descriptors.
error = self.close_file_descriptor( process_handle.stdout )
error = self.close_file_descriptor( process_handle.stderr )
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py
@@ -4,12 +4,17 @@
import stat
from string import Template
import sys
+import tarfile
+import time
+import urllib2
+import zipfile
from galaxy.util import asbool
from galaxy.util.template import fill_template
+from tool_shed.util import basic_util
from tool_shed.util import tool_dependency_util
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
+from tool_shed.galaxy_install.tool_dependencies.env_manager import EnvManager
# TODO: eliminate the use of fabric here.
from galaxy import eggs
@@ -26,6 +31,152 @@
VIRTUALENV_URL = 'https://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.9.1.tar.gz'
+class CompressedFile( object ):
+
+ def __init__( self, file_path, mode='r' ):
+ if tarfile.is_tarfile( file_path ):
+ self.file_type = 'tar'
+ elif zipfile.is_zipfile( file_path ) and not file_path.endswith( '.jar' ):
+ self.file_type = 'zip'
+ self.file_name = os.path.splitext( os.path.basename( file_path ) )[ 0 ]
+ if self.file_name.endswith( '.tar' ):
+ self.file_name = os.path.splitext( self.file_name )[ 0 ]
+ self.type = self.file_type
+ method = 'open_%s' % self.file_type
+ if hasattr( self, method ):
+ self.archive = getattr( self, method )( file_path, mode )
+ else:
+ raise NameError( 'File type %s specified, no open method found.' % self.file_type )
+
+ def extract( self, path ):
+ '''Determine the path to which the archive should be extracted.'''
+ contents = self.getmembers()
+ extraction_path = path
+ if len( contents ) == 1:
+ # The archive contains a single file, return the extraction path.
+ if self.isfile( contents[ 0 ] ):
+ extraction_path = os.path.join( path, self.file_name )
+ if not os.path.exists( extraction_path ):
+ os.makedirs( extraction_path )
+ self.archive.extractall( extraction_path )
+ else:
+ # Get the common prefix for all the files in the archive. If the common prefix ends with a slash,
+ # or self.isdir() returns True, the archive contains a single directory with the desired contents.
+ # Otherwise, it contains multiple files and/or directories at the root of the archive.
+ common_prefix = os.path.commonprefix( [ self.getname( item ) for item in contents ] )
+ if len( common_prefix ) >= 1 and not common_prefix.endswith( os.sep ) and self.isdir( self.getmember( common_prefix ) ):
+ common_prefix += os.sep
+ if common_prefix.endswith( os.sep ):
+ self.archive.extractall( os.path.join( path ) )
+ extraction_path = os.path.join( path, common_prefix )
+ else:
+ extraction_path = os.path.join( path, self.file_name )
+ if not os.path.exists( extraction_path ):
+ os.makedirs( extraction_path )
+ self.archive.extractall( os.path.join( extraction_path ) )
+ return os.path.abspath( extraction_path )
+
+ def getmembers_tar( self ):
+ return self.archive.getmembers()
+
+ def getmembers_zip( self ):
+ return self.archive.infolist()
+
+ def getname_tar( self, item ):
+ return item.name
+
+ def getname_zip( self, item ):
+ return item.filename
+
+ def getmember( self, name ):
+ for member in self.getmembers():
+ if self.getname( member ) == name:
+ return member
+
+ def getmembers( self ):
+ return getattr( self, 'getmembers_%s' % self.type )()
+
+ def getname( self, member ):
+ return getattr( self, 'getname_%s' % self.type )( member )
+
+ def isdir( self, member ):
+ return getattr( self, 'isdir_%s' % self.type )( member )
+
+ def isdir_tar( self, member ):
+ return member.isdir()
+
+ def isdir_zip( self, member ):
+ if member.filename.endswith( os.sep ):
+ return True
+ return False
+
+ def isfile( self, member ):
+ if not self.isdir( member ):
+ return True
+ return False
+
+ def open_tar( self, filepath, mode ):
+ return tarfile.open( filepath, mode, errorlevel=0 )
+
+ def open_zip( self, filepath, mode ):
+ return zipfile.ZipFile( filepath, mode )
+
+ def zipfile_ok( self, path_to_archive ):
+ """
+ This function is a bit pedantic and not functionally necessary. It checks whether there is
+ no file pointing outside of the extraction, because ZipFile.extractall() has some potential
+ security holes. See python zipfile documentation for more details.
+ """
+ basename = os.path.realpath( os.path.dirname( path_to_archive ) )
+ zip_archive = zipfile.ZipFile( path_to_archive )
+ for member in zip_archive.namelist():
+ member_path = os.path.realpath( os.path.join( basename, member ) )
+ if not member_path.startswith( basename ):
+ return False
+ return True
+
+
+class Download( object ):
+
+ def url_download( self, install_dir, downloaded_file_name, download_url, extract=True ):
+ file_path = os.path.join( install_dir, downloaded_file_name )
+ src = None
+ dst = None
+ # Set a timer so we don't sit here forever.
+ start_time = time.time()
+ try:
+ src = urllib2.urlopen( download_url )
+ dst = open( file_path, 'wb' )
+ while True:
+ chunk = src.read( basic_util.CHUNK_SIZE )
+ if chunk:
+ dst.write( chunk )
+ else:
+ break
+ time_taken = time.time() - start_time
+ if time_taken > basic_util.NO_OUTPUT_TIMEOUT:
+ err_msg = 'Downloading from URL %s took longer than the defined timeout period of %.1f seconds.' % \
+ ( str( download_url ), basic_util.NO_OUTPUT_TIMEOUT )
+ raise Exception( err_msg )
+ except Exception, e:
+ err_msg = err_msg = 'Error downloading from URL\n%s:\n%s' % ( str( download_url ), str( e ) )
+ raise Exception( err_msg )
+ finally:
+ if src:
+ src.close()
+ if dst:
+ dst.close()
+ if extract:
+ if tarfile.is_tarfile( file_path ) or ( zipfile.is_zipfile( file_path ) and not file_path.endswith( '.jar' ) ):
+ archive = CompressedFile( file_path )
+ extraction_path = archive.extract( install_dir )
+ else:
+ extraction_path = os.path.abspath( install_dir )
+ else:
+ extraction_path = os.path.abspath( install_dir )
+ return extraction_path
+
+
class RecipeStep( object ):
"""Abstract class that defines a standard format for handling recipe steps when installing packages."""
@@ -42,6 +193,22 @@
def __init__( self ):
self.type = 'assert_directory_executable'
+ def assert_directory_executable( self, full_path ):
+ """
+ Return True if a symbolic link or directory exists and is executable, but if
+ full_path is a file, return False.
+ """
+ if full_path is None:
+ return False
+ if os.path.isfile( full_path ):
+ return False
+ if os.path.isdir( full_path ):
+ # Make sure the owner has execute permission on the directory.
+ # See http://docs.python.org/2/library/stat.html
+ if stat.S_IXUSR & os.stat( full_path )[ stat.ST_MODE ] == 64:
+ return True
+ return False
+
def execute_step( self, app, tool_dependency, package_name, actions, action_dict, filtered_actions, env_file_builder,
install_environment, work_dir, current_dir=None, initial_download=False ):
"""
@@ -53,7 +220,7 @@
full_path = action_dict[ 'full_path' ]
else:
full_path = os.path.join( current_dir, action_dict[ 'full_path' ] )
- if not td_common_util.assert_directory_executable( full_path=full_path ):
+ if not self.assert_directory_executable( full_path=full_path ):
status = app.install_model.ToolDependency.installation_status.ERROR
error_message = 'The path %s is not a directory or is not executable by the owner.' % str( full_path )
tool_dependency = tool_dependency_util.set_tool_dependency_attributes( app,
@@ -66,7 +233,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="assert_executable">$INSTALL_DIR/mira/my_file</action>
if action_elem.text:
- action_dict[ 'full_path' ] = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_dict[ 'full_path' ] = basic_util.evaluate_template( action_elem.text, install_environment )
return action_dict
@@ -75,6 +242,18 @@
def __init__( self ):
self.type = 'assert_directory_exists'
+ def assert_directory_exists( self, full_path ):
+ """
+ Return True if a symbolic link or directory exists, but if full_path is a file,
+ return False. """
+ if full_path is None:
+ return False
+ if os.path.isfile( full_path ):
+ return False
+ if os.path.isdir( full_path ):
+ return True
+ return False
+
def execute_step( self, app, tool_dependency, package_name, actions, action_dict, filtered_actions, env_file_builder,
install_environment, work_dir, current_dir=None, initial_download=False ):
"""
@@ -86,7 +265,7 @@
full_path = action_dict[ 'full_path' ]
else:
full_path = os.path.join( current_dir, action_dict[ 'full_path' ] )
- if not td_common_util.assert_directory_exists( full_path=full_path ):
+ if not self.assert_directory_exists( full_path=full_path ):
status = app.install_model.ToolDependency.installation_status.ERROR
error_message = 'The path %s is not a directory or does not exist.' % str( full_path )
tool_dependency = tool_dependency_util.set_tool_dependency_attributes( app,
@@ -99,7 +278,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="make_directory">$INSTALL_DIR/mira</action>
if action_elem.text:
- action_dict[ 'full_path' ] = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_dict[ 'full_path' ] = basic_util.evaluate_template( action_elem.text, install_environment )
return action_dict
@@ -108,6 +287,22 @@
def __init__( self ):
self.type = 'assert_file_executable'
+ def assert_file_executable( self, full_path ):
+ """
+ Return True if a symbolic link or file exists and is executable, but if full_path
+ is a directory, return False.
+ """
+ if full_path is None:
+ return False
+ if os.path.isdir( full_path ):
+ return False
+ if os.path.exists( full_path ):
+ # Make sure the owner has execute permission on the file.
+ # See http://docs.python.org/2/library/stat.html
+ if stat.S_IXUSR & os.stat( full_path )[ stat.ST_MODE ] == 64:
+ return True
+ return False
+
def execute_step( self, app, tool_dependency, package_name, actions, action_dict, filtered_actions, env_file_builder,
install_environment, work_dir, current_dir=None, initial_download=False ):
"""
@@ -119,7 +314,7 @@
full_path = action_dict[ 'full_path' ]
else:
full_path = os.path.join( current_dir, action_dict[ 'full_path' ] )
- if not td_common_util.assert_file_executable( full_path=full_path ):
+ if not self.assert_file_executable( full_path=full_path ):
status = app.install_model.ToolDependency.installation_status.ERROR
error_message = 'The path %s is not a file or is not executable by the owner.' % str( full_path )
tool_dependency = tool_dependency_util.set_tool_dependency_attributes( app,
@@ -132,7 +327,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="assert_executable">$INSTALL_DIR/mira/my_file</action>
if action_elem.text:
- action_dict[ 'full_path' ] = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_dict[ 'full_path' ] = basic_util.evaluate_template( action_elem.text, install_environment )
return action_dict
@@ -141,6 +336,19 @@
def __init__( self ):
self.type = 'assert_file_exists'
+ def assert_file_exists( self, full_path ):
+ """
+ Return True if a symbolic link or file exists, but if full_path is a directory,
+ return False.
+ """
+ if full_path is None:
+ return False
+ if os.path.isdir( full_path ):
+ return False
+ if os.path.exists( full_path ):
+ return True
+ return False
+
def execute_step( self, app, tool_dependency, package_name, actions, action_dict, filtered_actions, env_file_builder,
install_environment, work_dir, current_dir=None, initial_download=False ):
"""
@@ -152,7 +360,7 @@
full_path = action_dict[ 'full_path' ]
else:
full_path = os.path.join( current_dir, action_dict[ 'full_path' ] )
- if not td_common_util.assert_file_exists( full_path=full_path ):
+ if not self.assert_file_exists( full_path=full_path ):
status = app.install_model.ToolDependency.installation_status.ERROR
error_message = 'The path %s is not a file or does not exist.' % str( full_path )
tool_dependency = tool_dependency_util.set_tool_dependency_attributes( app,
@@ -165,7 +373,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="assert_on_path">$INSTALL_DIR/mira/my_file</action>
if action_elem.text:
- action_dict[ 'full_path' ] = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_dict[ 'full_path' ] = basic_util.evaluate_template( action_elem.text, install_environment )
return action_dict
@@ -187,7 +395,7 @@
pre_cmd = './configure %s && make && make install' % configure_opts
else:
pre_cmd = './configure --prefix=$INSTALL_DIR %s && make && make install' % configure_opts
- cmd = install_environment.build_command( td_common_util.evaluate_template( pre_cmd, install_environment ) )
+ cmd = install_environment.build_command( basic_util.evaluate_template( pre_cmd, install_environment ) )
return_code = install_environment.handle_command( app=app,
tool_dependency=tool_dependency,
cmd=cmd,
@@ -199,7 +407,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# Handle configure, make and make install allow providing configuration options
if action_elem.text:
- configure_opts = td_common_util.evaluate_template( action_elem.text, install_environment )
+ configure_opts = basic_util.evaluate_template( action_elem.text, install_environment )
action_dict[ 'configure_opts' ] = configure_opts
return action_dict
@@ -274,7 +482,7 @@
received_mode = int( file_elem.get( 'mode', 600 ), base=8 )
# For added security, ensure that the setuid and setgid bits are not set.
mode = received_mode & ~( stat.S_ISUID | stat.S_ISGID )
- file = td_common_util.evaluate_template( file_elem.text, install_environment )
+ file = basic_util.evaluate_template( file_elem.text, install_environment )
chmod_tuple = ( file, mode )
chmod_actions.append( chmod_tuple )
if chmod_actions:
@@ -282,11 +490,17 @@
return action_dict
-class DownloadBinary( RecipeStep ):
+class DownloadBinary( Download, RecipeStep ):
def __init__( self ):
self.type = 'download_binary'
+ def download_binary( self, url, work_dir ):
+ """Download a pre-compiled binary from the specified URL."""
+ downloaded_filename = os.path.split( url )[ -1 ]
+ dir = self.url_download( work_dir, downloaded_filename, url, extract=False )
+ return downloaded_filename
+
def filter_actions_after_binary_installation( self, actions ):
'''Filter out actions that should not be processed if a binary download succeeded.'''
filtered_actions = []
@@ -311,7 +525,7 @@
log.debug( 'Attempting to download from %s to %s', url, str( target_directory ) )
downloaded_filename = None
try:
- downloaded_filename = td_common_util.download_binary( url, work_dir )
+ downloaded_filename = self.download_binary( url, work_dir )
if initial_download:
# Filter out any actions that are not download_binary, chmod, or set_environment.
filtered_actions = self.filter_actions_after_binary_installation( actions[ 1: ] )
@@ -338,9 +552,9 @@
full_path_to_dir = os.path.abspath( install_environment.install_dir )
else:
full_path_to_dir = os.path.abspath( install_environment.install_dir )
- td_common_util.move_file( current_dir=work_dir,
- source=downloaded_filename,
- destination=full_path_to_dir )
+ basic_util.move_file( current_dir=work_dir,
+ source=downloaded_filename,
+ destination=full_path_to_dir )
# Not sure why dir is ignored in this method, need to investigate...
dir = None
if initial_download:
@@ -368,7 +582,7 @@
return action_dict
-class DownloadByUrl( RecipeStep ):
+class DownloadByUrl( Download, RecipeStep ):
def __init__( self ):
self.type = 'download_by_url'
@@ -394,9 +608,9 @@
downloaded_filename = action_dict[ 'target_filename' ]
else:
downloaded_filename = os.path.split( url )[ -1 ]
- dir = td_common_util.url_download( work_dir, downloaded_filename, url, extract=True )
+ dir = self.url_download( work_dir, downloaded_filename, url, extract=True )
if is_binary:
- log_file = os.path.join( install_environment.install_dir, td_common_util.INSTALLATION_LOG )
+ log_file = os.path.join( install_environment.install_dir, basic_util.INSTALLATION_LOG )
if os.path.exists( log_file ):
logfile = open( log_file, 'ab' )
else:
@@ -422,7 +636,7 @@
return action_dict
-class DownloadFile( RecipeStep ):
+class DownloadFile( Download, RecipeStep ):
def __init__( self ):
self.type = 'download_file'
@@ -447,7 +661,7 @@
filename = action_dict[ 'target_filename' ]
else:
filename = url.split( '/' )[ -1 ]
- td_common_util.url_download( work_dir, filename, url )
+ self.url_download( work_dir, filename, url )
if initial_download:
dir = os.path.curdir
return tool_dependency, filtered_actions, dir
@@ -479,13 +693,17 @@
full_path = action_dict[ 'full_path' ]
else:
full_path = os.path.join( current_dir, action_dict[ 'full_path' ] )
- td_common_util.make_directory( full_path=full_path )
+ self.make_directory( full_path=full_path )
return tool_dependency, None, None
+ def make_directory( self, full_path ):
+ if not os.path.exists( full_path ):
+ os.makedirs( full_path )
+
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="make_directory">$INSTALL_DIR/lib/python</action>
if action_elem.text:
- action_dict[ 'full_path' ] = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_dict[ 'full_path' ] = basic_util.evaluate_template( action_elem.text, install_environment )
return action_dict
@@ -515,7 +733,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# make; make install; allow providing make options
if action_elem.text:
- make_opts = td_common_util.evaluate_template( action_elem.text, install_environment )
+ make_opts = basic_util.evaluate_template( action_elem.text, install_environment )
action_dict[ 'make_opts' ] = make_opts
return action_dict
@@ -531,18 +749,38 @@
Move a directory of files. Since this class is not used in the initial download stage, no recipe step
filtering is performed here, and None values are always returned for filtered_actions and dir.
"""
- td_common_util.move_directory_files( current_dir=current_dir,
- source_dir=os.path.join( action_dict[ 'source_directory' ] ),
- destination_dir=os.path.join( action_dict[ 'destination_directory' ] ) )
+ self.move_directory_files( current_dir=current_dir,
+ source_dir=os.path.join( action_dict[ 'source_directory' ] ),
+ destination_dir=os.path.join( action_dict[ 'destination_directory' ] ) )
return tool_dependency, None, None
+ def move_directory_files( self, current_dir, source_dir, destination_dir ):
+ source_directory = os.path.abspath( os.path.join( current_dir, source_dir ) )
+ destination_directory = os.path.join( destination_dir )
+ if not os.path.isdir( destination_directory ):
+ os.makedirs( destination_directory )
+ symlinks = []
+ regular_files = []
+ for file_name in os.listdir( source_directory ):
+ source_file = os.path.join( source_directory, file_name )
+ destination_file = os.path.join( destination_directory, file_name )
+ files_tuple = ( source_file, destination_file )
+ if os.path.islink( source_file ):
+ symlinks.append( files_tuple )
+ else:
+ regular_files.append( files_tuple )
+ for source_file, destination_file in symlinks:
+ shutil.move( source_file, destination_file )
+ for source_file, destination_file in regular_files:
+ shutil.move( source_file, destination_file )
+
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="move_directory_files">
# <source_directory>bin</source_directory>
# <destination_directory>$INSTALL_DIR/bin</destination_directory>
# </action>
for move_elem in action_elem:
- move_elem_text = td_common_util.evaluate_template( move_elem.text, install_environment )
+ move_elem_text = basic_util.evaluate_template( move_elem.text, install_environment )
if move_elem_text:
action_dict[ move_elem.tag ] = move_elem_text
return action_dict
@@ -559,10 +797,10 @@
Move a file on disk. Since this class is not used in the initial download stage, no recipe step
filtering is performed here, and None values are always returned for filtered_actions and dir.
"""
- td_common_util.move_file( current_dir=current_dir,
- source=os.path.join( action_dict[ 'source' ] ),
- destination=os.path.join( action_dict[ 'destination' ] ),
- rename_to=action_dict[ 'rename_to' ] )
+ basic_util.move_file( current_dir=current_dir,
+ source=os.path.join( action_dict[ 'source' ] ),
+ destination=os.path.join( action_dict[ 'destination' ] ),
+ rename_to=action_dict[ 'rename_to' ] )
return tool_dependency, None, None
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
@@ -570,8 +808,8 @@
# <source>misc/some_file</source>
# <destination>$INSTALL_DIR/bin</destination>
# </action>
- action_dict[ 'source' ] = td_common_util.evaluate_template( action_elem.find( 'source' ).text, install_environment )
- action_dict[ 'destination' ] = td_common_util.evaluate_template( action_elem.find( 'destination' ).text, install_environment )
+ action_dict[ 'source' ] = basic_util.evaluate_template( action_elem.find( 'source' ).text, install_environment )
+ action_dict[ 'destination' ] = basic_util.evaluate_template( action_elem.find( 'destination' ).text, install_environment )
action_dict[ 'rename_to' ] = action_elem.get( 'rename_to' )
return action_dict
@@ -717,12 +955,12 @@
# <action type="set_environment">
# <environment_variable name="PATH" action="prepend_to">$INSTALL_DIR</environment_variable>
# </action>
+ env_manager = EnvManager( app )
env_var_dicts = []
for env_elem in action_elem:
if env_elem.tag == 'environment_variable':
- env_var_dict = \
- td_common_util.create_env_var_dict( elem=env_elem,
- install_environment=install_environment )
+ env_var_dict = env_manager.create_env_var_dict( elem=env_elem,
+ install_environment=install_environment )
if env_var_dict:
env_var_dicts.append( env_var_dict )
if env_var_dicts:
@@ -764,16 +1002,17 @@
# the current tool dependency package. See the package_matplotlib_1_2 repository in the test tool
# shed for a real-world example.
all_env_shell_file_paths = []
+ env_manager = EnvManager( app )
for env_elem in action_elem:
if env_elem.tag == 'repository':
- env_shell_file_paths = td_common_util.get_env_shell_file_paths( app, env_elem )
+ env_shell_file_paths = env_manager.get_env_shell_file_paths( env_elem )
if env_shell_file_paths:
all_env_shell_file_paths.extend( env_shell_file_paths )
action_dict[ 'env_shell_file_paths' ] = all_env_shell_file_paths
return action_dict
-class SetupPerlEnvironment( RecipeStep ):
+class SetupPerlEnvironment( Download, RecipeStep ):
def __init__( self ):
self.type = 'setup_purl_environment'
@@ -822,7 +1061,7 @@
# We assume a URL to a gem file.
url = perl_package
perl_package_name = url.split( '/' )[ -1 ]
- dir = td_common_util.url_download( work_dir, perl_package_name, url, extract=True )
+ dir = self.url_download( work_dir, perl_package_name, url, extract=True )
# Search for Build.PL or Makefile.PL (ExtUtils::MakeMaker vs. Module::Build).
tmp_work_dir = os.path.join( work_dir, dir )
if os.path.exists( os.path.join( tmp_work_dir, 'Makefile.PL' ) ):
@@ -836,7 +1075,7 @@
return tool_dependency, filtered_actions, dir
return tool_dependency, None, None
with lcd( tmp_work_dir ):
- cmd = install_environment.build_command( td_common_util.evaluate_template( cmd, install_environment ) )
+ cmd = install_environment.build_command( basic_util.evaluate_template( cmd, install_environment ) )
return_code = install_environment.handle_command( app=app,
tool_dependency=tool_dependency,
cmd=cmd,
@@ -849,7 +1088,7 @@
# perl package from CPAN without version number.
# cpanm should be installed with the parent perl distribution, otherwise this will not work.
cmd += '''cpanm --local-lib=$INSTALL_DIR %s''' % ( perl_package )
- cmd = install_environment.build_command( td_common_util.evaluate_template( cmd, install_environment ) )
+ cmd = install_environment.build_command( basic_util.evaluate_template( cmd, install_environment ) )
return_code = install_environment.handle_command( app=app,
tool_dependency=tool_dependency,
cmd=cmd,
@@ -890,10 +1129,11 @@
# with each repository. This will potentially update the value of the 'env_shell_file_paths' entry
# in action_dict.
all_env_shell_file_paths = []
- action_dict = td_common_util.get_env_shell_file_paths_from_setup_environment_elem( app,
- all_env_shell_file_paths,
- action_elem,
- action_dict )
+ env_manager = EnvManager( app )
+ action_dict = env_manager.get_env_shell_file_paths_from_setup_environment_elem( app,
+ all_env_shell_file_paths,
+ action_elem,
+ action_dict )
perl_packages = []
for env_elem in action_elem:
if env_elem.tag == 'package':
@@ -908,7 +1148,7 @@
return action_dict
-class SetupREnvironment( RecipeStep ):
+class SetupREnvironment( Download, RecipeStep ):
def __init__( self ):
self.type = 'setup_r_environment'
@@ -947,7 +1187,7 @@
for url in action_dict[ 'r_packages' ]:
filename = url.split( '/' )[ -1 ]
tarball_names.append( filename )
- td_common_util.url_download( work_dir, filename, url, extract=False )
+ self.url_download( work_dir, filename, url, extract=False )
dir = os.path.curdir
current_dir = os.path.abspath( os.path.join( work_dir, dir ) )
with lcd( current_dir ):
@@ -958,7 +1198,7 @@
cmd = r'''PATH=$PATH:$R_HOME/bin; export PATH; R_LIBS=$INSTALL_DIR; export R_LIBS;
Rscript -e "install.packages(c('%s'),lib='$INSTALL_DIR', repos=NULL, dependencies=FALSE)"''' % \
( str( tarball_name ) )
- cmd = install_environment.build_command( td_common_util.evaluate_template( cmd, install_environment ) )
+ cmd = install_environment.build_command( basic_util.evaluate_template( cmd, install_environment ) )
return_code = install_environment.handle_command( app=app,
tool_dependency=tool_dependency,
cmd=cmd,
@@ -993,10 +1233,11 @@
# associated with each repository. This will potentially update the value of the
# 'env_shell_file_paths' entry in action_dict.
all_env_shell_file_paths = []
- action_dict = td_common_util.get_env_shell_file_paths_from_setup_environment_elem( app,
- all_env_shell_file_paths,
- action_elem,
- action_dict )
+ env_manager = EnvManager( app )
+ action_dict = env_manager.get_env_shell_file_paths_from_setup_environment_elem( app,
+ all_env_shell_file_paths,
+ action_elem,
+ action_dict )
r_packages = list()
for env_elem in action_elem:
if env_elem.tag == 'package':
@@ -1006,7 +1247,7 @@
return action_dict
-class SetupRubyEnvironment( RecipeStep ):
+class SetupRubyEnvironment( Download, RecipeStep ):
def __init__( self ):
self.type = 'setup_ruby_environment'
@@ -1058,7 +1299,7 @@
# We assume a URL to a gem file.
url = gem
gem_name = url.split( '/' )[ -1 ]
- td_common_util.url_download( work_dir, gem_name, url, extract=False )
+ self.url_download( work_dir, gem_name, url, extract=False )
cmd = '''PATH=$PATH:$RUBY_HOME/bin; export PATH; GEM_HOME=$INSTALL_DIR; export GEM_HOME;
gem install --local %s ''' % ( gem_name )
else:
@@ -1073,7 +1314,7 @@
# no version number given
cmd = '''PATH=$PATH:$RUBY_HOME/bin; export PATH; GEM_HOME=$INSTALL_DIR; export GEM_HOME;
gem install %s''' % ( gem )
- cmd = install_environment.build_command( td_common_util.evaluate_template( cmd, install_environment ) )
+ cmd = install_environment.build_command( basic_util.evaluate_template( cmd, install_environment ) )
return_code = install_environment.handle_command( app=app,
tool_dependency=tool_dependency,
cmd=cmd,
@@ -1114,10 +1355,11 @@
# associated with each repository. This will potentially update the value of the
# 'env_shell_file_paths' entry in action_dict.
all_env_shell_file_paths = []
- action_dict = td_common_util.get_env_shell_file_paths_from_setup_environment_elem( app,
- all_env_shell_file_paths,
- action_elem,
- action_dict )
+ env_manager = EnvManager( app )
+ action_dict = env_manager.get_env_shell_file_paths_from_setup_environment_elem( app,
+ all_env_shell_file_paths,
+ action_elem,
+ action_dict )
ruby_package_tups = []
for env_elem in action_elem:
if env_elem.tag == 'package':
@@ -1140,7 +1382,7 @@
return action_dict
-class SetupVirtualEnv( RecipeStep ):
+class SetupVirtualEnv( Download, RecipeStep ):
def __init__( self ):
self.type = 'setup_virtualenv'
@@ -1228,9 +1470,10 @@
with install_environment.make_tmp_dir() as work_dir:
downloaded_filename = VIRTUALENV_URL.rsplit('/', 1)[-1]
try:
- dir = td_common_util.url_download( work_dir, downloaded_filename, VIRTUALENV_URL )
+ dir = self.url_download( work_dir, downloaded_filename, VIRTUALENV_URL )
except:
- log.error( "Failed to download virtualenv: td_common_util.url_download( '%s', '%s', '%s' ) threw an exception", work_dir, downloaded_filename, VIRTUALENV_URL )
+ log.error( "Failed to download virtualenv: url_download( '%s', '%s', '%s' ) threw an exception",
+ work_dir, downloaded_filename, VIRTUALENV_URL )
return False
full_path_to_dir = os.path.abspath( os.path.join( work_dir, dir ) )
shutil.move( full_path_to_dir, venv_dir )
@@ -1245,7 +1488,7 @@
# lxml==2.3.0</action>
## Manually specify contents of requirements.txt file to create dynamically.
action_dict[ 'use_requirements_file' ] = asbool( action_elem.get( 'use_requirements_file', True ) )
- action_dict[ 'requirements' ] = td_common_util.evaluate_template( action_elem.text or 'requirements.txt', install_environment )
+ action_dict[ 'requirements' ] = basic_util.evaluate_template( action_elem.text or 'requirements.txt', install_environment )
action_dict[ 'python' ] = action_elem.get( 'python', 'python' )
return action_dict
@@ -1316,7 +1559,7 @@
def prepare_step( self, app, tool_dependency, action_elem, action_dict, install_environment, is_binary_download ):
# <action type="shell_command">make</action>
- action_elem_text = td_common_util.evaluate_template( action_elem.text, install_environment )
+ action_elem_text = basic_util.evaluate_template( action_elem.text, install_environment )
if action_elem_text:
action_dict[ 'command' ] = action_elem_text
return action_dict
@@ -1338,7 +1581,7 @@
env_vars = dict()
env_vars = install_environment.environment_dict()
tool_shed_repository = tool_dependency.tool_shed_repository
- env_vars.update( td_common_util.get_env_var_values( install_environment ) )
+ env_vars.update( basic_util.get_env_var_values( install_environment ) )
language = action_dict[ 'language' ]
with settings( warn_only=True, **env_vars ):
if language == 'cheetah':
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
@@ -10,7 +10,7 @@
from tool_shed.util import tool_dependency_util
from tool_shed.util import xml_util
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
+from tool_shed.galaxy_install.tool_dependencies.env_manager import EnvManager
from tool_shed.galaxy_install.tool_dependencies.recipe.env_file_builder import EnvFileBuilder
from tool_shed.galaxy_install.tool_dependencies.recipe.install_environment import InstallEnvironment
@@ -85,9 +85,9 @@
platform_info_dict = tool_dependency_util.get_platform_info_dict()
if package_install_version == '1.0':
# Handle tool dependency installation using a fabric method included in the Galaxy framework.
- actions_elem_tuples = td_common_util.parse_package_elem( package_elem,
- platform_info_dict=platform_info_dict,
- include_after_install_actions=True )
+ actions_elem_tuples = tool_dependency_util.parse_package_elem( package_elem,
+ platform_info_dict=platform_info_dict,
+ include_after_install_actions=True )
if not actions_elem_tuples:
proceed_with_install = False
error_message = 'Version %s of the %s package cannot be installed because ' % ( str( package_version ), str( package_name ) )
@@ -491,6 +491,7 @@
# <set_environment version="1.0">
# <repository toolshed="<tool shed>" name="<repository name>" owner="<repository owner>" changeset_revision="<changeset revision>" />
# </set_environment>
+ env_manager = EnvManager( app )
tool_dependencies = []
env_var_version = elem.get( 'version', '1.0' )
tool_shed_repository_install_dir = os.path.abspath( tool_shed_repository.repo_files_directory( app ) )
@@ -514,8 +515,8 @@
tool_dependency_version=None )
install_environment = InstallEnvironment( tool_shed_repository_install_dir=tool_shed_repository_install_dir,
install_dir=install_dir )
- env_var_dict = td_common_util.create_env_var_dict( elem=env_var_elem,
- install_environment=install_environment )
+ env_var_dict = env_manager.create_env_var_dict( elem=env_var_elem,
+ install_environment=install_environment )
if env_var_dict:
if not os.path.exists( install_dir ):
os.makedirs( install_dir )
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
+++ /dev/null
@@ -1,578 +0,0 @@
-import logging
-import os
-import re
-import shutil
-import stat
-import sys
-import tarfile
-import time
-import traceback
-import urllib2
-import zipfile
-from string import Template
-from tool_shed.util import common_util
-import tool_shed.util.shed_util_common as suc
-from galaxy.datatypes import checkers
-
-log = logging.getLogger( __name__ )
-
-# Set no activity timeout to 20 minutes.
-NO_OUTPUT_TIMEOUT = 1200.0
-INSTALLATION_LOG = 'INSTALLATION.log'
-
-
-class CompressedFile( object ):
-
- def __init__( self, file_path, mode='r' ):
- if istar( file_path ):
- self.file_type = 'tar'
- elif iszip( file_path ) and not isjar( file_path ):
- self.file_type = 'zip'
- self.file_name = os.path.splitext( os.path.basename( file_path ) )[ 0 ]
- if self.file_name.endswith( '.tar' ):
- self.file_name = os.path.splitext( self.file_name )[ 0 ]
- self.type = self.file_type
- method = 'open_%s' % self.file_type
- if hasattr( self, method ):
- self.archive = getattr( self, method )( file_path, mode )
- else:
- raise NameError( 'File type %s specified, no open method found.' % self.file_type )
-
- def extract( self, path ):
- '''Determine the path to which the archive should be extracted.'''
- contents = self.getmembers()
- extraction_path = path
- if len( contents ) == 1:
- # The archive contains a single file, return the extraction path.
- if self.isfile( contents[ 0 ] ):
- extraction_path = os.path.join( path, self.file_name )
- if not os.path.exists( extraction_path ):
- os.makedirs( extraction_path )
- self.archive.extractall( extraction_path )
- else:
- # Get the common prefix for all the files in the archive. If the common prefix ends with a slash,
- # or self.isdir() returns True, the archive contains a single directory with the desired contents.
- # Otherwise, it contains multiple files and/or directories at the root of the archive.
- common_prefix = os.path.commonprefix( [ self.getname( item ) for item in contents ] )
- if len( common_prefix ) >= 1 and not common_prefix.endswith( os.sep ) and self.isdir( self.getmember( common_prefix ) ):
- common_prefix += os.sep
- if common_prefix.endswith( os.sep ):
- self.archive.extractall( os.path.join( path ) )
- extraction_path = os.path.join( path, common_prefix )
- else:
- extraction_path = os.path.join( path, self.file_name )
- if not os.path.exists( extraction_path ):
- os.makedirs( extraction_path )
- self.archive.extractall( os.path.join( extraction_path ) )
- return os.path.abspath( extraction_path )
-
- def getmembers_tar( self ):
- return self.archive.getmembers()
-
- def getmembers_zip( self ):
- return self.archive.infolist()
-
- def getname_tar( self, item ):
- return item.name
-
- def getname_zip( self, item ):
- return item.filename
-
- def getmember( self, name ):
- for member in self.getmembers():
- if self.getname( member ) == name:
- return member
-
- def getmembers( self ):
- return getattr( self, 'getmembers_%s' % self.type )()
-
- def getname( self, member ):
- return getattr( self, 'getname_%s' % self.type )( member )
-
- def isdir( self, member ):
- return getattr( self, 'isdir_%s' % self.type )( member )
-
- def isdir_tar( self, member ):
- return member.isdir()
-
- def isdir_zip( self, member ):
- if member.filename.endswith( os.sep ):
- return True
- return False
-
- def isfile( self, member ):
- if not self.isdir( member ):
- return True
- return False
-
- def open_tar( self, filepath, mode ):
- return tarfile.open( filepath, mode, errorlevel=0 )
-
- def open_zip( self, filepath, mode ):
- return zipfile.ZipFile( filepath, mode )
-
-def assert_directory_executable( full_path ):
- """
- Return True if a symbolic link or directory exists and is executable, but if
- full_path is a file, return False.
- """
- if full_path is None:
- return False
- if os.path.isfile( full_path ):
- return False
- if os.path.isdir( full_path ):
- # Make sure the owner has execute permission on the directory.
- # See http://docs.python.org/2/library/stat.html
- if stat.S_IXUSR & os.stat( full_path )[ stat.ST_MODE ] == 64:
- return True
- return False
-
-def assert_directory_exists( full_path ):
- """
- Return True if a symbolic link or directory exists, but if full_path is a file,
- return False. """
- if full_path is None:
- return False
- if os.path.isfile( full_path ):
- return False
- if os.path.isdir( full_path ):
- return True
- return False
-
-def assert_file_executable( full_path ):
- """
- Return True if a symbolic link or file exists and is executable, but if full_path
- is a directory, return False.
- """
- if full_path is None:
- return False
- if os.path.isdir( full_path ):
- return False
- if os.path.exists( full_path ):
- # Make sure the owner has execute permission on the file.
- # See http://docs.python.org/2/library/stat.html
- if stat.S_IXUSR & os.stat( full_path )[ stat.ST_MODE ] == 64:
- return True
- return False
-
-def assert_file_exists( full_path ):
- """
- Return True if a symbolic link or file exists, but if full_path is a directory,
- return False.
- """
- if full_path is None:
- return False
- if os.path.isdir( full_path ):
- return False
- if os.path.exists( full_path ):
- return True
- return False
-
-def create_env_var_dict( elem, install_environment ):
- env_var_name = elem.get( 'name', 'PATH' )
- env_var_action = elem.get( 'action', 'prepend_to' )
- env_var_text = None
- tool_dependency_install_dir = install_environment.install_dir
- tool_shed_repository_install_dir = install_environment.tool_shed_repository_install_dir
- if elem.text and elem.text.find( 'REPOSITORY_INSTALL_DIR' ) >= 0:
- if tool_shed_repository_install_dir and elem.text.find( '$REPOSITORY_INSTALL_DIR' ) != -1:
- env_var_text = elem.text.replace( '$REPOSITORY_INSTALL_DIR', tool_shed_repository_install_dir )
- return dict( name=env_var_name, action=env_var_action, value=env_var_text )
- else:
- env_var_text = elem.text.replace( '$REPOSITORY_INSTALL_DIR', tool_dependency_install_dir )
- return dict( name=env_var_name, action=env_var_action, value=env_var_text )
- if elem.text and elem.text.find( 'INSTALL_DIR' ) >= 0:
- if tool_dependency_install_dir:
- env_var_text = elem.text.replace( '$INSTALL_DIR', tool_dependency_install_dir )
- return dict( name=env_var_name, action=env_var_action, value=env_var_text )
- else:
- env_var_text = elem.text.replace( '$INSTALL_DIR', tool_shed_repository_install_dir )
- return dict( name=env_var_name, action=env_var_action, value=env_var_text )
- if elem.text:
- # Allow for environment variables that contain neither REPOSITORY_INSTALL_DIR nor INSTALL_DIR
- # since there may be command line parameters that are tuned for a Galaxy instance. Allowing them
- # to be set in one location rather than being hard coded into each tool config is the best approach.
- # For example:
- # <environment_variable name="GATK2_SITE_OPTIONS" action="set_to">
- # "--num_threads 4 --num_cpu_threads_per_data_thread 3 --phone_home STANDARD"
- # </environment_variable>
- return dict( name=env_var_name, action=env_var_action, value=elem.text)
- return None
-
-def download_binary( url, work_dir ):
- """Download a pre-compiled binary from the specified URL."""
- downloaded_filename = os.path.split( url )[ -1 ]
- dir = url_download( work_dir, downloaded_filename, url, extract=False )
- return downloaded_filename
-
-def egrep_escape( text ):
- """Escape ``text`` to allow literal matching using egrep."""
- regex = re.escape( text )
- # Seems like double escaping is needed for \
- regex = regex.replace( '\\\\', '\\\\\\' )
- # Triple-escaping seems to be required for $ signs
- regex = regex.replace( r'\$', r'\\\$' )
- # Whereas single quotes should not be escaped
- regex = regex.replace( r"\'", "'" )
- return regex
-
-def evaluate_template( text, install_environment ):
- """
- Substitute variables defined in XML blocks from dependencies file. The value of the received
- repository_install_dir is the root installation directory of the repository that contains the
- tool dependency. The value of the received install_dir is the root installation directory of
- the tool_dependency.
- """
- return Template( text ).safe_substitute( get_env_var_values( install_environment ) )
-
-def format_traceback():
- ex_type, ex, tb = sys.exc_info()
- return ''.join( traceback.format_tb( tb ) )
-
-def get_env_shell_file_path( installation_directory ):
- env_shell_file_name = 'env.sh'
- default_location = os.path.abspath( os.path.join( installation_directory, env_shell_file_name ) )
- if os.path.exists( default_location ):
- return default_location
- for root, dirs, files in os.walk( installation_directory ):
- for name in files:
- if name == env_shell_file_name:
- return os.path.abspath( os.path.join( root, name ) )
- return None
-
-def get_env_shell_file_paths( app, elem ):
- # Currently only the following tag set is supported.
- # <repository toolshed="http://localhost:9009/" name="package_numpy_1_7" owner="test" changeset_revision="c84c6a8be056">
- # <package name="numpy" version="1.7.1" />
- # </repository>
- env_shell_file_paths = []
- toolshed = elem.get( 'toolshed', None )
- repository_name = elem.get( 'name', None )
- repository_owner = elem.get( 'owner', None )
- changeset_revision = elem.get( 'changeset_revision', None )
- if toolshed and repository_name and repository_owner and changeset_revision:
- # The protocol is not stored, but the port is if it exists.
- toolshed = common_util.remove_protocol_from_tool_shed_url( toolshed )
- repository = suc.get_repository_for_dependency_relationship( app, toolshed, repository_name, repository_owner, changeset_revision )
- if repository:
- for sub_elem in elem:
- tool_dependency_type = sub_elem.tag
- tool_dependency_name = sub_elem.get( 'name' )
- tool_dependency_version = sub_elem.get( 'version' )
- if tool_dependency_type and tool_dependency_name and tool_dependency_version:
- # Get the tool_dependency so we can get its installation directory.
- tool_dependency = None
- for tool_dependency in repository.tool_dependencies:
- if tool_dependency.type == tool_dependency_type and \
- tool_dependency.name == tool_dependency_name and \
- tool_dependency.version == tool_dependency_version:
- break
- if tool_dependency:
- tool_dependency_key = '%s/%s' % ( tool_dependency_name, tool_dependency_version )
- installation_directory = tool_dependency.installation_directory( app )
- env_shell_file_path = get_env_shell_file_path( installation_directory )
- if env_shell_file_path:
- env_shell_file_paths.append( env_shell_file_path )
- else:
- error_message = "Skipping tool dependency definition because unable to locate env.sh file for tool dependency "
- error_message += "type %s, name %s, version %s for repository %s" % \
- ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ), str( repository.name ) )
- log.debug( error_message )
- continue
- else:
- error_message = "Skipping tool dependency definition because unable to locate tool dependency "
- error_message += "type %s, name %s, version %s for repository %s" % \
- ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ), str( repository.name ) )
- log.debug( error_message )
- continue
- else:
- error_message = "Skipping invalid tool dependency definition: type %s, name %s, version %s." % \
- ( str( tool_dependency_type ), str( tool_dependency_name ), str( tool_dependency_version ) )
- log.debug( error_message )
- continue
- else:
- error_message = "Skipping set_environment_for_install definition because unable to locate required installed tool shed repository: "
- error_message += "toolshed %s, name %s, owner %s, changeset_revision %s." % \
- ( str( toolshed ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
- log.debug( error_message )
- else:
- error_message = "Skipping invalid set_environment_for_install definition: toolshed %s, name %s, owner %s, changeset_revision %s." % \
- ( str( toolshed ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
- log.debug( error_message )
- return env_shell_file_paths
-
-def get_env_shell_file_paths_from_setup_environment_elem( app, all_env_shell_file_paths, elem, action_dict ):
- """
- Parse an XML tag set to discover all child repository dependency tags and define the path to an env.sh file associated
- with the repository (this requires the repository dependency to be in an installed state). The received action_dict
- will be updated with these discovered paths and returned to the caller. This method handles tool dependency definition
- tag sets <setup_r_environment>, <setup_ruby_environment> and <setup_perl_environment>.
- """
- # An example elem is:
- # <action type="setup_perl_environment">
- # <repository name="package_perl_5_18" owner="iuc">
- # <package name="perl" version="5.18.1" />
- # </repository>
- # <repository name="package_expat_2_1" owner="iuc" prior_installation_required="True">
- # <package name="expat" version="2.1.0" />
- # </repository>
- # <package>http://search.cpan.org/CPAN/authors/id/T/TO/TODDR/XML-Parser-2.41.tar.gz</package>
- # <package>http://search.cpan.org/CPAN/authors/id/L/LD/LDS/CGI.pm-3.43.tar.gz</package>
- # </action>
- for action_elem in elem:
- if action_elem.tag == 'repository':
- env_shell_file_paths = get_env_shell_file_paths( app, action_elem )
- all_env_shell_file_paths.extend( env_shell_file_paths )
- if all_env_shell_file_paths:
- action_dict[ 'env_shell_file_paths' ] = all_env_shell_file_paths
- action_dict[ 'action_shell_file_paths' ] = env_shell_file_paths
- return action_dict
-
-def get_env_var_values( install_environment ):
- """
- Return a dictionary of values, some of which enable substitution of reserved words for the values.
- The received install_enviroment object has 2 important attributes for reserved word substitution:
- install_environment.tool_shed_repository_install_dir is the root installation directory of the repository
- that contains the tool dependency being installed, and install_environment.install_dir is the root
- installation directory of the tool dependency.
- """
- env_var_dict = {}
- env_var_dict[ 'REPOSITORY_INSTALL_DIR' ] = install_environment.tool_shed_repository_install_dir
- env_var_dict[ 'INSTALL_DIR' ] = install_environment.install_dir
- env_var_dict[ 'system_install' ] = install_environment.install_dir
- # If the Python interpreter is 64bit then we can safely assume that the underlying system is also 64bit.
- env_var_dict[ '__is64bit__' ] = sys.maxsize > 2**32
- return env_var_dict
-
-def isbz2( file_path ):
- return checkers.is_bz2( file_path )
-
-def isgzip( file_path ):
- return checkers.is_gzip( file_path )
-
-def isjar( file_path ):
- return iszip( file_path ) and file_path.endswith( '.jar' )
-
-def istar( file_path ):
- return tarfile.is_tarfile( file_path )
-
-def iszip( file_path ):
- return checkers.check_zip( file_path )
-
-def is_compressed( file_path ):
- if isjar( file_path ):
- return False
- else:
- return iszip( file_path ) or isgzip( file_path ) or istar( file_path ) or isbz2( file_path )
-
-def make_directory( full_path ):
- if not os.path.exists( full_path ):
- os.makedirs( full_path )
-
-def move_directory_files( current_dir, source_dir, destination_dir ):
- source_directory = os.path.abspath( os.path.join( current_dir, source_dir ) )
- destination_directory = os.path.join( destination_dir )
- if not os.path.isdir( destination_directory ):
- os.makedirs( destination_directory )
- symlinks = []
- regular_files = []
- for file_name in os.listdir( source_directory ):
- source_file = os.path.join( source_directory, file_name )
- destination_file = os.path.join( destination_directory, file_name )
- files_tuple = ( source_file, destination_file )
- if os.path.islink( source_file ):
- symlinks.append( files_tuple )
- else:
- regular_files.append( files_tuple )
- for source_file, destination_file in symlinks:
- shutil.move( source_file, destination_file )
- for source_file, destination_file in regular_files:
- shutil.move( source_file, destination_file )
-
-def move_file( current_dir, source, destination, rename_to=None ):
- source_path = os.path.abspath( os.path.join( current_dir, source ) )
- source_file = os.path.basename( source_path )
- if rename_to is not None:
- destination_file = rename_to
- destination_directory = os.path.join( destination )
- destination_path = os.path.join( destination_directory, destination_file )
- else:
- destination_directory = os.path.join( destination )
- destination_path = os.path.join( destination_directory, source_file )
- if not os.path.exists( destination_directory ):
- os.makedirs( destination_directory )
- shutil.move( source_path, destination_path )
-
-def parse_package_elem( package_elem, platform_info_dict=None, include_after_install_actions=True ):
- """
- Parse a <package> element within a tool dependency definition and return a list of action tuples.
- This method is called when setting metadata on a repository that includes a tool_dependencies.xml
- file or when installing a repository that includes a tool_dependencies.xml file. If installing,
- platform_info_dict must be a valid dictionary and include_after_install_actions must be True.
- """
- # The actions_elem_tuples list contains <actions> tag sets (possibly inside of an <actions_group>
- # tag set) to be processed in the order they are defined in the tool_dependencies.xml file.
- actions_elem_tuples = []
- # The tag sets that will go into the actions_elem_list are those that install a compiled binary if
- # the architecture and operating system match its defined attributes. If compiled binary is not
- # installed, the first <actions> tag set [following those that have the os and architecture attributes]
- # that does not have os or architecture attributes will be processed. This tag set must contain the
- # recipe for downloading and compiling source.
- actions_elem_list = []
- for elem in package_elem:
- if elem.tag == 'actions':
- # We have an <actions> tag that should not be matched against a specific combination of
- # architecture and operating system.
- in_actions_group = False
- actions_elem_tuples.append( ( in_actions_group, elem ) )
- elif elem.tag == 'actions_group':
- # We have an actions_group element, and its child <actions> elements should therefore be compared
- # with the current operating system
- # and processor architecture.
- in_actions_group = True
- # Record the number of <actions> elements so we can filter out any <action> elements that precede
- # <actions> elements.
- actions_elem_count = len( elem.findall( 'actions' ) )
- # Record the number of <actions> elements that have both architecture and os specified, in order
- # to filter out any platform-independent <actions> elements that come before platform-specific
- # <actions> elements.
- platform_actions_elements = []
- for actions_elem in elem.findall( 'actions' ):
- if actions_elem.get( 'architecture' ) is not None and actions_elem.get( 'os' ) is not None:
- platform_actions_elements.append( actions_elem )
- platform_actions_element_count = len( platform_actions_elements )
- platform_actions_elements_processed = 0
- actions_elems_processed = 0
- # The tag sets that will go into the after_install_actions list are <action> tags instead of <actions>
- # tags. These will be processed only if they are at the very end of the <actions_group> tag set (after
- # all <actions> tag sets). See below for details.
- after_install_actions = []
- # Inspect the <actions_group> element and build the actions_elem_list and the after_install_actions list.
- for child_element in elem:
- if child_element.tag == 'actions':
- actions_elems_processed += 1
- system = child_element.get( 'os' )
- architecture = child_element.get( 'architecture' )
- # Skip <actions> tags that have only one of architecture or os specified, in order for the
- # count in platform_actions_elements_processed to remain accurate.
- if ( system and not architecture ) or ( architecture and not system ):
- log.debug( 'Error: Both architecture and os attributes must be specified in an <actions> tag.' )
- continue
- # Since we are inside an <actions_group> tag set, compare it with our current platform information
- # and filter the <actions> tag sets that don't match. Require both the os and architecture attributes
- # to be defined in order to find a match.
- if system and architecture:
- platform_actions_elements_processed += 1
- # If either the os or architecture do not match the platform, this <actions> tag will not be
- # considered a match. Skip it and proceed with checking the next one.
- if platform_info_dict:
- if platform_info_dict[ 'os' ] != system or platform_info_dict[ 'architecture' ] != architecture:
- continue
- else:
- # We must not be installing a repository into Galaxy, so determining if we can install a
- # binary is not necessary.
- continue
- else:
- # <actions> tags without both os and architecture attributes are only allowed to be specified
- # after platform-specific <actions> tags. If we find a platform-independent <actions> tag before
- # all platform-specific <actions> tags have been processed.
- if platform_actions_elements_processed < platform_actions_element_count:
- debug_msg = 'Error: <actions> tags without os and architecture attributes are only allowed '
- debug_msg += 'after all <actions> tags with os and architecture attributes have been defined. '
- debug_msg += 'Skipping the <actions> tag set with no os or architecture attributes that has '
- debug_msg += 'been defined between two <actions> tag sets that have these attributes defined. '
- log.debug( debug_msg )
- continue
- # If we reach this point, it means one of two things: 1) The system and architecture attributes are
- # not defined in this <actions> tag, or 2) The system and architecture attributes are defined, and
- # they are an exact match for the current platform. Append the child element to the list of elements
- # to process.
- actions_elem_list.append( child_element )
- elif child_element.tag == 'action':
- # Any <action> tags within an <actions_group> tag set must come after all <actions> tags.
- if actions_elems_processed == actions_elem_count:
- # If all <actions> elements have been processed, then this <action> element can be appended to the
- # list of actions to execute within this group.
- after_install_actions.append( child_element )
- else:
- # If any <actions> elements remain to be processed, then log a message stating that <action>
- # elements are not allowed to precede any <actions> elements within an <actions_group> tag set.
- debug_msg = 'Error: <action> tags are only allowed at the end of an <actions_group> tag set after '
- debug_msg += 'all <actions> tags. Skipping <%s> element with type %s.' % \
- ( child_element.tag, child_element.get( 'type', 'unknown' ) )
- log.debug( debug_msg )
- continue
- if platform_info_dict is None and not include_after_install_actions:
- # We must be setting metadata on a repository.
- if len( actions_elem_list ) >= 1:
- actions_elem_tuples.append( ( in_actions_group, actions_elem_list[ 0 ] ) )
- else:
- # We are processing a recipe that contains only an <actions_group> tag set for installing a binary,
- # but does not include an additional recipe for installing and compiling from source.
- actions_elem_tuples.append( ( in_actions_group, [] ) )
- elif platform_info_dict is not None and include_after_install_actions:
- # We must be installing a repository.
- if after_install_actions:
- actions_elem_list.extend( after_install_actions )
- actions_elem_tuples.append( ( in_actions_group, actions_elem_list ) )
- else:
- # Skip any element that is not <actions> or <actions_group> - this will skip comments, <repository> tags
- # and <readme> tags.
- in_actions_group = False
- continue
- return actions_elem_tuples
-
-def __shellquote( s ):
- """Quote and escape the supplied string for use in shell expressions."""
- return "'" + s.replace( "'", "'\\''" ) + "'"
-
-def url_download( install_dir, downloaded_file_name, download_url, extract=True ):
- file_path = os.path.join( install_dir, downloaded_file_name )
- src = None
- dst = None
- # Set a timer so we don't sit here forever.
- start_time = time.time()
- try:
- src = urllib2.urlopen( download_url )
- dst = open( file_path, 'wb' )
- while True:
- chunk = src.read( suc.CHUNK_SIZE )
- if chunk:
- dst.write( chunk )
- else:
- break
- time_taken = time.time() - start_time
- if time_taken > NO_OUTPUT_TIMEOUT:
- err_msg = 'Downloading from URL %s took longer than the defined timeout period of %.1f seconds.' % \
- ( str( download_url ), NO_OUTPUT_TIMEOUT )
- raise Exception( err_msg )
- except Exception, e:
- err_msg = err_msg = 'Error downloading from URL\n%s:\n%s' % ( str( download_url ), str( e ) )
- raise Exception( err_msg )
- finally:
- if src:
- src.close()
- if dst:
- dst.close()
- if extract:
- if istar( file_path ) or ( iszip( file_path ) and not isjar( file_path ) ):
- archive = CompressedFile( file_path )
- extraction_path = archive.extract( install_dir )
- else:
- extraction_path = os.path.abspath( install_dir )
- else:
- extraction_path = os.path.abspath( install_dir )
- return extraction_path
-
-def zipfile_ok( path_to_archive ):
- """
- This function is a bit pedantic and not functionally necessary. It checks whether there is no file pointing outside of the extraction,
- because ZipFile.extractall() has some potential security holes. See python zipfile documentation for more details.
- """
- basename = os.path.realpath( os.path.dirname( path_to_archive ) )
- zip_archive = zipfile.ZipFile( path_to_archive )
- for member in zip_archive.namelist():
- member_path = os.path.realpath( os.path.join( basename, member ) )
- if not member_path.startswith( basename ):
- return False
- return True
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/scripts/check_filesystem_for_empty_tool_dependency_installation_paths.py
--- a/lib/tool_shed/scripts/check_filesystem_for_empty_tool_dependency_installation_paths.py
+++ b/lib/tool_shed/scripts/check_filesystem_for_empty_tool_dependency_installation_paths.py
@@ -7,7 +7,7 @@
new_path.extend( sys.path[1:] )
sys.path = new_path
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
+from tool_shed.util.basic_util import INSTALLATION_LOG
def main( args ):
empty_installation_paths = []
@@ -31,13 +31,13 @@
no_files = False
if len( dirs ) == 0:
no_dirs = True
- if len( files ) == 0 or len( files ) == 1 and td_common_util.INSTALLATION_LOG in files:
+ if len( files ) == 0 or len( files ) == 1 and INSTALLATION_LOG in files:
no_files = True
if no_files and no_dirs and root not in empty_installation_paths:
empty_installation_paths.append( root )
if len( empty_installation_paths ) > 0:
print 'The following %d tool dependency installation directories were found to be empty or contain only the file %s.' % \
- ( len( empty_installation_paths ), td_common_util.INSTALLATION_LOG )
+ ( len( empty_installation_paths ), INSTALLATION_LOG )
if args.delete:
for path in empty_installation_paths:
if os.path.exists( path ):
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/scripts/check_s3_for_empty_tool_dependency_installation_paths.py
--- a/lib/tool_shed/scripts/check_s3_for_empty_tool_dependency_installation_paths.py
+++ b/lib/tool_shed/scripts/check_s3_for_empty_tool_dependency_installation_paths.py
@@ -12,8 +12,7 @@
import boto
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
-
+from tool_shed.util.basic_util import INSTALLATION_LOG
class BucketList( object ):
@@ -77,7 +76,7 @@
# This would not be the case in a Galaxy instance, since the Galaxy admin will need to verify the contents of
# the installation path in order to determine which action should be taken.
elif len( tool_dependency_path_contents ) == 2 and \
- tool_dependency_path_contents[1].name.endswith( td_common_util.INSTALLATION_LOG ):
+ tool_dependency_path_contents[1].name.endswith( INSTALLATION_LOG ):
empty_directories.append( tool_dependency_path_contents[ 0 ] )
return [ item.name for item in empty_directories ]
@@ -106,7 +105,7 @@
print 'No empty installation paths found, exiting.'
return 0
print 'The following %d tool dependency installation paths were found to be empty or contain only the file %s.' % \
- ( len( dependency_cleaner.empty_installation_paths ), td_common_util.INSTALLATION_LOG )
+ ( len( dependency_cleaner.empty_installation_paths ), INSTALLATION_LOG )
if asbool( args.delete ):
dependency_cleaner.delete_empty_installation_paths()
else:
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/util/basic_util.py
--- a/lib/tool_shed/util/basic_util.py
+++ b/lib/tool_shed/util/basic_util.py
@@ -1,5 +1,8 @@
import logging
import os
+import shutil
+import sys
+from string import Template
from galaxy.util import unicodify
@@ -10,8 +13,52 @@
log = logging.getLogger( __name__ )
+CHUNK_SIZE = 2**20 # 1Mb
+INSTALLATION_LOG = 'INSTALLATION.log'
+# Set no activity timeout to 20 minutes.
+NO_OUTPUT_TIMEOUT = 1200.0
+MAXDIFFSIZE = 8000
MAX_DISPLAY_SIZE = 32768
+def evaluate_template( text, install_environment ):
+ """
+ Substitute variables defined in XML blocks from dependencies file. The value of the received
+ repository_install_dir is the root installation directory of the repository that contains the
+ tool dependency. The value of the received install_dir is the root installation directory of
+ the tool_dependency.
+ """
+ return Template( text ).safe_substitute( get_env_var_values( install_environment ) )
+
+def get_env_var_values( install_environment ):
+ """
+ Return a dictionary of values, some of which enable substitution of reserved words for the values.
+ The received install_enviroment object has 2 important attributes for reserved word substitution:
+ install_environment.tool_shed_repository_install_dir is the root installation directory of the repository
+ that contains the tool dependency being installed, and install_environment.install_dir is the root
+ installation directory of the tool dependency.
+ """
+ env_var_dict = {}
+ env_var_dict[ 'REPOSITORY_INSTALL_DIR' ] = install_environment.tool_shed_repository_install_dir
+ env_var_dict[ 'INSTALL_DIR' ] = install_environment.install_dir
+ env_var_dict[ 'system_install' ] = install_environment.install_dir
+ # If the Python interpreter is 64bit then we can safely assume that the underlying system is also 64bit.
+ env_var_dict[ '__is64bit__' ] = sys.maxsize > 2**32
+ return env_var_dict
+
+def move_file( current_dir, source, destination, rename_to=None ):
+ source_path = os.path.abspath( os.path.join( current_dir, source ) )
+ source_file = os.path.basename( source_path )
+ if rename_to is not None:
+ destination_file = rename_to
+ destination_directory = os.path.join( destination )
+ destination_path = os.path.join( destination_directory, destination_file )
+ else:
+ destination_directory = os.path.join( destination )
+ destination_path = os.path.join( destination_directory, source_file )
+ if not os.path.exists( destination_directory ):
+ os.makedirs( destination_directory )
+ shutil.move( source_path, destination_path )
+
def remove_dir( dir ):
"""Attempt to remove a directory from disk."""
if dir:
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/util/commit_util.py
--- a/lib/tool_shed/util/commit_util.py
+++ b/lib/tool_shed/util/commit_util.py
@@ -12,6 +12,7 @@
from galaxy.util.odict import odict
from galaxy.web import url_for
import tool_shed.util.shed_util_common as suc
+from tool_shed.util import basic_util
from tool_shed.util import hg_util
from tool_shed.util import tool_util
from tool_shed.util import xml_util
@@ -126,7 +127,7 @@
bzipped_file = bz2.BZ2File( uploaded_file_name, 'rb' )
while 1:
try:
- chunk = bzipped_file.read( suc.CHUNK_SIZE )
+ chunk = bzipped_file.read( basic_util.CHUNK_SIZE )
except IOError:
os.close( fd )
os.remove( uncompressed )
@@ -239,7 +240,7 @@
gzipped_file = gzip.GzipFile( uploaded_file_name, 'rb' )
while 1:
try:
- chunk = gzipped_file.read( suc.CHUNK_SIZE )
+ chunk = gzipped_file.read( basic_util.CHUNK_SIZE )
except IOError, e:
os.close( fd )
os.remove( uncompressed )
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/util/metadata_util.py
--- a/lib/tool_shed/util/metadata_util.py
+++ b/lib/tool_shed/util/metadata_util.py
@@ -22,11 +22,12 @@
from tool_shed.util import tool_dependency_util
from tool_shed.util import tool_util
from tool_shed.util import xml_util
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
import tool_shed.repository_types.util as rt_util
log = logging.getLogger( __name__ )
+REPOSITORY_DATA_MANAGER_CONFIG_FILENAME = 'data_manager_conf.xml'
+
# Repository metadata comparisons for changeset revisions.
EQUAL = 'equal'
NO_METADATA = 'no metadata'
@@ -37,7 +38,7 @@
NOT_TOOL_CONFIGS = [ suc.DATATYPES_CONFIG_FILENAME,
rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME,
rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME,
- suc.REPOSITORY_DATA_MANAGER_CONFIG_FILENAME ]
+ REPOSITORY_DATA_MANAGER_CONFIG_FILENAME ]
def add_tool_versions( trans, id, repository_metadata, changeset_revisions ):
# Build a dictionary of { 'tool id' : 'parent tool id' } pairs for each tool in repository_metadata.
@@ -750,7 +751,7 @@
metadata_dict = generate_data_manager_metadata( app,
repository,
files_dir,
- hg_util.get_config_from_disk( suc.REPOSITORY_DATA_MANAGER_CONFIG_FILENAME, files_dir ),
+ hg_util.get_config_from_disk( REPOSITORY_DATA_MANAGER_CONFIG_FILENAME, files_dir ),
metadata_dict,
shed_config_dict=shed_config_dict )
@@ -809,10 +810,10 @@
if package_install_version == '1.0':
# Complex repository dependencies can be defined within the last <actions> tag set contained in an
# <actions_group> tag set. Comments, <repository> tag sets and <readme> tag sets will be skipped
- # in td_common_util.parse_package_elem().
- actions_elem_tuples = td_common_util.parse_package_elem( sub_elem,
- platform_info_dict=None,
- include_after_install_actions=False )
+ # in tool_dependency_util.parse_package_elem().
+ actions_elem_tuples = tool_dependency_util.parse_package_elem( sub_elem,
+ platform_info_dict=None,
+ include_after_install_actions=False )
if actions_elem_tuples:
# We now have a list of a single tuple that looks something like:
# [(True, <Element 'actions' at 0x104017850>)]
diff -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 -r b1b1e5cefca5b0193c8623a9aee7a39cd1dda55c lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -24,11 +24,8 @@
log = logging.getLogger( __name__ )
-CHUNK_SIZE = 2**20 # 1Mb
MAX_CONTENT_SIZE = 1048576
-MAXDIFFSIZE = 8000
DATATYPES_CONFIG_FILENAME = 'datatypes_conf.xml'
-REPOSITORY_DATA_MANAGER_CONFIG_FILENAME = 'data_manager_conf.xml'
new_repo_email_alert_template = """
Sharable link: ${sharable_link}
This diff is so big that we needed to truncate the remainder.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: HDA view: properly compile underscore template on first load, override template fn to pass localizer (_l) into template fn
by commits-noreply@bitbucket.org 28 May '14
by commits-noreply@bitbucket.org 28 May '14
28 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/31740dffaf9a/
Changeset: 31740dffaf9a
User: carlfeberhard
Date: 2014-05-28 19:41:07
Summary: HDA view: properly compile underscore template on first load, override template fn to pass localizer (_l) into template fn
Affected #: 3 files
diff -r 7fd77febf6bb8aa7a6acde9c3a31429f7b1ee99f -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 static/scripts/mvc/dataset/hda-base.js
--- a/static/scripts/mvc/dataset/hda-base.js
+++ b/static/scripts/mvc/dataset/hda-base.js
@@ -652,7 +652,8 @@
});
//------------------------------------------------------------------------------ TEMPLATES
-var skeletonTemplate = [
+//TODO: possibly break these out into a sep. module
+var skeletonTemplate = _.template([
'<div class="dataset hda">',
'<div class="dataset-warnings">',
// error during index fetch - show error on dataset
@@ -704,9 +705,9 @@
'<div class="dataset-body"></div>',
'</div>'
-].join( '' );
+].join( '' ));
-var bodyTemplate = [
+var bodyTemplate = _.template([
'<div class="dataset-body">',
'<% if( hda.body ){ %>',
'<div class="dataset-summary">',
@@ -799,14 +800,15 @@
'<% } %>',
// end if body
'</div>'
-].join( '' );
+].join( '' ));
HDABaseView.templates = {
+ // we override here in order to pass the localizer (_L) into the template scope - since we use it as a fn within
skeleton : function( hdaJSON ){
- return _.template( skeletonTemplate, hdaJSON, { variable: 'hda' });
+ return skeletonTemplate({ _l: _l, hda: hdaJSON });
},
body : function( hdaJSON ){
- return _.template( bodyTemplate, hdaJSON, { variable: 'hda' });
+ return bodyTemplate({ _l: _l, hda: hdaJSON });
}
};
diff -r 7fd77febf6bb8aa7a6acde9c3a31429f7b1ee99f -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 static/scripts/packed/mvc/dataset/hda-base.js
--- a/static/scripts/packed/mvc/dataset/hda-base.js
+++ b/static/scripts/packed/mvc/dataset/hda-base.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-model","mvc/base-mvc","utils/localization"],function(e,b,d){var g=Backbone.View.extend(b.LoggableMixin).extend({tagName:"div",fxSpeed:"fast",_queueNewRender:function(i,j){j=(j===undefined)?(true):(j);var h=this;if(j){$(h).queue(function(k){this.$el.fadeOut(h.fxSpeed,k)})}$(h).queue(function(k){this.$el.empty().attr("class",h.className).addClass("state-"+h.model.get("state")).append(i.children());if(this.selectable){this.showSelector(0)}k()});if(j){$(h).queue(function(k){this.$el.fadeIn(h.fxSpeed,k)})}$(h).queue(function(k){this.trigger("rendered",h);if(this.model.inReadyState()){this.trigger("rendered:ready",h)}if(this.draggable){this.draggableOn()}k()})},toggleBodyVisibility:function(k,i){var h=32,j=13;if(k&&(k.type==="keydown")&&!(k.keyCode===h||k.keyCode===j)){return true}var l=this.$el.find(".dataset-body");i=(i===undefined)?(!l.is(":visible")):(i);if(i){this.expandBody()}else{this.collapseBody()}return false},showSelector:function(){if(this.selected){this.select(null,true)}this.selectable=true;this.trigger("selectable",true,this);this.$(".dataset-primary-actions").hide();this.$(".dataset-selector").show()},hideSelector:function(){this.selectable=false;this.trigger("selectable",false,this);this.$(".dataset-selector").hide();this.$(".dataset-primary-actions").show()},toggleSelector:function(){if(!this.$el.find(".dataset-selector").is(":visible")){this.showSelector()}else{this.hideSelector()}},select:function(h){this.$el.find(".dataset-selector span").removeClass("fa-square-o").addClass("fa-check-square-o");if(!this.selected){this.trigger("selected",this);this.selected=true}return false},deselect:function(h){this.$el.find(".dataset-selector span").removeClass("fa-check-square-o").addClass("fa-square-o");if(this.selected){this.trigger("de-selected",this);this.selected=false}return false},toggleSelect:function(h){if(this.selected){this.deselect(h)}else{this.select(h)}},});var c=g.extend({className:"dataset hda history-panel-hda",id:function(){return"hda-"+this.model.get("id")},initialize:function(h){if(h.logger){this.logger=this.model.logger=h.logger}this.log(this+".initialize:",h);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton];this.linkTarget=h.linkTarget||"_blank";this.selectable=h.selectable||false;this.selected=h.selected||false;this.expanded=h.expanded||false;this.draggable=h.draggable||false;this._setUpListeners()},_setUpListeners:function(){this.model.on("change",function(i,h){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this)},render:function(i){this.$el.find("[title]").tooltip("destroy");this.urls=this.model.urls();var h=this._buildNewRender();this._queueNewRender(h,i);return this},_buildNewRender:function(){var h=$(c.templates.skeleton(this.model.toJSON()));h.find(".dataset-primary-actions").append(this._render_titleButtons());h.children(".dataset-body").replaceWith(this._render_body());this._setUpBehaviors(h);return h},_setUpBehaviors:function(h){h=h||this.$el;make_popup_menus(h);h.find("[title]").tooltip({placement:"bottom"})},_render_titleButtons:function(){return[this._render_displayButton()]},_render_displayButton:function(){if((this.model.get("state")===e.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(this.model.get("state")===e.HistoryDatasetAssociation.STATES.DISCARDED)||(!this.model.get("accessible"))){return null}var i={target:this.linkTarget,classes:"dataset-display"};if(this.model.get("purged")){i.disabled=true;i.title=d("Cannot display datasets removed from disk")}else{if(this.model.get("state")===e.HistoryDatasetAssociation.STATES.UPLOAD){i.disabled=true;i.title=d("This dataset must finish uploading before it can be viewed")}else{if(this.model.get("state")===e.HistoryDatasetAssociation.STATES.NEW){i.disabled=true;i.title=d("This dataset is not yet viewable")}else{i.title=d("View data");i.href=this.urls.display;var h=this;i.onclick=function(j){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Data Viewer: "+h.model.get("name"),type:"url",content:h.urls.display});j.preventDefault()}}}}}i.faIcon="fa-eye";return faIconButton(i)},_render_downloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}var i=this.urls,j=this.model.get("meta_files");if(_.isEmpty(j)){return $(['<a href="'+i.download+'" title="'+d("Download")+'" ','class="icon-btn dataset-download-btn">','<span class="fa fa-floppy-o"></span>',"</a>"].join(""))}var k="dataset-"+this.model.get("id")+"-popup",h=['<div popupmenu="'+k+'">','<a href="'+i.download+'">',d("Download dataset"),"</a>","<a>"+d("Additional files")+"</a>",_.map(j,function(l){return['<a class="action-button" href="',i.meta_download+l.file_type,'">',d("Download")," ",l.file_type,"</a>"].join("")}).join("\n"),"</div>",'<div class="icon-btn-group">','<a href="'+i.download+'" title="'+d("Download")+'" ','class="icon-btn dataset-download-btn">','<span class="fa fa-floppy-o"></span>','</a><a class="icon-btn popup" id="'+k+'">','<span class="fa fa-caret-down"></span>',"</a>","</div>"].join("\n");return $(h)},_render_showParamsButton:function(){return faIconButton({title:d("View details"),classes:"dataset-params-btn",href:this.urls.show_params,target:this.linkTarget,faIcon:"fa-info-circle"})},_render_body:function(){var i=$('<div>Error: unknown dataset state "'+this.model.get("state")+'".</div>'),h=this["_render_body_"+this.model.get("state")];if(_.isFunction(h)){i=h.call(this)}this._setUpBehaviors(i);if(this.expanded){i.show()}return i},_render_stateBodyHelper:function(h,k){k=k||[];var i=this,j=$(c.templates.body(_.extend(this.model.toJSON(),{body:h})));j.find(".dataset-actions .left").append(_.map(k,function(l){return l.call(i)}));return j},_render_body_new:function(){return this._render_stateBodyHelper("<div>"+d("This is a new dataset and not all of its data are available yet")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_noPermission:function(){return this._render_stateBodyHelper("<div>"+d("You do not have permission to view this dataset")+"</div>")},_render_body_discarded:function(){return this._render_stateBodyHelper("<div>"+d("The job creating this dataset was cancelled before completion")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_queued:function(){return this._render_stateBodyHelper("<div>"+d("This job is waiting to run")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_upload:function(){return this._render_stateBodyHelper("<div>"+d("This dataset is currently uploading")+"</div>")},_render_body_setting_metadata:function(){return this._render_stateBodyHelper("<div>"+d("Metadata is being auto-detected")+"</div>")},_render_body_running:function(){return this._render_stateBodyHelper("<div>"+d("This job is currently running")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_paused:function(){return this._render_stateBodyHelper("<div>"+d('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume')+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_error:function(){var h=['<span class="help-text">',d("An error occurred with this dataset"),":</span>",'<div class="job-error-text">',$.trim(this.model.get("misc_info")),"</div>"].join("");if(!this.model.get("purged")){h="<div>"+this.model.get("misc_blurb")+"</div>"+h}return this._render_stateBodyHelper(h,[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers))},_render_body_empty:function(){return this._render_stateBodyHelper("<div>"+d("No data")+": <i>"+this.model.get("misc_blurb")+"</i></div>",this.defaultPrimaryActionButtonRenderers)},_render_body_failed_metadata:function(){var h=$('<div class="warningmessagesmall"></div>').append($("<strong/>").text(d("An error occurred setting the metadata for this dataset"))),i=this._render_body_ok();i.prepend(h);return i},_render_body_ok:function(){var h=this,j=$(c.templates.body(this.model.toJSON())),i=[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers);j.find(".dataset-actions .left").append(_.map(i,function(k){return k.call(h)}));if(this.model.isDeletedOrPurged()){return j}return j},events:{"click .dataset-title-bar":"toggleBodyVisibility","keydown .dataset-title-bar":"toggleBodyVisibility","click .dataset-selector":"toggleSelect"},expandBody:function(){var h=this;function i(){h.$el.children(".dataset-body").replaceWith(h._render_body());h.$el.children(".dataset-body").slideDown(h.fxSpeed,function(){h.expanded=true;h.trigger("body-expanded",h.model.get("id"))})}if(this.model.inReadyState()&&!this.model.hasDetails()){this.model.fetch({silent:true}).always(function(j){h.urls=h.model.urls();i()})}else{i()}},collapseBody:function(){var h=this;this.$el.children(".dataset-body").slideUp(h.fxSpeed,function(){h.expanded=false;h.trigger("body-collapsed",h.model.get("id"))})},draggableOn:function(){this.draggable=true;this.dragStartHandler=_.bind(this._dragStartHandler,this);this.dragEndHandler=_.bind(this._dragEndHandler,this);var h=this.$el.find(".dataset-title-bar").attr("draggable",true).get(0);h.addEventListener("dragstart",this.dragStartHandler,false);h.addEventListener("dragend",this.dragEndHandler,false)},draggableOff:function(){this.draggable=false;var h=this.$el.find(".dataset-title-bar").attr("draggable",false).get(0);h.removeEventListener("dragstart",this.dragStartHandler,false);h.removeEventListener("dragend",this.dragEndHandler,false)},toggleDraggable:function(){if(this.draggable){this.draggableOff()}else{this.draggableOn()}},_dragStartHandler:function(h){this.trigger("dragstart",this);h.dataTransfer.effectAllowed="move";h.dataTransfer.setData("text",JSON.stringify(this.model.toJSON()));return false},_dragEndHandler:function(h){this.trigger("dragend",this);return false},remove:function(i){var h=this;this.$el.fadeOut(h.fxSpeed,function(){h.$el.remove();h.off();if(i){i()}})},toString:function(){var h=(this.model)?(this.model+""):("(no model)");return"HDABaseView("+h+")"}});var a=['<div class="dataset hda">','<div class="dataset-warnings">',"<% if( hda.error ){ %>",'<div class="errormessagesmall">',d("There was an error getting the data for this dataset"),":<%- hda.error %>","</div>","<% } %>","<% if( hda.deleted ){ %>","<% if( hda.purged ){ %>",'<div class="dataset-purged-msg warningmessagesmall"><strong>',d("This dataset has been deleted and removed from disk")+".","</strong></div>","<% } else { %>",'<div class="dataset-deleted-msg warningmessagesmall"><strong>',d("This dataset has been deleted")+".","</strong></div>","<% } %>","<% } %>","<% if( !hda.visible ){ %>",'<div class="dataset-hidden-msg warningmessagesmall"><strong>',d("This dataset has been hidden")+".","</strong></div>","<% } %>","</div>",'<div class="dataset-selector">','<span class="fa fa-2x fa-square-o"></span>',"</div>",'<div class="dataset-primary-actions"></div>','<div class="dataset-title-bar clear" tabindex="0">','<span class="dataset-state-icon state-icon"></span>','<div class="dataset-title">','<span class="hda-hid"><%- hda.hid %></span> ','<span class="dataset-name"><%- hda.name %></span>',"</div>","</div>",'<div class="dataset-body"></div>',"</div>"].join("");var f=['<div class="dataset-body">',"<% if( hda.body ){ %>",'<div class="dataset-summary">',"<%= hda.body %>","</div>",'<div class="dataset-actions clear">','<div class="left"></div>','<div class="right"></div>',"</div>","<% } else { %>",'<div class="dataset-summary">',"<% if( hda.misc_blurb ){ %>",'<div class="dataset-blurb">','<span class="value"><%- hda.misc_blurb %></span>',"</div>","<% } %>","<% if( hda.data_type ){ %>",'<div class="dataset-datatype">','<label class="prompt">',d("format"),"</label>",'<span class="value"><%- hda.data_type %></span>',"</div>","<% } %>","<% if( hda.metadata_dbkey ){ %>",'<div class="dataset-dbkey">','<label class="prompt">',d("database"),"</label>",'<span class="value">',"<%- hda.metadata_dbkey %>","</span>","</div>","<% } %>","<% if( hda.misc_info ){ %>",'<div class="dataset-info">','<span class="value"><%- hda.misc_info %></span>',"</div>","<% } %>","</div>",'<div class="dataset-actions clear">','<div class="left"></div>','<div class="right"></div>',"</div>","<% if( !hda.deleted ){ %>",'<div class="tags-display"></div>','<div class="annotation-display"></div>','<div class="dataset-display-applications">',"<% _.each( hda.display_apps, function( app ){ %>",'<div class="display-application">','<span class="display-application-location"><%- app.label %></span> ','<span class="display-application-links">',"<% _.each( app.links, function( link ){ %>",'<a target="<%= link.target %>" href="<%= link.href %>">',"<% print( _l( link.text ) ); %>","</a> ","<% }); %>","</span>","</div>","<% }); %>","<% _.each( hda.display_types, function( app ){ %>",'<div class="display-application">','<span class="display-application-location"><%- app.label %></span> ','<span class="display-application-links">',"<% _.each( app.links, function( link ){ %>",'<a target="<%= link.target %>" href="<%= link.href %>">',"<% print( _l( link.text ) ); %>","</a> ","<% }); %>","</span>","</div>","<% }); %>","</div>",'<div class="dataset-peek">',"<% if( hda.peek ){ %>",'<pre class="peek"><%= hda.peek %></pre>',"<% } %>","</div>","<% } %>","<% } %>","</div>"].join("");c.templates={skeleton:function(h){return _.template(a,h,{variable:"hda"})},body:function(h){return _.template(f,h,{variable:"hda"})}};return{HistoryContentBaseView:g,HDABaseView:c}});
\ No newline at end of file
+define(["mvc/dataset/hda-model","mvc/base-mvc","utils/localization"],function(e,b,d){var g=Backbone.View.extend(b.LoggableMixin).extend({tagName:"div",fxSpeed:"fast",_queueNewRender:function(i,j){j=(j===undefined)?(true):(j);var h=this;if(j){$(h).queue(function(k){this.$el.fadeOut(h.fxSpeed,k)})}$(h).queue(function(k){this.$el.empty().attr("class",h.className).addClass("state-"+h.model.get("state")).append(i.children());if(this.selectable){this.showSelector(0)}k()});if(j){$(h).queue(function(k){this.$el.fadeIn(h.fxSpeed,k)})}$(h).queue(function(k){this.trigger("rendered",h);if(this.model.inReadyState()){this.trigger("rendered:ready",h)}if(this.draggable){this.draggableOn()}k()})},toggleBodyVisibility:function(k,i){var h=32,j=13;if(k&&(k.type==="keydown")&&!(k.keyCode===h||k.keyCode===j)){return true}var l=this.$el.find(".dataset-body");i=(i===undefined)?(!l.is(":visible")):(i);if(i){this.expandBody()}else{this.collapseBody()}return false},showSelector:function(){if(this.selected){this.select(null,true)}this.selectable=true;this.trigger("selectable",true,this);this.$(".dataset-primary-actions").hide();this.$(".dataset-selector").show()},hideSelector:function(){this.selectable=false;this.trigger("selectable",false,this);this.$(".dataset-selector").hide();this.$(".dataset-primary-actions").show()},toggleSelector:function(){if(!this.$el.find(".dataset-selector").is(":visible")){this.showSelector()}else{this.hideSelector()}},select:function(h){this.$el.find(".dataset-selector span").removeClass("fa-square-o").addClass("fa-check-square-o");if(!this.selected){this.trigger("selected",this);this.selected=true}return false},deselect:function(h){this.$el.find(".dataset-selector span").removeClass("fa-check-square-o").addClass("fa-square-o");if(this.selected){this.trigger("de-selected",this);this.selected=false}return false},toggleSelect:function(h){if(this.selected){this.deselect(h)}else{this.select(h)}},});var c=g.extend({className:"dataset hda history-panel-hda",id:function(){return"hda-"+this.model.get("id")},initialize:function(h){if(h.logger){this.logger=this.model.logger=h.logger}this.log(this+".initialize:",h);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton];this.linkTarget=h.linkTarget||"_blank";this.selectable=h.selectable||false;this.selected=h.selected||false;this.expanded=h.expanded||false;this.draggable=h.draggable||false;this._setUpListeners()},_setUpListeners:function(){this.model.on("change",function(i,h){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this)},render:function(i){this.$el.find("[title]").tooltip("destroy");this.urls=this.model.urls();var h=this._buildNewRender();this._queueNewRender(h,i);return this},_buildNewRender:function(){var h=$(c.templates.skeleton(this.model.toJSON()));h.find(".dataset-primary-actions").append(this._render_titleButtons());h.children(".dataset-body").replaceWith(this._render_body());this._setUpBehaviors(h);return h},_setUpBehaviors:function(h){h=h||this.$el;make_popup_menus(h);h.find("[title]").tooltip({placement:"bottom"})},_render_titleButtons:function(){return[this._render_displayButton()]},_render_displayButton:function(){if((this.model.get("state")===e.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(this.model.get("state")===e.HistoryDatasetAssociation.STATES.DISCARDED)||(!this.model.get("accessible"))){return null}var i={target:this.linkTarget,classes:"dataset-display"};if(this.model.get("purged")){i.disabled=true;i.title=d("Cannot display datasets removed from disk")}else{if(this.model.get("state")===e.HistoryDatasetAssociation.STATES.UPLOAD){i.disabled=true;i.title=d("This dataset must finish uploading before it can be viewed")}else{if(this.model.get("state")===e.HistoryDatasetAssociation.STATES.NEW){i.disabled=true;i.title=d("This dataset is not yet viewable")}else{i.title=d("View data");i.href=this.urls.display;var h=this;i.onclick=function(j){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Data Viewer: "+h.model.get("name"),type:"url",content:h.urls.display});j.preventDefault()}}}}}i.faIcon="fa-eye";return faIconButton(i)},_render_downloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}var i=this.urls,j=this.model.get("meta_files");if(_.isEmpty(j)){return $(['<a href="'+i.download+'" title="'+d("Download")+'" ','class="icon-btn dataset-download-btn">','<span class="fa fa-floppy-o"></span>',"</a>"].join(""))}var k="dataset-"+this.model.get("id")+"-popup",h=['<div popupmenu="'+k+'">','<a href="'+i.download+'">',d("Download dataset"),"</a>","<a>"+d("Additional files")+"</a>",_.map(j,function(l){return['<a class="action-button" href="',i.meta_download+l.file_type,'">',d("Download")," ",l.file_type,"</a>"].join("")}).join("\n"),"</div>",'<div class="icon-btn-group">','<a href="'+i.download+'" title="'+d("Download")+'" ','class="icon-btn dataset-download-btn">','<span class="fa fa-floppy-o"></span>','</a><a class="icon-btn popup" id="'+k+'">','<span class="fa fa-caret-down"></span>',"</a>","</div>"].join("\n");return $(h)},_render_showParamsButton:function(){return faIconButton({title:d("View details"),classes:"dataset-params-btn",href:this.urls.show_params,target:this.linkTarget,faIcon:"fa-info-circle"})},_render_body:function(){var i=$('<div>Error: unknown dataset state "'+this.model.get("state")+'".</div>'),h=this["_render_body_"+this.model.get("state")];if(_.isFunction(h)){i=h.call(this)}this._setUpBehaviors(i);if(this.expanded){i.show()}return i},_render_stateBodyHelper:function(h,k){k=k||[];var i=this,j=$(c.templates.body(_.extend(this.model.toJSON(),{body:h})));j.find(".dataset-actions .left").append(_.map(k,function(l){return l.call(i)}));return j},_render_body_new:function(){return this._render_stateBodyHelper("<div>"+d("This is a new dataset and not all of its data are available yet")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_noPermission:function(){return this._render_stateBodyHelper("<div>"+d("You do not have permission to view this dataset")+"</div>")},_render_body_discarded:function(){return this._render_stateBodyHelper("<div>"+d("The job creating this dataset was cancelled before completion")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_queued:function(){return this._render_stateBodyHelper("<div>"+d("This job is waiting to run")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_upload:function(){return this._render_stateBodyHelper("<div>"+d("This dataset is currently uploading")+"</div>")},_render_body_setting_metadata:function(){return this._render_stateBodyHelper("<div>"+d("Metadata is being auto-detected")+"</div>")},_render_body_running:function(){return this._render_stateBodyHelper("<div>"+d("This job is currently running")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_paused:function(){return this._render_stateBodyHelper("<div>"+d('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume')+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_error:function(){var h=['<span class="help-text">',d("An error occurred with this dataset"),":</span>",'<div class="job-error-text">',$.trim(this.model.get("misc_info")),"</div>"].join("");if(!this.model.get("purged")){h="<div>"+this.model.get("misc_blurb")+"</div>"+h}return this._render_stateBodyHelper(h,[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers))},_render_body_empty:function(){return this._render_stateBodyHelper("<div>"+d("No data")+": <i>"+this.model.get("misc_blurb")+"</i></div>",this.defaultPrimaryActionButtonRenderers)},_render_body_failed_metadata:function(){var h=$('<div class="warningmessagesmall"></div>').append($("<strong/>").text(d("An error occurred setting the metadata for this dataset"))),i=this._render_body_ok();i.prepend(h);return i},_render_body_ok:function(){var h=this,j=$(c.templates.body(this.model.toJSON())),i=[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers);j.find(".dataset-actions .left").append(_.map(i,function(k){return k.call(h)}));if(this.model.isDeletedOrPurged()){return j}return j},events:{"click .dataset-title-bar":"toggleBodyVisibility","keydown .dataset-title-bar":"toggleBodyVisibility","click .dataset-selector":"toggleSelect"},expandBody:function(){var h=this;function i(){h.$el.children(".dataset-body").replaceWith(h._render_body());h.$el.children(".dataset-body").slideDown(h.fxSpeed,function(){h.expanded=true;h.trigger("body-expanded",h.model.get("id"))})}if(this.model.inReadyState()&&!this.model.hasDetails()){this.model.fetch({silent:true}).always(function(j){h.urls=h.model.urls();i()})}else{i()}},collapseBody:function(){var h=this;this.$el.children(".dataset-body").slideUp(h.fxSpeed,function(){h.expanded=false;h.trigger("body-collapsed",h.model.get("id"))})},draggableOn:function(){this.draggable=true;this.dragStartHandler=_.bind(this._dragStartHandler,this);this.dragEndHandler=_.bind(this._dragEndHandler,this);var h=this.$el.find(".dataset-title-bar").attr("draggable",true).get(0);h.addEventListener("dragstart",this.dragStartHandler,false);h.addEventListener("dragend",this.dragEndHandler,false)},draggableOff:function(){this.draggable=false;var h=this.$el.find(".dataset-title-bar").attr("draggable",false).get(0);h.removeEventListener("dragstart",this.dragStartHandler,false);h.removeEventListener("dragend",this.dragEndHandler,false)},toggleDraggable:function(){if(this.draggable){this.draggableOff()}else{this.draggableOn()}},_dragStartHandler:function(h){this.trigger("dragstart",this);h.dataTransfer.effectAllowed="move";h.dataTransfer.setData("text",JSON.stringify(this.model.toJSON()));return false},_dragEndHandler:function(h){this.trigger("dragend",this);return false},remove:function(i){var h=this;this.$el.fadeOut(h.fxSpeed,function(){h.$el.remove();h.off();if(i){i()}})},toString:function(){var h=(this.model)?(this.model+""):("(no model)");return"HDABaseView("+h+")"}});var a=_.template(['<div class="dataset hda">','<div class="dataset-warnings">',"<% if( hda.error ){ %>",'<div class="errormessagesmall">',d("There was an error getting the data for this dataset"),":<%- hda.error %>","</div>","<% } %>","<% if( hda.deleted ){ %>","<% if( hda.purged ){ %>",'<div class="dataset-purged-msg warningmessagesmall"><strong>',d("This dataset has been deleted and removed from disk")+".","</strong></div>","<% } else { %>",'<div class="dataset-deleted-msg warningmessagesmall"><strong>',d("This dataset has been deleted")+".","</strong></div>","<% } %>","<% } %>","<% if( !hda.visible ){ %>",'<div class="dataset-hidden-msg warningmessagesmall"><strong>',d("This dataset has been hidden")+".","</strong></div>","<% } %>","</div>",'<div class="dataset-selector">','<span class="fa fa-2x fa-square-o"></span>',"</div>",'<div class="dataset-primary-actions"></div>','<div class="dataset-title-bar clear" tabindex="0">','<span class="dataset-state-icon state-icon"></span>','<div class="dataset-title">','<span class="hda-hid"><%- hda.hid %></span> ','<span class="dataset-name"><%- hda.name %></span>',"</div>","</div>",'<div class="dataset-body"></div>',"</div>"].join(""));var f=_.template(['<div class="dataset-body">',"<% if( hda.body ){ %>",'<div class="dataset-summary">',"<%= hda.body %>","</div>",'<div class="dataset-actions clear">','<div class="left"></div>','<div class="right"></div>',"</div>","<% } else { %>",'<div class="dataset-summary">',"<% if( hda.misc_blurb ){ %>",'<div class="dataset-blurb">','<span class="value"><%- hda.misc_blurb %></span>',"</div>","<% } %>","<% if( hda.data_type ){ %>",'<div class="dataset-datatype">','<label class="prompt">',d("format"),"</label>",'<span class="value"><%- hda.data_type %></span>',"</div>","<% } %>","<% if( hda.metadata_dbkey ){ %>",'<div class="dataset-dbkey">','<label class="prompt">',d("database"),"</label>",'<span class="value">',"<%- hda.metadata_dbkey %>","</span>","</div>","<% } %>","<% if( hda.misc_info ){ %>",'<div class="dataset-info">','<span class="value"><%- hda.misc_info %></span>',"</div>","<% } %>","</div>",'<div class="dataset-actions clear">','<div class="left"></div>','<div class="right"></div>',"</div>","<% if( !hda.deleted ){ %>",'<div class="tags-display"></div>','<div class="annotation-display"></div>','<div class="dataset-display-applications">',"<% _.each( hda.display_apps, function( app ){ %>",'<div class="display-application">','<span class="display-application-location"><%- app.label %></span> ','<span class="display-application-links">',"<% _.each( app.links, function( link ){ %>",'<a target="<%= link.target %>" href="<%= link.href %>">',"<% print( _l( link.text ) ); %>","</a> ","<% }); %>","</span>","</div>","<% }); %>","<% _.each( hda.display_types, function( app ){ %>",'<div class="display-application">','<span class="display-application-location"><%- app.label %></span> ','<span class="display-application-links">',"<% _.each( app.links, function( link ){ %>",'<a target="<%= link.target %>" href="<%= link.href %>">',"<% print( _l( link.text ) ); %>","</a> ","<% }); %>","</span>","</div>","<% }); %>","</div>",'<div class="dataset-peek">',"<% if( hda.peek ){ %>",'<pre class="peek"><%= hda.peek %></pre>',"<% } %>","</div>","<% } %>","<% } %>","</div>"].join(""));c.templates={skeleton:function(h){return a({_l:d,hda:h})},body:function(h){return f({_l:d,hda:h})}};return{HistoryContentBaseView:g,HDABaseView:c}});
\ No newline at end of file
diff -r 7fd77febf6bb8aa7a6acde9c3a31429f7b1ee99f -r 31740dffaf9ae4e345fd2d4de4ab44ed133dd567 static/scripts/packed/viz/trackster.js
--- a/static/scripts/packed/viz/trackster.js
+++ b/static/scripts/packed/viz/trackster.js
@@ -1,1 +1,1 @@
-var ui=null;var view=null;var browser_router=null;require(["utils/utils","libs/jquery/jstorage","libs/jquery/jquery.event.drag","libs/jquery/jquery.event.hover","libs/jquery/jquery.mousewheel","libs/jquery/jquery-ui","libs/jquery/select2","libs/farbtastic","libs/jquery/jquery.form","libs/jquery/jquery.rating","mvc/ui"],function(a){a.cssLoadFile("static/style/jquery.rating.css");a.cssLoadFile("static/style/autocomplete_tagging.css");a.cssLoadFile("static/style/jquery-ui/smoothness/jquery-ui.css");a.cssLoadFile("static/style/library.css");a.cssLoadFile("static/style/trackster.css")});define(["libs/backbone/backbone","viz/trackster_ui"],function(b,a){var c=Backbone.View.extend({initialize:function(){ui=new a.TracksterUI(galaxy_config.root);ui.createButtonMenu();ui.buttonMenu.$el.attr("style","float: right");$("#center .unified-panel-header-inner").append(ui.buttonMenu.$el);$("#right .unified-panel-title").append("Bookmarks");$("#right .unified-panel-icons").append("<a id='add-bookmark-button' class='icon-button menu-button plus-button' href='javascript:void(0);' title='Add bookmark'></a>");$("#right-border").click(function(){view.resize_window()});force_right_panel("hide");if(galaxy_config.app.id){this.view_existing()}else{this.view_new()}},view_existing:function(){var d=galaxy_config.app.viz_config;view=ui.create_visualization({container:$("#center .unified-panel-body"),name:d.title,vis_id:d.vis_id,dbkey:d.dbkey},d.viewport,d.tracks,d.bookmarks,true);this.init_editor()},view_new:function(){var d=this;$.ajax({url:galaxy_config.root+"api/genomes?chrom_info=True",data:{},error:function(){alert("Couldn't create new browser.")},success:function(e){Galaxy.modal.show({title:"New Visualization",body:d.template_view_new(e),buttons:{Cancel:function(){window.location=galaxy_config.root+"visualization/list"},Create:function(){d.create_browser($("#new-title").val(),$("#new-dbkey").val());Galaxy.modal.hide()}}});if(galaxy_config.app.default_dbkey){$("#new-dbkey").val(galaxy_config.app.default_dbkey)}$("#new-title").focus();$("select[name='dbkey']").select2();$("#overlay").css("overflow","auto")}})},template_view_new:function(d){var f='<form id="new-browser-form" action="javascript:void(0);" method="post" onsubmit="return false;"><div class="form-row"><label for="new-title">Browser name:</label><div class="form-row-input"><input type="text" name="title" id="new-title" value="Unnamed"></input></div><div style="clear: both;"></div></div><div class="form-row"><label for="new-dbkey">Reference genome build (dbkey): </label><div class="form-row-input"><select name="dbkey" id="new-dbkey">';for(var e=0;e<d.length;e++){f+='<option value="'+d[e][1]+'">'+d[e][0]+"</option>"}f+='</select></div><div style="clear: both;"></div></div><div class="form-row">Is the build not listed here? <a href="'+galaxy_config.root+'user/dbkeys?use_panels=True">Add a Custom Build</a></div></form>';return f},create_browser:function(e,d){$(document).trigger("convert_to_values");view=ui.create_visualization({container:$("#center .unified-panel-body"),name:e,dbkey:d},galaxy_config.app.gene_region);this.init_editor();view.editor=true},init_editor:function(){$("#center .unified-panel-title").text(view.config.get_value("name")+" ("+view.dbkey+")");if(galaxy_config.app.add_dataset){$.ajax({url:galaxy_config.root+"api/datasets/"+galaxy_config.app.add_dataset,data:{hda_ldda:"hda",data_type:"track_config"},dataType:"json",success:function(d){view.add_drawable(a.object_from_template(d,view,view))}})}$("#add-bookmark-button").click(function(){var e=view.chrom+":"+view.low+"-"+view.high,d="Bookmark description";return ui.add_bookmark(e,d,true)});ui.init_keyboard_nav(view)}});return{GalaxyApp:c}});
\ No newline at end of file
+var ui=null;var view=null;var browser_router=null;require(["utils/utils","libs/jquery/jquery.event.drag","libs/jquery/jquery.event.hover","libs/jquery/jquery.mousewheel","libs/jquery/jquery-ui","libs/jquery/select2","libs/farbtastic","libs/jquery/jquery.form","libs/jquery/jquery.rating","mvc/ui"],function(a){a.cssLoadFile("static/style/jquery.rating.css");a.cssLoadFile("static/style/autocomplete_tagging.css");a.cssLoadFile("static/style/jquery-ui/smoothness/jquery-ui.css");a.cssLoadFile("static/style/library.css");a.cssLoadFile("static/style/trackster.css")});define(["libs/backbone/backbone","viz/trackster_ui"],function(b,a){var c=Backbone.View.extend({initialize:function(){ui=new a.TracksterUI(galaxy_config.root);ui.createButtonMenu();ui.buttonMenu.$el.attr("style","float: right");$("#center .unified-panel-header-inner").append(ui.buttonMenu.$el);$("#right .unified-panel-title").append("Bookmarks");$("#right .unified-panel-icons").append("<a id='add-bookmark-button' class='icon-button menu-button plus-button' href='javascript:void(0);' title='Add bookmark'></a>");$("#right-border").click(function(){view.resize_window()});force_right_panel("hide");if(galaxy_config.app.id){this.view_existing()}else{this.view_new()}},view_existing:function(){var d=galaxy_config.app.viz_config;view=ui.create_visualization({container:$("#center .unified-panel-body"),name:d.title,vis_id:d.vis_id,dbkey:d.dbkey},d.viewport,d.tracks,d.bookmarks,true);this.init_editor()},view_new:function(){var d=this;$.ajax({url:galaxy_config.root+"api/genomes?chrom_info=True",data:{},error:function(){alert("Couldn't create new browser.")},success:function(e){Galaxy.modal.show({title:"New Visualization",body:d.template_view_new(e),buttons:{Cancel:function(){window.location=galaxy_config.root+"visualization/list"},Create:function(){d.create_browser($("#new-title").val(),$("#new-dbkey").val());Galaxy.modal.hide()}}});if(galaxy_config.app.default_dbkey){$("#new-dbkey").val(galaxy_config.app.default_dbkey)}$("#new-title").focus();$("select[name='dbkey']").select2();$("#overlay").css("overflow","auto")}})},template_view_new:function(d){var f='<form id="new-browser-form" action="javascript:void(0);" method="post" onsubmit="return false;"><div class="form-row"><label for="new-title">Browser name:</label><div class="form-row-input"><input type="text" name="title" id="new-title" value="Unnamed"></input></div><div style="clear: both;"></div></div><div class="form-row"><label for="new-dbkey">Reference genome build (dbkey): </label><div class="form-row-input"><select name="dbkey" id="new-dbkey">';for(var e=0;e<d.length;e++){f+='<option value="'+d[e][1]+'">'+d[e][0]+"</option>"}f+='</select></div><div style="clear: both;"></div></div><div class="form-row">Is the build not listed here? <a href="'+galaxy_config.root+'user/dbkeys?use_panels=True">Add a Custom Build</a></div></form>';return f},create_browser:function(e,d){$(document).trigger("convert_to_values");view=ui.create_visualization({container:$("#center .unified-panel-body"),name:e,dbkey:d},galaxy_config.app.gene_region);this.init_editor();view.editor=true},init_editor:function(){$("#center .unified-panel-title").text(view.config.get_value("name")+" ("+view.dbkey+")");if(galaxy_config.app.add_dataset){$.ajax({url:galaxy_config.root+"api/datasets/"+galaxy_config.app.add_dataset,data:{hda_ldda:"hda",data_type:"track_config"},dataType:"json",success:function(d){view.add_drawable(a.object_from_template(d,view,view))}})}$("#add-bookmark-button").click(function(){var e=view.chrom+":"+view.low+"-"+view.high,d="Bookmark description";return ui.add_bookmark(e,d,true)});ui.init_keyboard_nav(view)}});return{GalaxyApp:c}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/2aafaa6a9159/
Changeset: 2aafaa6a9159
User: jmchilton
Date: 2014-05-28 18:58:33
Summary: Remove invalid comment.
Affected #: 1 file
diff -r 23aac3b3c0e2cfe784d8c5dfd94cc3b6ccff7b7c -r 2aafaa6a915977f2f5970e01ccfbf51dc3db7c14 static/scripts/galaxy.tools.js
--- a/static/scripts/galaxy.tools.js
+++ b/static/scripts/galaxy.tools.js
@@ -30,7 +30,7 @@
'min_option_count': 2 // Don't show multiple select switch if only
// one dataset available.
},
- 'select_collection': { // NOT YET IMPLEMENTED
+ 'select_collection': {
'icon_class': 'fa-folder-o',
'select_by': 'Run tool in parallel across dataset collection',
'allow_remap': false
https://bitbucket.org/galaxy/galaxy-central/commits/a763290e92ad/
Changeset: a763290e92ad
User: jmchilton
Date: 2014-05-28 18:58:33
Summary: Improvements to workflow extraction tests.
Affected #: 1 file
diff -r 2aafaa6a915977f2f5970e01ccfbf51dc3db7c14 -r a763290e92adb2215f4cba507e3eb9fe52618d2c test/api/test_workflows.py
--- a/test/api/test_workflows.py
+++ b/test/api/test_workflows.py
@@ -75,11 +75,12 @@
@skip_without_tool( "cat1" )
def test_extract_from_history( self ):
+ # Run the simple test workflow and extract it back out from history
workflow = self.workflow_populator.load_workflow( name="test_for_extract" )
workflow_request, history_id = self._setup_workflow_run( workflow )
contents_response = self._get( "histories/%s/contents" % history_id )
self._assert_status_code_is( contents_response, 200 )
- hda_ids = map( lambda c: c[ "hid" ], contents_response.json() )
+ input_hids = map( lambda c: c[ "hid" ], contents_response.json() )
run_workflow_response = self._post( "workflows", data=workflow_request )
self._assert_status_code_is( run_workflow_response, 200 )
@@ -89,23 +90,16 @@
jobs_response = self._get( "jobs", data=data )
self._assert_status_code_is( jobs_response, 200 )
cat1_job_id = jobs_response.json()[ 0 ][ "id" ]
-
- contents_response = self._get( "history/%s/contents", data=data )
- create_from_data = dict(
+ downloaded_workflow = self._extract_and_download_workflow(
from_history_id=history_id,
- dataset_ids=dumps( hda_ids ),
+ dataset_ids=dumps( input_hids ),
job_ids=dumps( [ cat1_job_id ] ),
workflow_name="test import from history",
)
- create_workflow_response = self._post( "workflows", data=create_from_data )
- self._assert_status_code_is( create_workflow_response, 200 )
-
- new_workflow_id = create_workflow_response.json()[ "id" ]
- download_response = self._get( "workflows/%s/download" % new_workflow_id )
- self._assert_status_code_is( download_response, 200 )
- downloaded_workflow = download_response.json()
self.assertEquals( downloaded_workflow[ "name" ], "test import from history" )
assert len( downloaded_workflow[ "steps" ] ) == 3
+ self._get_steps_of_type( downloaded_workflow, "data_input", expected_len=2 )
+ self._get_steps_of_type( downloaded_workflow, "tool", expected_len=1 )
@skip_without_tool( "collection_paired_test" )
def test_extract_workflows_with_dataset_collections( self ):
@@ -241,7 +235,8 @@
def _get_steps_of_type( self, downloaded_workflow, type, expected_len=None ):
steps = [ s for s in downloaded_workflow[ "steps" ].values() if s[ "type" ] == type ]
if expected_len is not None:
- assert len( steps ) == expected_len
+ n = len( steps )
+ assert n == expected_len, "Expected %d steps of type %s, found %d" % ( expected_len, type, n )
return steps
@skip_without_tool( "random_lines1" )
https://bitbucket.org/galaxy/galaxy-central/commits/7fd77febf6bb/
Changeset: 7fd77febf6bb
User: jmchilton
Date: 2014-05-28 18:58:33
Summary: Workflow extraction test from copied datasets.
Detailed some bugs I found in the test case and recorded them here https://trello.com/c/mKzLbM2P.
Affected #: 1 file
diff -r a763290e92adb2215f4cba507e3eb9fe52618d2c -r 7fd77febf6bb8aa7a6acde9c3a31429f7b1ee99f test/api/test_workflows.py
--- a/test/api/test_workflows.py
+++ b/test/api/test_workflows.py
@@ -1,6 +1,7 @@
from base import api
from json import dumps
from json import loads
+import operator
import time
from .helpers import WorkflowPopulator
from .helpers import DatasetPopulator
@@ -75,21 +76,11 @@
@skip_without_tool( "cat1" )
def test_extract_from_history( self ):
+ history_id = self.dataset_populator.new_history()
# Run the simple test workflow and extract it back out from history
- workflow = self.workflow_populator.load_workflow( name="test_for_extract" )
- workflow_request, history_id = self._setup_workflow_run( workflow )
+ cat1_job_id = self.__setup_and_run_cat1_workflow( history_id=history_id )
contents_response = self._get( "histories/%s/contents" % history_id )
- self._assert_status_code_is( contents_response, 200 )
- input_hids = map( lambda c: c[ "hid" ], contents_response.json() )
-
- run_workflow_response = self._post( "workflows", data=workflow_request )
- self._assert_status_code_is( run_workflow_response, 200 )
-
- self.dataset_populator.wait_for_history( history_id, assert_ok=True )
- data = dict( history_id=history_id, tool_id="cat1" )
- jobs_response = self._get( "jobs", data=data )
- self._assert_status_code_is( jobs_response, 200 )
- cat1_job_id = jobs_response.json()[ 0 ][ "id" ]
+ input_hids = map( lambda c: c[ "hid" ], contents_response.json()[ 0:2 ] )
downloaded_workflow = self._extract_and_download_workflow(
from_history_id=history_id,
dataset_ids=dumps( input_hids ),
@@ -97,9 +88,74 @@
workflow_name="test import from history",
)
self.assertEquals( downloaded_workflow[ "name" ], "test import from history" )
+ self.__assert_looks_like_cat1_example_workflow( downloaded_workflow )
+
+ def test_extract_with_copied_inputs( self ):
+ old_history_id = self.dataset_populator.new_history()
+ # Run the simple test workflow and extract it back out from history
+ self.__setup_and_run_cat1_workflow( history_id=old_history_id )
+
+ history_id = self.dataset_populator.new_history()
+
+ # Bug cannot mess up hids or these don't extract correctly. See Trello card here:
+ # https://trello.com/c/mKzLbM2P
+ # # create dummy dataset to complicate hid mapping
+ # self.dataset_populator.new_dataset( history_id, content="dummydataset" )
+ # offset = 1
+
+ offset = 0
+ old_contents = self._get( "histories/%s/contents" % old_history_id ).json()
+ for old_dataset in old_contents:
+ payload = dict(
+ source="hda",
+ content=old_dataset["id"]
+ )
+ response = self._post( "histories/%s/contents/datasets" % history_id, payload )
+ self._assert_status_code_is( response, 200 )
+ new_contents = self._get( "histories/%s/contents" % history_id ).json()
+ input_hids = map( lambda c: c[ "hid" ], new_contents[ (offset + 0):(offset + 2) ] )
+ cat1_job_id = self.__job_id( history_id, new_contents[ (offset + 2) ][ "id" ] )
+ downloaded_workflow = self._extract_and_download_workflow(
+ from_history_id=history_id,
+ dataset_ids=dumps( input_hids ),
+ job_ids=dumps( [ cat1_job_id ] ),
+ workflow_name="test import from history",
+ )
+ self.__assert_looks_like_cat1_example_workflow( downloaded_workflow )
+
+ def __assert_looks_like_cat1_example_workflow( self, downloaded_workflow ):
assert len( downloaded_workflow[ "steps" ] ) == 3
- self._get_steps_of_type( downloaded_workflow, "data_input", expected_len=2 )
- self._get_steps_of_type( downloaded_workflow, "tool", expected_len=1 )
+ input_steps = self._get_steps_of_type( downloaded_workflow, "data_input", expected_len=2 )
+ tool_step = self._get_steps_of_type( downloaded_workflow, "tool", expected_len=1 )[ 0 ]
+
+ input1 = tool_step[ "input_connections" ][ "input1" ]
+ input2 = tool_step[ "input_connections" ][ "queries_0|input2" ]
+
+ print downloaded_workflow
+ self.assertEquals( input_steps[ 0 ][ "id" ], input1[ "id" ] )
+ self.assertEquals( input_steps[ 1 ][ "id" ], input2[ "id" ] )
+
+ def __setup_and_run_cat1_workflow( self, history_id ):
+ workflow = self.workflow_populator.load_workflow( name="test_for_extract" )
+ workflow_request, history_id = self._setup_workflow_run( workflow, history_id=history_id )
+ run_workflow_response = self._post( "workflows", data=workflow_request )
+ self._assert_status_code_is( run_workflow_response, 200 )
+
+ self.dataset_populator.wait_for_history( history_id, assert_ok=True, timeout=10 )
+ return self.__cat_job_id( history_id )
+
+ def __cat_job_id( self, history_id ):
+ data = dict( history_id=history_id, tool_id="cat1" )
+ jobs_response = self._get( "jobs", data=data )
+ self._assert_status_code_is( jobs_response, 200 )
+ cat1_job_id = jobs_response.json()[ 0 ][ "id" ]
+ return cat1_job_id
+
+ def __job_id( self, history_id, dataset_id ):
+ url = "histories/%s/contents/%s/provenance" % ( history_id, dataset_id )
+ prov_response = self._get( url, data=dict( follow=False ) )
+ self._assert_status_code_is( prov_response, 200 )
+ return prov_response.json()[ "job_id" ]
@skip_without_tool( "collection_paired_test" )
def test_extract_workflows_with_dataset_collections( self ):
@@ -237,7 +293,7 @@
if expected_len is not None:
n = len( steps )
assert n == expected_len, "Expected %d steps of type %s, found %d" % ( expected_len, type, n )
- return steps
+ return sorted( steps, key=operator.itemgetter("id") )
@skip_without_tool( "random_lines1" )
def test_run_replace_params_by_tool( self ):
@@ -316,7 +372,7 @@
# renamed to 'the_new_name'.
assert "the_new_name" in map( lambda hda: hda[ "name" ], contents )
- def _setup_workflow_run( self, workflow ):
+ def _setup_workflow_run( self, workflow, history_id=None ):
uploaded_workflow_id = self.workflow_populator.create_workflow( workflow )
workflow_inputs = self._workflow_inputs( uploaded_workflow_id )
step_1 = step_2 = None
@@ -326,7 +382,8 @@
step_1 = key
if label == "WorkflowInput2":
step_2 = key
- history_id = self.dataset_populator.new_history()
+ if not history_id:
+ history_id = self.dataset_populator.new_history()
hda1 = self.dataset_populator.new_dataset( history_id, content="1 2 3" )
hda2 = self.dataset_populator.new_dataset( history_id, content="4 5 6" )
workflow_request = dict(
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Handle Tool Shed category administration in the Tool Shed's repository registry.
by commits-noreply@bitbucket.org 28 May '14
by commits-noreply@bitbucket.org 28 May '14
28 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/23aac3b3c0e2/
Changeset: 23aac3b3c0e2
User: greg
Date: 2014-05-28 18:08:04
Summary: Handle Tool Shed category administration in the Tool Shed's repository registry.
Affected #: 2 files
diff -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb -r 23aac3b3c0e2cfe784d8c5dfd94cc3b6ccff7b7c lib/galaxy/webapps/tool_shed/controllers/admin.py
--- a/lib/galaxy/webapps/tool_shed/controllers/admin.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/admin.py
@@ -132,6 +132,8 @@
category = trans.app.model.Category( name=name, description=description )
trans.sa_session.add( category )
trans.sa_session.flush()
+ # Update the Tool Shed's repository registry.
+ trans.app.repository_registry.add_category_entry( category )
message = "Category '%s' has been created" % category.name
status = 'done'
trans.response.send_redirect( web.url_for( controller='admin',
@@ -224,21 +226,32 @@
message=message,
status='error' ) )
category = suc.get_category( trans, id )
+ original_category_name = str( category.name )
+ original_category_description = str( category.description )
if kwd.get( 'edit_category_button', False ):
+ flush_needed = False
new_name = kwd.get( 'name', '' ).strip()
new_description = kwd.get( 'description', '' ).strip()
- if category.name != new_name or category.description != new_description:
+ if original_category_name != new_name:
if not new_name:
message = 'Enter a valid name'
status = 'error'
- elif category.name != new_name and suc.get_category_by_name( trans, new_name ):
+ elif original_category_name != new_name and suc.get_category_by_name( trans, new_name ):
message = 'A category with that name already exists'
status = 'error'
else:
category.name = new_name
+ flush_needed = True
+ if original_category_description != new_description:
category.description = new_description
+ if not flus_needed:
+ flush_needed = True
+ if flush_needed:
trans.sa_session.add( category )
trans.sa_session.flush()
+ if original_category_name != new_name:
+ # Update the Tool Shed's repository registry.
+ trans.app.repository_registry.edit_category_entry( original_category_name, new_name )
message = "The information has been saved for category '%s'" % ( category.name )
status = 'done'
return trans.response.send_redirect( web.url_for( controller='admin',
@@ -403,6 +416,8 @@
category.deleted = True
trans.sa_session.add( category )
trans.sa_session.flush()
+ # Update the Tool Shed's repository registry.
+ trans.app.repository_registry.remove_category_entry( category )
message += " %s " % category.name
else:
message = "No category ids received for deleting."
@@ -459,6 +474,8 @@
category.deleted = False
trans.sa_session.add( category )
trans.sa_session.flush()
+ # Update the Tool Shed's repository registry.
+ trans.app.repository_registry.add_category_entry( category )
count += 1
undeleted_categories += " %s" % category.name
message = "Undeleted %d categories: %s" % ( count, undeleted_categories )
diff -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb -r 23aac3b3c0e2cfe784d8c5dfd94cc3b6ccff7b7c lib/tool_shed/repository_registry.py
--- a/lib/tool_shed/repository_registry.py
+++ b/lib/tool_shed/repository_registry.py
@@ -41,6 +41,21 @@
self.load_viewable_repositories_and_suites_by_category()
self.load_repository_and_suite_tuples()
+ def add_category_entry( self, category ):
+ category_name = str( category.name )
+ if category_name not in self.viewable_repositories_and_suites_by_category:
+ self.viewable_repositories_and_suites_by_category[ category_name ] = 0
+ if category_name not in self.viewable_suites_by_category:
+ self.viewable_suites_by_category[ category_name ] = 0
+ if category_name not in self.viewable_valid_repositories_and_suites_by_category:
+ self.viewable_valid_repositories_and_suites_by_category[ category_name ] = 0
+ if category_name not in self.viewable_valid_suites_by_category:
+ self.viewable_valid_suites_by_category[ category_name ] = 0
+ if category_name not in self.certified_level_one_viewable_repositories_and_suites_by_category:
+ self.certified_level_one_viewable_repositories_and_suites_by_category[ category_name ] = 0
+ if category_name not in self.certified_level_one_viewable_suites_by_category:
+ self.certified_level_one_viewable_suites_by_category[ category_name ] = 0
+
def add_entry( self, repository ):
try:
if repository:
@@ -88,6 +103,44 @@
# will be corrected at next server start.
log.exception( "Handled error adding entry to repository registry: %s." % str( e ) )
+ def edit_category_entry( self, old_name, new_name ):
+ if old_name in self.viewable_repositories_and_suites_by_category:
+ val = self.viewable_repositories_and_suites_by_category[ old_name ]
+ del self.viewable_repositories_and_suites_by_category[ old_name ]
+ self.viewable_repositories_and_suites_by_category[ new_name ] = val
+ else:
+ self.viewable_repositories_and_suites_by_category[ new_name ] = 0
+ if old_name in self.viewable_valid_repositories_and_suites_by_category:
+ val = self.viewable_valid_repositories_and_suites_by_category[ old_name ]
+ del self.viewable_valid_repositories_and_suites_by_category[ old_name ]
+ self.viewable_valid_repositories_and_suites_by_category[ new_name ] = val
+ else:
+ self.viewable_valid_repositories_and_suites_by_category[ new_name ] = 0
+ if old_name in self.viewable_suites_by_category:
+ val = self.viewable_suites_by_category[ old_name ]
+ del self.viewable_suites_by_category[ old_name ]
+ self.viewable_suites_by_category[ new_name ] = val
+ else:
+ self.viewable_suites_by_category[ new_name ] = 0
+ if old_name in self.viewable_valid_suites_by_category:
+ val = self.viewable_valid_suites_by_category[ old_name ]
+ del self.viewable_valid_suites_by_category[ old_name ]
+ self.viewable_valid_suites_by_category[ new_name ] = val
+ else:
+ self.viewable_valid_suites_by_category[ new_name ] = 0
+ if old_name in self.certified_level_one_viewable_repositories_and_suites_by_category:
+ val = self.certified_level_one_viewable_repositories_and_suites_by_category[ old_name ]
+ del self.certified_level_one_viewable_repositories_and_suites_by_category[ old_name ]
+ self.certified_level_one_viewable_repositories_and_suites_by_category[ new_name ] = val
+ else:
+ self.certified_level_one_viewable_repositories_and_suites_by_category[ new_name ] = 0
+ if old_name in self.certified_level_one_viewable_suites_by_category:
+ val = self.certified_level_one_viewable_suites_by_category[ old_name ]
+ del self.certified_level_one_viewable_suites_by_category[ old_name ]
+ self.certified_level_one_viewable_suites_by_category[ new_name ] = val
+ else:
+ self.certified_level_one_viewable_suites_by_category[ new_name ] = 0
+
def get_certified_level_one_clause_list( self ):
certified_level_one_tuples = []
clause_list = []
@@ -266,6 +319,21 @@
if repository.type in [ rt_util.REPOSITORY_SUITE_DEFINITION ]:
self.certified_level_one_viewable_suites_by_category[ category_name ] += 1
+ def remove_category_entry( self, category ):
+ catgeory_name = str( category.name )
+ if catgeory_name in self.viewable_repositories_and_suites_by_category:
+ del self.viewable_repositories_and_suites_by_category[ catgeory_name ]
+ if catgeory_name in self.viewable_valid_repositories_and_suites_by_category:
+ del self.viewable_valid_repositories_and_suites_by_category[ catgeory_name ]
+ if catgeory_name in self.viewable_suites_by_category:
+ del self.viewable_suites_by_category[ catgeory_name ]
+ if catgeory_name in self.viewable_valid_suites_by_category:
+ del self.viewable_valid_suites_by_category[ catgeory_name ]
+ if catgeory_name in self.certified_level_one_viewable_repositories_and_suites_by_category:
+ del self.certified_level_one_viewable_repositories_and_suites_by_category[ catgeory_name ]
+ if catgeory_name in self.certified_level_one_viewable_suites_by_category:
+ del self.certified_level_one_viewable_suites_by_category[ catgeory_name ]
+
def remove_entry( self, repository ):
try:
if repository:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Continued down-sizing of the Tool Shed's shed_util_common module by moving certain functions to more appropriate locations. Some fixes for adding and removing entries to the Tool Shed's repository registry.
by commits-noreply@bitbucket.org 28 May '14
by commits-noreply@bitbucket.org 28 May '14
28 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/dbc3d5c3506e/
Changeset: dbc3d5c3506e
User: greg
Date: 2014-05-28 17:17:50
Summary: Continued down-sizing of the Tool Shed's shed_util_common module by moving certain functions to more appropriate locations. Some fixes for adding and removing entries to the Tool Shed's repository registry.
Affected #: 36 files
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py
--- a/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py
+++ b/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py
@@ -11,6 +11,7 @@
from tool_shed.galaxy_install import repository_util
from tool_shed.util import common_util
from tool_shed.util import encoding_util
+from tool_shed.util import hg_util
from tool_shed.util import metadata_util
from tool_shed.util import workflow_util
from tool_shed.util import tool_util
@@ -108,7 +109,7 @@
changeset_revisions = json.from_json_string( raw_text )
if len( changeset_revisions ) >= 1:
return changeset_revisions[ -1 ]
- return suc.INITIAL_CHANGELOG_HASH
+ return hg_util.INITIAL_CHANGELOG_HASH
def __get_value_mapper( self, trans, tool_shed_repository ):
value_mapper={ 'id' : trans.security.encode_id( tool_shed_repository.id ),
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -462,8 +462,8 @@
err_msg = ''
tool_shed_repository = tool_dependencies[ 0 ].tool_shed_repository
# Get the tool_dependencies.xml file from the repository.
- tool_dependencies_config = suc.get_config_from_disk( rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME,
- tool_shed_repository.repo_path( trans.app ) )
+ tool_dependencies_config = hg_util.get_config_from_disk( rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME,
+ tool_shed_repository.repo_path( trans.app ) )
installed_tool_dependencies = \
common_install_util.install_specified_packages( app=trans.app,
tool_shed_repository=tool_shed_repository,
@@ -505,7 +505,7 @@
'repository/get_latest_downloadable_changeset_revision%s' % params )
raw_text = common_util.tool_shed_get( trans.app, tool_shed_url, url )
latest_downloadable_revision = json.from_json_string( raw_text )
- if latest_downloadable_revision == suc.INITIAL_CHANGELOG_HASH:
+ if latest_downloadable_revision == hg_util.INITIAL_CHANGELOG_HASH:
message = 'Error retrieving the latest downloadable revision for this repository via the url <b>%s</b>.' % url
status = 'error'
else:
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/galaxy/webapps/tool_shed/api/repositories.py
--- a/lib/galaxy/webapps/tool_shed/api/repositories.py
+++ b/lib/galaxy/webapps/tool_shed/api/repositories.py
@@ -13,6 +13,7 @@
import tool_shed.repository_types.util as rt_util
import tool_shed.util.shed_util_common as suc
from tool_shed.galaxy_install import repository_util
+from tool_shed.util import basic_util
from tool_shed.util import encoding_util
from tool_shed.util import hg_util
from tool_shed.util import import_util
@@ -279,7 +280,7 @@
repository_status_info_dict,
import_results_tups )
import_util.check_status_and_reset_downloadable( trans, import_results_tups )
- suc.remove_dir( file_path )
+ basic_util.remove_dir( file_path )
# NOTE: the order of installation is defined in import_results_tups, but order will be lost
# when transferred to return_dict.
return_dict = {}
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -15,6 +15,7 @@
from galaxy.util import json
from galaxy.model.orm import and_
import tool_shed.util.shed_util_common as suc
+from tool_shed.util import basic_util
from tool_shed.util import common_util
from tool_shed.util import container_util
from tool_shed.util import encoding_util
@@ -1146,7 +1147,7 @@
# server account's .hgrc file to include the following setting:
# [web]
# allow_archive = bz2, gz, zip
- file_type_str = suc.get_file_type_str( changeset_revision, file_type )
+ file_type_str = export_util.get_file_type_str( changeset_revision, file_type )
repository.times_downloaded += 1
trans.sa_session.add( repository )
trans.sa_session.flush()
@@ -1188,7 +1189,7 @@
# Make sure the file is removed from disk after the contents have been downloaded.
os.unlink( repositories_archive.name )
repositories_archive_path, file_name = os.path.split( repositories_archive.name )
- suc.remove_dir( repositories_archive_path )
+ basic_util.remove_dir( repositories_archive_path )
return opened_archive
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans.app, repository_id, changeset_revision )
metadata = repository_metadata.metadata
@@ -1312,7 +1313,10 @@
return self.install_matched_repository_grid( trans, **kwd )
else:
kwd[ 'message' ] = "tool id: <b>%s</b><br/>tool name: <b>%s</b><br/>tool version: <b>%s</b><br/>exact matches only: <b>%s</b>" % \
- ( suc.stringify( tool_ids ), suc.stringify( tool_names ), suc.stringify( tool_versions ), str( exact_matches_checked ) )
+ ( basic_util.stringify( tool_ids ),
+ basic_util.stringify( tool_names ),
+ basic_util.stringify( tool_versions ),
+ str( exact_matches_checked ) )
self.matched_repository_grid.title = "Repositories with matching tools"
return self.matched_repository_grid( trans, **kwd )
else:
@@ -1320,9 +1324,9 @@
status = "error"
exact_matches_check_box = CheckboxField( 'exact_matches', checked=exact_matches_checked )
return trans.fill_template( '/webapps/tool_shed/repository/find_tools.mako',
- tool_id=suc.stringify( tool_ids ),
- tool_name=suc.stringify( tool_names ),
- tool_version=suc.stringify( tool_versions ),
+ tool_id=basic_util.stringify( tool_ids ),
+ tool_name=basic_util.stringify( tool_names ),
+ tool_version=basic_util.stringify( tool_versions ),
exact_matches_check_box=exact_matches_check_box,
message=message,
status=status )
@@ -1396,7 +1400,7 @@
return self.install_matched_repository_grid( trans, **kwd )
else:
kwd[ 'message' ] = "workflow name: <b>%s</b><br/>exact matches only: <b>%s</b>" % \
- ( suc.stringify( workflow_names ), str( exact_matches_checked ) )
+ ( basic_util.stringify( workflow_names ), str( exact_matches_checked ) )
self.matched_repository_grid.title = "Repositories with matching workflows"
return self.matched_repository_grid( trans, **kwd )
else:
@@ -1407,7 +1411,7 @@
workflow_names = []
exact_matches_check_box = CheckboxField( 'exact_matches', checked=exact_matches_checked )
return trans.fill_template( '/webapps/tool_shed/repository/find_workflows.mako',
- workflow_name=suc.stringify( workflow_names ),
+ workflow_name=basic_util.stringify( workflow_names ),
exact_matches_check_box=exact_matches_check_box,
message=message,
status=status )
@@ -1666,7 +1670,7 @@
if repository:
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
return suc.get_latest_downloadable_changeset_revision( trans.app, repository, repo )
- return suc.INITIAL_CHANGELOG_HASH
+ return hg_util.INITIAL_CHANGELOG_HASH
@web.json
def get_readme_files( self, trans, **kwd ):
@@ -1832,7 +1836,7 @@
#manafest.
repo_dir = repository.repo_path( trans.app )
# Get the tool_dependencies.xml file from disk.
- tool_dependencies_config = suc.get_config_from_disk( rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME, repo_dir )
+ tool_dependencies_config = hg_util.get_config_from_disk( rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME, repo_dir )
# Return the encoded contents of the tool_dependencies.xml file.
if tool_dependencies_config:
tool_dependencies_config_file = open( tool_dependencies_config, 'rb' )
@@ -1982,7 +1986,7 @@
repository_status_info_dict,
import_results_tups )
import_util.check_status_and_reset_downloadable( trans, import_results_tups )
- suc.remove_dir( file_path )
+ basic_util.remove_dir( file_path )
return trans.fill_template( '/webapps/tool_shed/repository/import_capsule_results.mako',
export_info_dict=export_info_dict,
import_results_tups=import_results_tups,
@@ -2318,7 +2322,7 @@
is_malicious = False
skip_tool_test = None
repository_dependencies = None
- if changeset_revision != suc.INITIAL_CHANGELOG_HASH:
+ if changeset_revision != hg_util.INITIAL_CHANGELOG_HASH:
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans.app, id, changeset_revision )
if repository_metadata:
revision_label = hg_util.get_revision_label( trans, repository, changeset_revision, include_date=False )
@@ -2328,8 +2332,8 @@
# There is no repository_metadata defined for the changeset_revision, so see if it was defined in a previous
# changeset in the changelog.
previous_changeset_revision = \
- suc.get_previous_metadata_changeset_revision( repository, repo, changeset_revision, downloadable=False )
- if previous_changeset_revision != suc.INITIAL_CHANGELOG_HASH:
+ metadata_util.get_previous_metadata_changeset_revision( repository, repo, changeset_revision, downloadable=False )
+ if previous_changeset_revision != hg_util.INITIAL_CHANGELOG_HASH:
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans.app, id, previous_changeset_revision )
if repository_metadata:
revision_label = hg_util.get_revision_label( trans, repository, previous_changeset_revision, include_date=False )
@@ -2390,7 +2394,7 @@
changeset_revision,
repository_dependencies,
repository_metadata )
- heads = suc.get_repository_heads( repo )
+ heads = hg_util.get_repository_heads( repo )
deprecated_repository_dependency_tups = \
repository_dependency_util.get_repository_dependency_tups_from_repository_metadata( trans.app,
repository_metadata,
@@ -2432,7 +2436,7 @@
repository = suc.get_repository_in_tool_shed( trans, id )
changeset_revision = kwd.get( 'changeset_revision', repository.tip( trans.app ) )
metadata = None
- if changeset_revision != suc.INITIAL_CHANGELOG_HASH:
+ if changeset_revision != hg_util.INITIAL_CHANGELOG_HASH:
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans.app, id, changeset_revision )
if repository_metadata:
metadata = repository_metadata.metadata
@@ -2441,11 +2445,11 @@
# in a previous changeset in the changelog.
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
previous_changeset_revision = \
- suc.get_previous_metadata_changeset_revision( repository,
- repo,
- changeset_revision,
- downloadable=False )
- if previous_changeset_revision != suc.INITIAL_CHANGELOG_HASH:
+ metadata_util.get_previous_metadata_changeset_revision( repository,
+ repo,
+ changeset_revision,
+ downloadable=False )
+ if previous_changeset_revision != hg_util.INITIAL_CHANGELOG_HASH:
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans.app,
id,
previous_changeset_revision )
@@ -2602,10 +2606,15 @@
repository = suc.get_repository_by_name_and_owner( trans.app, name, owner )
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
# Get the lower bound changeset revision.
- lower_bound_changeset_revision = suc.get_previous_metadata_changeset_revision( repository, repo, changeset_revision, downloadable=True )
+ lower_bound_changeset_revision = metadata_util.get_previous_metadata_changeset_revision( repository,
+ repo,
+ changeset_revision,
+ downloadable=True )
# Build the list of changeset revision hashes.
changeset_hashes = []
- for changeset in suc.reversed_lower_upper_bounded_changelog( repo, lower_bound_changeset_revision, changeset_revision ):
+ for changeset in hg_util.reversed_lower_upper_bounded_changelog( repo,
+ lower_bound_changeset_revision,
+ changeset_revision ):
changeset_hashes.append( str( repo.changectx( changeset ) ) )
if changeset_hashes:
changeset_hashes_str = ','.join( changeset_hashes )
@@ -3117,7 +3126,7 @@
for diff in patch.diff( repo, node1=ctx_parent.node(), node2=ctx.node(), opts=diffopts ):
if len( diff ) > suc.MAXDIFFSIZE:
diff = util.shrink_string_by_size( diff, suc.MAXDIFFSIZE )
- diffs.append( suc.to_html_string( diff ) )
+ diffs.append( basic_util.to_html_string( diff ) )
modified, added, removed, deleted, unknown, ignored, clean = repo.status( node1=ctx_parent.node(), node2=ctx.node() )
anchors = modified + added + removed + deleted + unknown + ignored + clean
metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, id, ctx_str, metadata_only=True )
@@ -3236,7 +3245,7 @@
status = 'warning'
else:
metadata = None
- is_malicious = suc.changeset_is_malicious( trans.app, id, repository.tip( trans.app ) )
+ is_malicious = metadata_util.is_malicious( trans.app, id, repository.tip( trans.app ) )
if is_malicious:
if trans.app.security_agent.can_push( trans.app, trans.user, repository ):
message += malicious_error_can_push
@@ -3249,7 +3258,7 @@
repository_dependencies,
repository_metadata )
repository_type_select_field = rt_util.build_repository_type_select_field( trans, repository=repository )
- heads = suc.get_repository_heads( repo )
+ heads = hg_util.get_repository_heads( repo )
return trans.fill_template( '/webapps/tool_shed/repository/view_repository.mako',
repo=repo,
heads=heads,
@@ -3320,7 +3329,7 @@
work_dir )
if message:
status = 'error'
- suc.remove_dir( work_dir )
+ basic_util.remove_dir( work_dir )
break
if guid:
tool_lineage = tool_util.get_version_lineage_for_tool( trans, repository_id, repository_metadata, guid )
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/galaxy/webapps/tool_shed/controllers/repository_review.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository_review.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository_review.py
@@ -470,7 +470,7 @@
metadata_revision_hashes = [ metadata_revision.changeset_revision for metadata_revision in repository.metadata_revisions ]
reviewed_revision_hashes = [ review.changeset_revision for review in repository.reviews ]
reviews_dict = odict()
- for changeset in suc.get_reversed_changelog_changesets( repo ):
+ for changeset in hg_util.get_reversed_changelog_changesets( repo ):
ctx = repo.changectx( changeset )
changeset_revision = str( ctx )
if changeset_revision in metadata_revision_hashes or changeset_revision in reviewed_revision_hashes:
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/galaxy/webapps/tool_shed/controllers/upload.py
--- a/lib/galaxy/webapps/tool_shed/controllers/upload.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/upload.py
@@ -10,6 +10,7 @@
from galaxy.datatypes import checkers
import tool_shed.repository_types.util as rt_util
import tool_shed.util.shed_util_common as suc
+from tool_shed.util import basic_util
from tool_shed.util import commit_util
from tool_shed.util import hg_util
from tool_shed.util import metadata_util
@@ -64,9 +65,9 @@
try:
commands.clone( hg_util.get_configured_ui(), repo_url, uploaded_directory )
except Exception, e:
- message = 'Error uploading via mercurial clone: %s' % suc.to_html_string( str( e ) )
+ message = 'Error uploading via mercurial clone: %s' % basic_util.to_html_string( str( e ) )
status = 'error'
- suc.remove_dir( uploaded_directory )
+ basic_util.remove_dir( uploaded_directory )
uploaded_directory = None
elif url:
valid_url = True
@@ -296,7 +297,7 @@
# Reset the tool_data_tables by loading the empty tool_data_table_conf.xml file.
tool_util.reset_tool_data_tables( trans.app )
if uploaded_directory:
- suc.remove_dir( uploaded_directory )
+ basic_util.remove_dir( uploaded_directory )
trans.response.send_redirect( web.url_for( controller='repository',
action='browse_repository',
id=repository_id,
@@ -305,7 +306,7 @@
status=status ) )
else:
if uploaded_directory:
- suc.remove_dir( uploaded_directory )
+ basic_util.remove_dir( uploaded_directory )
status = 'error'
# Reset the tool_data_tables by loading the empty tool_data_table_conf.xml file.
tool_util.reset_tool_data_tables( trans.app )
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/galaxy_install/repository_util.py
--- a/lib/tool_shed/galaxy_install/repository_util.py
+++ b/lib/tool_shed/galaxy_install/repository_util.py
@@ -9,6 +9,7 @@
from galaxy import web
from galaxy.model.orm import or_
import tool_shed.util.shed_util_common as suc
+from tool_shed.util import basic_util
from tool_shed.util import common_util
from tool_shed.util import common_install_util
from tool_shed.util import container_util
@@ -448,7 +449,7 @@
files_dir = relative_install_dir
if shed_config_dict.get( 'tool_path' ):
files_dir = os.path.join( shed_config_dict[ 'tool_path' ], files_dir )
- datatypes_config = suc.get_config_from_disk( suc.DATATYPES_CONFIG_FILENAME, files_dir )
+ datatypes_config = hg_util.get_config_from_disk( suc.DATATYPES_CONFIG_FILENAME, files_dir )
# Load data types required by tools.
converter_path, display_path = \
datatype_util.alter_config_and_load_prorietary_datatypes( trans.app, datatypes_config, files_dir, override=False )
@@ -620,14 +621,14 @@
tool_shed_repository,
trans.install_model.ToolShedRepository.installation_status.INSTALLING_TOOL_DEPENDENCIES )
# Get the tool_dependencies.xml file from the repository.
- tool_dependencies_config = suc.get_config_from_disk( 'tool_dependencies.xml', install_dir )
+ tool_dependencies_config = hg_util.get_config_from_disk( 'tool_dependencies.xml', install_dir )
installed_tool_dependencies = \
common_install_util.install_specified_packages( app=trans.app,
tool_shed_repository=tool_shed_repository,
tool_dependencies_config=tool_dependencies_config,
tool_dependencies=tool_shed_repository.tool_dependencies,
from_tool_migration_manager=False )
- suc.remove_dir( work_dir )
+ basic_util.remove_dir( work_dir )
suc.update_tool_shed_repository_status( trans.app,
tool_shed_repository,
trans.install_model.ToolShedRepository.installation_status.INSTALLED )
@@ -878,7 +879,7 @@
repository,
trans.install_model.ToolShedRepository.installation_status.INSTALLING_TOOL_DEPENDENCIES )
# Get the tool_dependencies.xml file from the repository.
- tool_dependencies_config = suc.get_config_from_disk( 'tool_dependencies.xml', repository.repo_path( trans.app ) )
+ tool_dependencies_config = hg_util.get_config_from_disk( 'tool_dependencies.xml', repository.repo_path( trans.app ) )
installed_tool_dependencies = \
common_install_util.install_specified_packages( app=trans.app,
tool_shed_repository=repository,
@@ -888,7 +889,7 @@
for installed_tool_dependency in installed_tool_dependencies:
if installed_tool_dependency.status in [ trans.install_model.ToolDependency.installation_status.ERROR ]:
repair_dict = add_repair_dict_entry( repository.name, installed_tool_dependency.error_message )
- suc.remove_dir( work_dir )
+ basic_util.remove_dir( work_dir )
suc.update_tool_shed_repository_status( trans.app, repository, trans.install_model.ToolShedRepository.installation_status.INSTALLED )
return repair_dict
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
@@ -405,7 +405,7 @@
package_name=package_name,
package_version=package_version,
tool_dependencies_config=config_to_use )
- suc.remove_file( tmp_filename )
+ self.remove_file( tmp_filename )
else:
message = "Unable to locate required tool shed repository named %s owned by %s with revision %s." % \
( str( required_repository_name ), str( required_repository_owner ), str( default_required_repository_changeset_revision ) )
@@ -429,6 +429,15 @@
print "Error installing tool dependency for required repository: %s" % str( rd_tool_dependency.error_message )
return tool_dependency, proceed_with_install, action_elem_tuples
+ def remove_file( self, file_name ):
+ """Attempt to remove a file from disk."""
+ if file_name:
+ if os.path.exists( file_name ):
+ try:
+ os.remove( file_name )
+ except:
+ pass
+
class SetEnvironment( RecipeTag ):
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/galaxy_install/tool_migration_manager.py
--- a/lib/tool_shed/galaxy_install/tool_migration_manager.py
+++ b/lib/tool_shed/galaxy_install/tool_migration_manager.py
@@ -11,6 +11,7 @@
from galaxy import util
from galaxy.tools import ToolSection
import tool_shed.util.shed_util_common as suc
+from tool_shed.util import basic_util
from tool_shed.util import common_install_util
from tool_shed.util import common_util
from tool_shed.util import datatype_util
@@ -251,13 +252,13 @@
def get_guid( self, repository_clone_url, relative_install_dir, tool_config ):
if self.shed_config_dict.get( 'tool_path' ):
relative_install_dir = os.path.join( self.shed_config_dict[ 'tool_path' ], relative_install_dir )
- tool_config_filename = suc.strip_path( tool_config )
+ tool_config_filename = basic_util.strip_path( tool_config )
for root, dirs, files in os.walk( relative_install_dir ):
if root.find( '.hg' ) < 0 and root.find( 'hgrc' ) < 0:
if '.hg' in dirs:
dirs.remove( '.hg' )
for name in files:
- filename = suc.strip_path( name )
+ filename = basic_util.strip_path( name )
if filename == tool_config_filename:
full_path = str( os.path.abspath( os.path.join( root, name ) ) )
tool = self.toolbox.load_tool( full_path )
@@ -444,7 +445,7 @@
tool_shed_repository,
self.app.install_model.ToolShedRepository.installation_status.INSTALLING_TOOL_DEPENDENCIES )
# Get the tool_dependencies.xml file from disk.
- tool_dependencies_config = suc.get_config_from_disk( 'tool_dependencies.xml', repo_install_dir )
+ tool_dependencies_config = hg_util.get_config_from_disk( 'tool_dependencies.xml', repo_install_dir )
installed_tool_dependencies = \
common_install_util.install_specified_packages( app=self.app,
tool_shed_repository=tool_shed_repository,
@@ -462,7 +463,7 @@
self.app.install_model.context.add( tool_shed_repository )
self.app.install_model.context.flush()
work_dir = tempfile.mkdtemp( prefix="tmp-toolshed-hrc" )
- datatypes_config = suc.get_config_from_disk( suc.DATATYPES_CONFIG_FILENAME, repo_install_dir )
+ datatypes_config = hg_util.get_config_from_disk( suc.DATATYPES_CONFIG_FILENAME, repo_install_dir )
# Load proprietary data types required by tools. The value of override is not important here since the Galaxy server will be started
# after this installation completes.
converter_path, display_path = datatype_util.alter_config_and_load_prorietary_datatypes( self.app, datatypes_config, repo_install_dir, override=False ) #repo_install_dir was relative_install_dir
@@ -481,7 +482,7 @@
if display_path:
# Load proprietary datatype display applications
self.app.datatypes_registry.load_display_applications( installed_repository_dict=repository_dict )
- suc.remove_dir( work_dir )
+ basic_util.remove_dir( work_dir )
def install_repository( self, repository_elem, tool_shed_repository, install_dependencies, is_repository_dependency=False ):
"""Install a single repository, loading contained tools into the tool panel."""
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/grids/repository_grids.py
--- a/lib/tool_shed/grids/repository_grids.py
+++ b/lib/tool_shed/grids/repository_grids.py
@@ -98,7 +98,7 @@
def get_value( self, trans, grid, repository ):
"""Display the current repository heads."""
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
- heads = suc.get_repository_heads( repo )
+ heads = hg_util.get_repository_heads( repo )
multiple_heads = len( heads ) > 1
if multiple_heads:
heads_str = '<font color="red">'
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/grids/util.py
--- a/lib/tool_shed/grids/util.py
+++ b/lib/tool_shed/grids/util.py
@@ -5,6 +5,7 @@
from galaxy.web.form_builder import SelectField
from tool_shed.util import hg_util
+from tool_shed.util import metadata_util
from tool_shed.util import shed_util_common as suc
log = logging.getLogger( __name__ )
@@ -178,8 +179,11 @@
return repository_metadata
return None
except:
- latest_downloadable_revision = suc.get_previous_metadata_changeset_revision( repository, repo, tip_ctx, downloadable=True )
- if latest_downloadable_revision == suc.INITIAL_CHANGELOG_HASH:
+ latest_downloadable_revision = metadata_util.get_previous_metadata_changeset_revision( repository,
+ repo,
+ tip_ctx,
+ downloadable=True )
+ if latest_downloadable_revision == hg_util.INITIAL_CHANGELOG_HASH:
return None
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans.app,
encoded_repository_id,
@@ -225,8 +229,11 @@
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans.app, encoded_repository_id, tip_ctx )
return repository_metadata
except:
- latest_downloadable_revision = suc.get_previous_metadata_changeset_revision( repository, repo, tip_ctx, downloadable=False )
- if latest_downloadable_revision == suc.INITIAL_CHANGELOG_HASH:
+ latest_downloadable_revision = metadata_util.get_previous_metadata_changeset_revision( repository,
+ repo,
+ tip_ctx,
+ downloadable=False )
+ if latest_downloadable_revision == hg_util.INITIAL_CHANGELOG_HASH:
return None
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans.app,
encoded_repository_id,
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/repository_registry.py
--- a/lib/tool_shed/repository_registry.py
+++ b/lib/tool_shed/repository_registry.py
@@ -50,17 +50,35 @@
for rca in repository.categories:
category = rca.category
category_name = str( category.name )
- self.viewable_repositories_and_suites_by_category[ category_name ] += 1
+ if category_name in self.viewable_repositories_and_suites_by_category:
+ self.viewable_repositories_and_suites_by_category[ category_name ] += 1
+ else:
+ self.viewable_repositories_and_suites_by_category[ category_name ] = 1
if is_valid:
- self.viewable_valid_repositories_and_suites_by_category[ category_name ] += 1
+ if category_name in self.viewable_valid_repositories_and_suites_by_category:
+ self.viewable_valid_repositories_and_suites_by_category[ category_name ] += 1
+ else:
+ self.viewable_valid_repositories_and_suites_by_category[ category_name ] = 1
if repository.type == rt_util.REPOSITORY_SUITE_DEFINITION:
- self.viewable_suites_by_category[ category_name ] += 1
+ if category_name in self.viewable_suites_by_category:
+ self.viewable_suites_by_category[ category_name ] += 1
+ else:
+ self.viewable_suites_by_category[ category_name ] = 1
if is_valid:
- self.viewable_valid_suites_by_category[ category_name ] += 1
+ if category_name in self.viewable_valid_suites_by_category:
+ self.viewable_valid_suites_by_category[ category_name ] += 1
+ else:
+ self.viewable_valid_suites_by_category[ category_name ] = 1
if is_level_one_certified:
- self.certified_level_one_viewable_repositories_and_suites_by_category[ category_name ] += 1
+ if category_name in self.certified_level_one_viewable_repositories_and_suites_by_category:
+ self.certified_level_one_viewable_repositories_and_suites_by_category[ category_name ] += 1
+ else:
+ self.certified_level_one_viewable_repositories_and_suites_by_category[ category_name ] = 1
if repository.type == rt_util.REPOSITORY_SUITE_DEFINITION:
- self.certified_level_one_viewable_suites_by_category[ category_name ] += 1
+ if category_name in self.certified_level_one_viewable_suites_by_category:
+ self.certified_level_one_viewable_suites_by_category[ category_name ] += 1
+ else:
+ self.certified_level_one_viewable_suites_by_category[ category_name ] = 1
self.load_repository_and_suite_tuple( repository )
if is_level_one_certified:
self.load_certified_level_one_repository_and_suite_tuple( repository )
@@ -97,7 +115,7 @@
repo = hg_util.get_repo_for_repository( self.app, repository=repository, repo_path=None, create=False )
# Get the latest installable changeset revision since that is all that is currently configured for testing.
latest_installable_changeset_revision = suc.get_latest_downloadable_changeset_revision( self.app, repository, repo )
- if latest_installable_changeset_revision not in [ None, suc.INITIAL_CHANGELOG_HASH ]:
+ if latest_installable_changeset_revision not in [ None, hg_util.INITIAL_CHANGELOG_HASH ]:
encoded_repository_id = self.app.security.encode_id( repository.id )
repository_metadata = suc.get_repository_metadata_by_changeset_revision( self.app,
encoded_repository_id,
@@ -168,7 +186,7 @@
name = str( repository.name )
owner = str( repository.user.username )
tip_changeset_hash = repository.tip( self.app )
- if tip_changeset_hash != suc.INITIAL_CHANGELOG_HASH:
+ if tip_changeset_hash != hg_util.INITIAL_CHANGELOG_HASH:
certified_level_one_tuple = ( name, owner, tip_changeset_hash )
if repository.type == rt_util.REPOSITORY_SUITE_DEFINITION:
if certified_level_one_tuple not in self.certified_level_one_suite_tuples:
@@ -257,17 +275,41 @@
for rca in repository.categories:
category = rca.category
category_name = str( category.name )
- self.viewable_repositories_and_suites_by_category[ category_name ] -= 1
+ if category_name in self.viewable_repositories_and_suites_by_category:
+ if self.viewable_repositories_and_suites_by_category[ category_name ] > 0:
+ self.viewable_repositories_and_suites_by_category[ category_name ] -= 1
+ else:
+ self.viewable_repositories_and_suites_by_category[ category_name ] = 0
if is_valid:
- self.viewable_valid_repositories_and_suites_by_category[ category_name ] -= 1
+ if category_name in self.viewable_valid_repositories_and_suites_by_category:
+ if self.viewable_valid_repositories_and_suites_by_category[ category_name ] > 0:
+ self.viewable_valid_repositories_and_suites_by_category[ category_name ] -= 1
+ else:
+ self.viewable_valid_repositories_and_suites_by_category[ category_name ] = 0
if repository.type == rt_util.REPOSITORY_SUITE_DEFINITION:
- self.viewable_suites_by_category[ category_name ] -= 1
+ if category_name in self.viewable_suites_by_category:
+ if self.viewable_suites_by_category[ category_name ] > 0:
+ self.viewable_suites_by_category[ category_name ] -= 1
+ else:
+ self.viewable_suites_by_category[ category_name ] = 0
if is_valid:
- self.viewable_valid_suites_by_category[ category_name ] -= 1
+ if category_name in self.viewable_valid_suites_by_category:
+ if self.viewable_valid_suites_by_category[ category_name ] > 0:
+ self.viewable_valid_suites_by_category[ category_name ] -= 1
+ else:
+ self.viewable_valid_suites_by_category[ category_name ] = 0
if is_level_one_certified:
- self.certified_level_one_viewable_repositories_and_suites_by_category[ category_name ] -= 1
+ if category_name in self.certified_level_one_viewable_repositories_and_suites_by_category:
+ if self.certified_level_one_viewable_repositories_and_suites_by_category[ category_name ] > 0:
+ self.certified_level_one_viewable_repositories_and_suites_by_category[ category_name ] -= 1
+ else:
+ self.certified_level_one_viewable_repositories_and_suites_by_category[ category_name ] = 0
if repository.type == rt_util.REPOSITORY_SUITE_DEFINITION:
- self.certified_level_one_viewable_suites_by_category[ category_name ] -= 1
+ if category_name in self.certified_level_one_viewable_suites_by_category:
+ if self.certified_level_one_viewable_suites_by_category[ category_name ] > 0:
+ self.certified_level_one_viewable_suites_by_category[ category_name ] -= 1
+ else:
+ self.certified_level_one_viewable_suites_by_category[ category_name ] = 0
self.unload_repository_and_suite_tuple( repository )
if is_level_one_certified:
self.unload_certified_level_one_repository_and_suite_tuple( repository )
@@ -286,7 +328,7 @@
name = str( repository.name )
owner = str( repository.user.username )
tip_changeset_hash = repository.tip( self.app )
- if tip_changeset_hash != suc.INITIAL_CHANGELOG_HASH:
+ if tip_changeset_hash != hg_util.INITIAL_CHANGELOG_HASH:
certified_level_one_tuple = ( name, owner, tip_changeset_hash )
if repository.type == rt_util.REPOSITORY_SUITE_DEFINITION:
if certified_level_one_tuple in self.certified_level_one_suite_tuples:
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/repository_types/repository_suite_definition.py
--- a/lib/tool_shed/repository_types/repository_suite_definition.py
+++ b/lib/tool_shed/repository_types/repository_suite_definition.py
@@ -1,7 +1,7 @@
import logging
from tool_shed.repository_types.metadata import TipOnly
import tool_shed.repository_types.util as rt_util
-import tool_shed.util.shed_util_common as suc
+from tool_shed.util import basic_util
from galaxy import eggs
eggs.require( 'mercurial' )
@@ -35,7 +35,7 @@
# is named repository_dependencies.xml.
files_changed_in_changeset = ctx.files()
for file_path in files_changed_in_changeset:
- file_name = suc.strip_path( file_path )
+ file_name = basic_util.strip_path( file_path )
if file_name not in self.valid_file_names:
return False
return True
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/repository_types/tool_dependency_definition.py
--- a/lib/tool_shed/repository_types/tool_dependency_definition.py
+++ b/lib/tool_shed/repository_types/tool_dependency_definition.py
@@ -1,7 +1,7 @@
import logging
from tool_shed.repository_types.metadata import TipOnly
import tool_shed.repository_types.util as rt_util
-import tool_shed.util.shed_util_common as suc
+from tool_shed.util import basic_util
from galaxy import eggs
eggs.require( 'mercurial' )
@@ -34,7 +34,7 @@
# Inspect all files in the changeset (in sorted order) to make sure there is only one and it is named tool_dependencies.xml.
files_changed_in_changeset = ctx.files()
for file_path in files_changed_in_changeset:
- file_name = suc.strip_path( file_path )
+ file_name = basic_util.strip_path( file_path )
if file_name not in self.valid_file_names:
return False
return True
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/scripts/api/common.py
--- a/lib/tool_shed/scripts/api/common.py
+++ b/lib/tool_shed/scripts/api/common.py
@@ -8,11 +8,8 @@
new_path.extend( sys.path[ 1: ] )
sys.path = new_path
-import tool_shed.util.shed_util_common as suc
from tool_shed.util import common_util
-
-from galaxy import eggs
-import pkg_resources
+from tool_shed.util import hg_util
def delete( api_key, url, data, return_formatted=True ):
"""
@@ -115,7 +112,7 @@
return None, error_message
if len( changeset_revisions ) >= 1:
return changeset_revisions[ -1 ], error_message
- return suc.INITIAL_CHANGELOG_HASH, error_message
+ return hg_util.INITIAL_CHANGELOG_HASH, error_message
def get_repository_dict( url, repository_dict ):
"""
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/scripts/api/export.py
--- a/lib/tool_shed/scripts/api/export.py
+++ b/lib/tool_shed/scripts/api/export.py
@@ -17,17 +17,6 @@
CHUNK_SIZE = 2**20 # 1Mb
-def get_file_type_str( changeset_revision, file_type ):
- if file_type == 'zip':
- file_type_str = '%s.zip' % changeset_revision
- elif file_type == 'bz2':
- file_type_str = '%s.tar.bz2' % changeset_revision
- elif file_type == 'gz':
- file_type_str = '%s.tar.gz' % changeset_revision
- else:
- file_type_str = ''
- return file_type_str
-
def string_as_bool( string ):
if str( string ).lower() in ( 'true', 'yes', 'on' ):
return True
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/scripts/api/get_filtered_repository_revisions.py
--- a/lib/tool_shed/scripts/api/get_filtered_repository_revisions.py
+++ b/lib/tool_shed/scripts/api/get_filtered_repository_revisions.py
@@ -34,7 +34,7 @@
from galaxy.util import asbool
from galaxy.util.json import from_json_string
-import tool_shed.util.shed_util_common as suc
+from tool_shed.util import hg_util
def main( options ):
base_tool_shed_url = options.tool_shed_url.rstrip( '/' )
@@ -71,12 +71,12 @@
repository_dicts.append( baseline_repository_dict )
else:
# Don't test empty repositories.
- changeset_revision = baseline_repository_dict.get( 'changeset_revision', suc.INITIAL_CHANGELOG_HASH )
- if changeset_revision != suc.INITIAL_CHANGELOG_HASH:
+ changeset_revision = baseline_repository_dict.get( 'changeset_revision', hg_util.INITIAL_CHANGELOG_HASH )
+ if changeset_revision != hg_util.INITIAL_CHANGELOG_HASH:
# Merge the dictionary returned from /api/repository_revisions with the detailed repository_dict and
# append it to the list of repository_dicts to install and test.
if latest_revision_only:
- latest_revision = repository_dict.get( 'latest_revision', suc.INITIAL_CHANGELOG_HASH )
+ latest_revision = repository_dict.get( 'latest_revision', hg_util.INITIAL_CHANGELOG_HASH )
if changeset_revision == latest_revision:
repository_dicts.append( dict( repository_dict.items() + baseline_repository_dict.items() ) )
else:
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/util/basic_util.py
--- /dev/null
+++ b/lib/tool_shed/util/basic_util.py
@@ -0,0 +1,57 @@
+import logging
+import os
+
+from galaxy.util import unicodify
+
+from galaxy import eggs
+
+eggs.require( 'markupsafe' )
+import markupsafe
+
+log = logging.getLogger( __name__ )
+
+MAX_DISPLAY_SIZE = 32768
+
+def remove_dir( dir ):
+ """Attempt to remove a directory from disk."""
+ if dir:
+ if os.path.exists( dir ):
+ try:
+ shutil.rmtree( dir )
+ except:
+ pass
+
+def size_string( raw_text, size=MAX_DISPLAY_SIZE ):
+ """Return a subset of a string (up to MAX_DISPLAY_SIZE) translated to a safe string for display in a browser."""
+ if raw_text and len( raw_text ) >= size:
+ large_str = '\nFile contents truncated because file size is larger than maximum viewing size of %s\n' % util.nice_size( size )
+ raw_text = '%s%s' % ( raw_text[ 0:size ], large_str )
+ return raw_text or ''
+
+def stringify( list ):
+ if list:
+ return ','.join( list )
+ return ''
+
+def strip_path( fpath ):
+ """Attempt to strip the path from a file name."""
+ if not fpath:
+ return fpath
+ try:
+ file_path, file_name = os.path.split( fpath )
+ except:
+ file_name = fpath
+ return file_name
+
+def to_html_string( text ):
+ """Translates the characters in text to an html string"""
+ if text:
+ try:
+ text = unicodify( text )
+ except UnicodeDecodeError, e:
+ return "Error decoding string: %s" % str( e )
+ text = unicode( markupsafe.escape( text ) )
+ text = text.replace( '\n', '<br/>' )
+ text = text.replace( ' ', ' ' )
+ text = text.replace( ' ', ' ' )
+ return text
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/util/commit_util.py
--- a/lib/tool_shed/util/commit_util.py
+++ b/lib/tool_shed/util/commit_util.py
@@ -345,7 +345,7 @@
if repository:
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
lastest_installable_changeset_revision = suc.get_latest_downloadable_changeset_revision( trans.app, repository, repo )
- if lastest_installable_changeset_revision != suc.INITIAL_CHANGELOG_HASH:
+ if lastest_installable_changeset_revision != hg_util.INITIAL_CHANGELOG_HASH:
elem.attrib[ 'changeset_revision' ] = lastest_installable_changeset_revision
revised = True
else:
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/util/datatype_util.py
--- a/lib/tool_shed/util/datatype_util.py
+++ b/lib/tool_shed/util/datatype_util.py
@@ -3,6 +3,8 @@
import tempfile
from galaxy import eggs
from galaxy.util import asbool
+from tool_shed.util import basic_util
+from tool_shed.util import hg_util
from tool_shed.util import tool_util
from tool_shed.util import xml_util
import tool_shed.util.shed_util_common as suc
@@ -127,7 +129,7 @@
for converter in elem.findall( 'converter' ):
converter_config = converter.get( 'file', None )
if converter_config:
- converter_config_file_name = suc.strip_path( converter_config )
+ converter_config_file_name = basic_util.strip_path( converter_config )
for root, dirs, files in os.walk( relative_install_dir ):
if root.find( '.hg' ) < 0:
for name in files:
@@ -144,7 +146,7 @@
for display_app in elem.findall( 'display' ):
display_config = display_app.get( 'file', None )
if display_config:
- display_config_file_name = suc.strip_path( display_config )
+ display_config_file_name = basic_util.strip_path( display_config )
for root, dirs, files in os.walk( relative_install_dir ):
if root.find( '.hg' ) < 0:
for name in files:
@@ -166,7 +168,7 @@
# Load proprietary datatypes and return information needed for loading proprietary datatypes converters and display applications later.
metadata = repository.metadata
repository_dict = None
- datatypes_config = suc.get_config_from_disk( suc.DATATYPES_CONFIG_FILENAME, relative_install_dir )
+ datatypes_config = hg_util.get_config_from_disk( suc.DATATYPES_CONFIG_FILENAME, relative_install_dir )
if datatypes_config:
converter_path, display_path = alter_config_and_load_prorietary_datatypes( app, datatypes_config, relative_install_dir, deactivate=deactivate )
if converter_path or display_path:
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/util/export_util.py
--- a/lib/tool_shed/util/export_util.py
+++ b/lib/tool_shed/util/export_util.py
@@ -11,6 +11,7 @@
from galaxy import eggs
from galaxy import web
from galaxy.util.odict import odict
+from tool_shed.util import basic_util
from tool_shed.util import commit_util
from tool_shed.util import common_install_util
from tool_shed.util import common_util
@@ -101,7 +102,7 @@
attributes, sub_elements = get_repository_attributes_and_sub_elements( ordered_repository, archive_name )
elem = xml_util.create_element( 'repository', attributes=attributes, sub_elements=sub_elements )
exported_repository_registry.exported_repository_elems.append( elem )
- suc.remove_dir( work_dir )
+ basic_util.remove_dir( work_dir )
# Keep information about the export in a file name export_info.xml in the archive.
sub_elements = generate_export_elem( tool_shed_url, repository, changeset_revision, export_repository_dependencies, api )
export_elem = xml_util.create_element( 'export_info', attributes=None, sub_elements=sub_elements )
@@ -127,7 +128,7 @@
return repositories_archive, error_messages
def generate_repository_archive( trans, work_dir, tool_shed_url, repository, changeset_revision, file_type ):
- file_type_str = suc.get_file_type_str( changeset_revision, file_type )
+ file_type_str = get_file_type_str( changeset_revision, file_type )
file_name = '%s-%s' % ( repository.name, file_type_str )
return_code, error_message = archive_repository_revision( trans, ui, repository, work_dir, changeset_revision )
if return_code:
@@ -172,7 +173,7 @@
def generate_repository_archive_filename( tool_shed_url, name, owner, changeset_revision, file_type,
export_repository_dependencies=False, use_tmp_archive_dir=False ):
tool_shed = remove_protocol_from_tool_shed_url( tool_shed_url )
- file_type_str = suc.get_file_type_str( changeset_revision, file_type )
+ file_type_str = get_file_type_str( changeset_revision, file_type )
if export_repository_dependencies:
repositories_archive_filename = '%s_%s_%s_%s_%s' % ( CAPSULE_WITH_DEPENDENCIES_FILENAME, tool_shed, name, owner, file_type_str )
else:
@@ -210,6 +211,17 @@
return repository, repository_metadata.changeset_revision
return None, None
+def get_file_type_str( changeset_revision, file_type ):
+ if file_type == 'zip':
+ file_type_str = '%s.zip' % changeset_revision
+ elif file_type == 'bz2':
+ file_type_str = '%s.tar.bz2' % changeset_revision
+ elif file_type == 'gz':
+ file_type_str = '%s.tar.gz' % changeset_revision
+ else:
+ file_type_str = ''
+ return file_type_str
+
def get_repo_info_dict_for_import( encoded_repository_id, encoded_repository_ids, repo_info_dicts ):
"""
The received encoded_repository_ids and repo_info_dicts are lists that contain associated elements at each
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/util/hg_util.py
--- a/lib/tool_shed/util/hg_util.py
+++ b/lib/tool_shed/util/hg_util.py
@@ -1,4 +1,6 @@
import logging
+import os
+
from datetime import datetime
from time import gmtime
from time import strftime
@@ -12,8 +14,12 @@
from mercurial import hg
from mercurial import ui
+from tool_shed.util import basic_util
+
log = logging.getLogger( __name__ )
+INITIAL_CHANGELOG_HASH = '000000000000'
+
def clone_repository( repository_clone_url, repository_file_dir, ctx_rev ):
"""
Clone the repository up to the specified changeset_revision. No subsequent revisions will be
@@ -32,6 +38,22 @@
log.debug( error_message )
return False, error_message
+def copy_file_from_manifest( repo, ctx, filename, dir ):
+ """
+ Copy the latest version of the file named filename from the repository manifest to the directory
+ to which dir refers.
+ """
+ for changeset in reversed_upper_bounded_changelog( repo, ctx ):
+ changeset_ctx = repo.changectx( changeset )
+ fctx = get_file_context_from_ctx( changeset_ctx, filename )
+ if fctx and fctx not in [ 'DELETED' ]:
+ file_path = os.path.join( dir, filename )
+ fh = open( file_path, 'wb' )
+ fh.write( fctx.data() )
+ fh.close()
+ return file_path
+ return None
+
def get_changectx_for_changeset( repo, changeset_revision, **kwd ):
"""Retrieve a specified changectx from a repository."""
for changeset in repo.changelog:
@@ -40,6 +62,25 @@
return ctx
return None
+def get_config( config_file, repo, ctx, dir ):
+ """Return the latest version of config_filename from the repository manifest."""
+ config_file = basic_util.strip_path( config_file )
+ for changeset in reversed_upper_bounded_changelog( repo, ctx ):
+ changeset_ctx = repo.changectx( changeset )
+ for ctx_file in changeset_ctx.files():
+ ctx_file_name = basic_util.strip_path( ctx_file )
+ if ctx_file_name == config_file:
+ return get_named_tmpfile_from_ctx( changeset_ctx, ctx_file, dir )
+ return None
+
+def get_config_from_disk( config_file, relative_install_dir ):
+ for root, dirs, files in os.walk( relative_install_dir ):
+ if root.find( '.hg' ) < 0:
+ for name in files:
+ if name == config_file:
+ return os.path.abspath( os.path.join( root, name ) )
+ return None
+
def get_configured_ui():
"""Configure any desired ui settings."""
_ui = ui.ui()
@@ -50,6 +91,44 @@
_ui.setconfig( 'ui', 'quiet', True )
return _ui
+def get_ctx_file_path_from_manifest( filename, repo, changeset_revision ):
+ """
+ Get the ctx file path for the latest revision of filename from the repository manifest up
+ to the value of changeset_revision.
+ """
+ stripped_filename = basic_util.strip_path( filename )
+ for changeset in reversed_upper_bounded_changelog( repo, changeset_revision ):
+ manifest_changeset_revision = str( repo.changectx( changeset ) )
+ manifest_ctx = repo.changectx( changeset )
+ for ctx_file in manifest_ctx.files():
+ ctx_file_name = basic_util.strip_path( ctx_file )
+ if ctx_file_name == stripped_filename:
+ return manifest_ctx, ctx_file
+ return None, None
+
+def get_file_context_from_ctx( ctx, filename ):
+ """Return the mercurial file context for a specified file."""
+ # We have to be careful in determining if we found the correct file because multiple files with
+ # the same name may be in different directories within ctx if the files were moved within the change
+ # set. For example, in the following ctx.files() list, the former may have been moved to the latter:
+ # ['tmap_wrapper_0.0.19/tool_data_table_conf.xml.sample', 'tmap_wrapper_0.3.3/tool_data_table_conf.xml.sample'].
+ # Another scenario is that the file has been deleted.
+ deleted = False
+ filename = basic_util.strip_path( filename )
+ for ctx_file in ctx.files():
+ ctx_file_name = basic_util.strip_path( ctx_file )
+ if filename == ctx_file_name:
+ try:
+ # If the file was moved, its destination will be returned here.
+ fctx = ctx[ ctx_file ]
+ return fctx
+ except LookupError, e:
+ # Set deleted for now, and continue looking in case the file was moved instead of deleted.
+ deleted = True
+ if deleted:
+ return 'DELETED'
+ return None
+
def get_mercurial_default_options_dict( command, command_table=None, **kwd ):
'''Borrowed from repoman - get default parameters for a mercurial command.'''
if command_table is None:
@@ -62,6 +141,32 @@
default_options_dict[ option ] = kwd[ option ]
return default_options_dict
+def get_named_tmpfile_from_ctx( ctx, filename, dir ):
+ """
+ Return a named temporary file created from a specified file with a given name included in a repository
+ changeset revision.
+ """
+ filename = basic_util.strip_path( filename )
+ for ctx_file in ctx.files():
+ ctx_file_name = basic_util.strip_path( ctx_file )
+ if filename == ctx_file_name:
+ try:
+ # If the file was moved, its destination file contents will be returned here.
+ fctx = ctx[ ctx_file ]
+ except LookupError, e:
+ # Continue looking in case the file was moved.
+ fctx = None
+ continue
+ if fctx:
+ fh = tempfile.NamedTemporaryFile( 'wb', prefix="tmp-toolshed-gntfc", dir=dir )
+ tmp_filename = fh.name
+ fh.close()
+ fh = open( tmp_filename, 'wb' )
+ fh.write( fctx.data() )
+ fh.close()
+ return tmp_filename
+ return None
+
def get_readable_ctx_date( ctx ):
"""Convert the date of the changeset (the received ctx) to a human-readable date."""
t, tz = ctx.date()
@@ -75,6 +180,18 @@
if repo_path is not None:
return hg.repository( get_configured_ui(), repo_path, create=create )
+def get_repository_heads( repo ):
+ """Return current repository heads, which are changesets with no child changesets."""
+ heads = [ repo[ h ] for h in repo.heads( None ) ]
+ return heads
+
+def get_reversed_changelog_changesets( repo ):
+ """Return a list of changesets in reverse order from that provided by the repository manifest."""
+ reversed_changelog = []
+ for changeset in repo.changelog:
+ reversed_changelog.insert( 0, changeset )
+ return reversed_changelog
+
def get_revision_label( trans, repository, changeset_revision, include_date=True, include_hash=True ):
"""
Return a string consisting of the human read-able changeset rev and the changeset revision string
@@ -146,6 +263,38 @@
label = "-1:%s" % changeset_revision
return rev, label
+def reversed_lower_upper_bounded_changelog( repo, excluded_lower_bounds_changeset_revision, included_upper_bounds_changeset_revision ):
+ """
+ Return a reversed list of changesets in the repository changelog after the excluded_lower_bounds_changeset_revision,
+ but up to and including the included_upper_bounds_changeset_revision. The value of excluded_lower_bounds_changeset_revision
+ will be the value of INITIAL_CHANGELOG_HASH if no valid changesets exist before included_upper_bounds_changeset_revision.
+ """
+ # To set excluded_lower_bounds_changeset_revision, calling methods should do the following, where the value
+ # of changeset_revision is a downloadable changeset_revision.
+ # excluded_lower_bounds_changeset_revision = \
+ # metadata_util.get_previous_metadata_changeset_revision( repository, repo, changeset_revision, downloadable=? )
+ if excluded_lower_bounds_changeset_revision == INITIAL_CHANGELOG_HASH:
+ appending_started = True
+ else:
+ appending_started = False
+ reversed_changelog = []
+ for changeset in repo.changelog:
+ changeset_hash = str( repo.changectx( changeset ) )
+ if appending_started:
+ reversed_changelog.insert( 0, changeset )
+ if changeset_hash == excluded_lower_bounds_changeset_revision and not appending_started:
+ appending_started = True
+ if changeset_hash == included_upper_bounds_changeset_revision:
+ break
+ return reversed_changelog
+
+def reversed_upper_bounded_changelog( repo, included_upper_bounds_changeset_revision ):
+ """
+ Return a reversed list of changesets in the repository changelog up to and including the
+ included_upper_bounds_changeset_revision.
+ """
+ return reversed_lower_upper_bounded_changelog( repo, INITIAL_CHANGELOG_HASH, included_upper_bounds_changeset_revision )
+
def update_repository( repo, ctx_rev=None ):
"""
Update the cloned repository to changeset_revision. It is critical that the installed repository is updated to the desired
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/util/metadata_util.py
--- a/lib/tool_shed/util/metadata_util.py
+++ b/lib/tool_shed/util/metadata_util.py
@@ -13,6 +13,7 @@
import tool_shed.util.shed_util_common as suc
from tool_shed.repository_types.metadata import TipOnly
+from tool_shed.util import basic_util
from tool_shed.util import common_util
from tool_shed.util import common_install_util
from tool_shed.util import container_util
@@ -526,7 +527,7 @@
tool_config = sub_elem.attrib[ 'file' ]
target_datatype = sub_elem.attrib[ 'target_datatype' ]
# Parse the tool_config to get the guid.
- tool_config_path = suc.get_config_from_disk( tool_config, repository_files_dir )
+ tool_config_path = hg_util.get_config_from_disk( tool_config, repository_files_dir )
full_path = os.path.abspath( tool_config_path )
tool, valid, error_message = tool_util.load_tool_from_config( app, app.security.encode_id( repository.id ), full_path )
if tool is None:
@@ -632,7 +633,7 @@
app.config.tool_data_path = work_dir #FIXME: Thread safe?
app.config.tool_data_table_config_path = work_dir
# Handle proprietary datatypes, if any.
- datatypes_config = suc.get_config_from_disk( suc.DATATYPES_CONFIG_FILENAME, files_dir )
+ datatypes_config = hg_util.get_config_from_disk( suc.DATATYPES_CONFIG_FILENAME, files_dir )
if datatypes_config:
metadata_dict = generate_datatypes_metadata( app, repository, repository_clone_url, files_dir, datatypes_config, metadata_dict )
# Get the relative path to all sample files included in the repository for storage in the repository's metadata.
@@ -749,14 +750,14 @@
metadata_dict = generate_data_manager_metadata( app,
repository,
files_dir,
- suc.get_config_from_disk( suc.REPOSITORY_DATA_MANAGER_CONFIG_FILENAME, files_dir ),
+ hg_util.get_config_from_disk( suc.REPOSITORY_DATA_MANAGER_CONFIG_FILENAME, files_dir ),
metadata_dict,
shed_config_dict=shed_config_dict )
if readme_files:
metadata_dict[ 'readme_files' ] = readme_files
# This step must be done after metadata for tools has been defined.
- tool_dependencies_config = suc.get_config_from_disk( 'tool_dependencies.xml', files_dir )
+ tool_dependencies_config = hg_util.get_config_from_disk( 'tool_dependencies.xml', files_dir )
if tool_dependencies_config:
metadata_dict, error_message = generate_tool_dependency_metadata( app,
repository,
@@ -772,7 +773,7 @@
# Reset the value of the app's tool_data_path and tool_data_table_config_path to their respective original values.
app.config.tool_data_path = original_tool_data_path
app.config.tool_data_table_config_path = original_tool_data_table_config_path
- suc.remove_dir( work_dir )
+ basic_util.remove_dir( work_dir )
return metadata_dict, invalid_file_tups
def generate_package_dependency_metadata( app, elem, valid_tool_dependencies_dict, invalid_tool_dependencies_dict ):
@@ -1006,7 +1007,7 @@
outputs = []
for output in ttb.outputs:
name, file_name, extra = output
- outputs.append( ( name, suc.strip_path( file_name ) if file_name else None ) )
+ outputs.append( ( name, basic_util.strip_path( file_name ) if file_name else None ) )
if file_name not in required_files and file_name is not None:
required_files.append( file_name )
test_dict = dict( name=str( ttb.name ),
@@ -1078,6 +1079,29 @@
# The tool did not change through all of the changeset revisions.
return old_id
+def get_previous_metadata_changeset_revision( repository, repo, before_changeset_revision, downloadable=True ):
+ """
+ Return the changeset_revision in the repository changelog that has associated metadata prior to
+ the changeset to which before_changeset_revision refers. If there isn't one, return the hash value
+ of an empty repository changelog, hg_util.INITIAL_CHANGELOG_HASH.
+ """
+ changeset_revisions = suc.get_ordered_metadata_changeset_revisions( repository, repo, downloadable=downloadable )
+ if len( changeset_revisions ) == 1:
+ changeset_revision = changeset_revisions[ 0 ]
+ if changeset_revision == before_changeset_revision:
+ return hg_util.INITIAL_CHANGELOG_HASH
+ return changeset_revision
+ previous_changeset_revision = None
+ for changeset_revision in changeset_revisions:
+ if changeset_revision == before_changeset_revision:
+ if previous_changeset_revision:
+ return previous_changeset_revision
+ else:
+ # Return the hash value of an empty repository changelog - note that this will not be a valid changeset revision.
+ return hg_util.INITIAL_CHANGELOG_HASH
+ else:
+ previous_changeset_revision = changeset_revision
+
def get_relative_path_to_repository_file( root, name, relative_install_dir, work_dir, shed_config_dict, resetting_all_metadata_on_repository ):
if resetting_all_metadata_on_repository:
full_path_to_file = os.path.join( root, name )
@@ -1344,6 +1368,13 @@
return True
return False
+def is_malicious( app, id, changeset_revision, **kwd ):
+ """Check the malicious flag in repository metadata for a specified change set revision."""
+ repository_metadata = suc.get_repository_metadata_by_changeset_revision( app, id, changeset_revision )
+ if repository_metadata:
+ return repository_metadata.malicious
+ return False
+
def new_datatypes_metadata_required( trans, repository_metadata, metadata_dict ):
"""
Compare the last saved metadata for each datatype in the repository with the new metadata in metadata_dict to determine if a new
@@ -1821,7 +1852,7 @@
changeset_revisions.append( metadata_changeset_revision )
ancestor_changeset_revision = None
ancestor_metadata_dict = None
- suc.remove_dir( work_dir )
+ basic_util.remove_dir( work_dir )
# Delete all repository_metadata records for this repository that do not have a changeset_revision
# value in changeset_revisions.
clean_repository_metadata( trans, id, changeset_revisions )
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/util/readme_util.py
--- a/lib/tool_shed/util/readme_util.py
+++ b/lib/tool_shed/util/readme_util.py
@@ -10,6 +10,7 @@
from galaxy.util import unicodify
import tool_shed.util.shed_util_common as suc
+from tool_shed.util import basic_util
from tool_shed.util import common_util
from tool_shed.util import hg_util
@@ -46,7 +47,7 @@
log.exception( "Error reading README file '%s' from disk: %s" % ( str( relative_path_to_readme_file ), str( e ) ) )
text = None
if text:
- text_of_reasonable_length = suc.size_string( text )
+ text_of_reasonable_length = basic_util.size_string( text )
if text_of_reasonable_length.find( '.. image:: ' ) >= 0:
# Handle image display for README files that are contained in repositories in the tool shed or installed into Galaxy.
lock = threading.Lock()
@@ -69,17 +70,17 @@
host_url=web.url_for( '/', qualified=True ) )
text_of_reasonable_length = unicodify( text_of_reasonable_length )
else:
- text_of_reasonable_length = suc.to_html_string( text_of_reasonable_length )
+ text_of_reasonable_length = basic_util.to_html_string( text_of_reasonable_length )
readme_files_dict[ readme_file_name ] = text_of_reasonable_length
else:
# We must be in the tool shed and have an old changeset_revision, so we need to retrieve the file contents from the repository manifest.
ctx = hg_util.get_changectx_for_changeset( repo, changeset_revision )
if ctx:
- fctx = suc.get_file_context_from_ctx( ctx, readme_file_name )
+ fctx = hg_util.get_file_context_from_ctx( ctx, readme_file_name )
if fctx and fctx not in [ 'DELETED' ]:
try:
text = unicodify( fctx.data() )
- readme_files_dict[ readme_file_name ] = suc.size_string( text )
+ readme_files_dict[ readme_file_name ] = basic_util.size_string( text )
except Exception, e:
log.exception( "Error reading README file '%s' from repository manifest: %s" % \
( str( relative_path_to_readme_file ), str( e ) ) )
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/util/review_util.py
--- a/lib/tool_shed/util/review_util.py
+++ b/lib/tool_shed/util/review_util.py
@@ -55,7 +55,7 @@
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
reviewed_revision_hashes = [ review.changeset_revision for review in repository.reviews ]
previous_reviews_dict = odict()
- for changeset in suc.reversed_upper_bounded_changelog( repo, changeset_revision ):
+ for changeset in hg_util.reversed_upper_bounded_changelog( repo, changeset_revision ):
previous_changeset_revision = str( repo.changectx( changeset ) )
if previous_changeset_revision in reviewed_revision_hashes:
previous_rev, previous_changeset_revision_label = hg_util.get_rev_label_from_changeset_revision( repo, previous_changeset_revision )
@@ -89,7 +89,7 @@
"""Determine if a repository has a changeset revision review prior to the received changeset revision."""
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
reviewed_revision_hashes = [ review.changeset_revision for review in repository.reviews ]
- for changeset in suc.reversed_upper_bounded_changelog( repo, changeset_revision ):
+ for changeset in hg_util.reversed_upper_bounded_changelog( repo, changeset_revision ):
previous_changeset_revision = str( repo.changectx( changeset ) )
if previous_changeset_revision in reviewed_revision_hashes:
return True
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -7,13 +7,13 @@
from galaxy import util
from galaxy.util import asbool
from galaxy.util import json
-from galaxy.util import unicodify
from galaxy.web import url_for
from galaxy.web.form_builder import SelectField
from galaxy.datatypes import checkers
from galaxy.model.orm import and_
from galaxy.model.orm import or_
import sqlalchemy.orm.exc
+from tool_shed.util import basic_util
from tool_shed.util import common_util
from tool_shed.util import encoding_util
from tool_shed.util import hg_util
@@ -22,18 +22,11 @@
from xml.etree import ElementTree as XmlET
from urllib2 import HTTPError
-from galaxy import eggs
-
-eggs.require( 'markupsafe' )
-import markupsafe
-
log = logging.getLogger( __name__ )
CHUNK_SIZE = 2**20 # 1Mb
-INITIAL_CHANGELOG_HASH = '000000000000'
MAX_CONTENT_SIZE = 1048576
MAXDIFFSIZE = 8000
-MAX_DISPLAY_SIZE = 32768
DATATYPES_CONFIG_FILENAME = 'datatypes_conf.xml'
REPOSITORY_DATA_MANAGER_CONFIG_FILENAME = 'data_manager_conf.xml'
@@ -124,13 +117,6 @@
tool_dependencies_select_field.add_option( option_label, option_value )
return tool_dependencies_select_field
-def changeset_is_malicious( app, id, changeset_revision, **kwd ):
- """Check the malicious flag in repository metadata for a specified change set"""
- repository_metadata = get_repository_metadata_by_changeset_revision( app, id, changeset_revision )
- if repository_metadata:
- return repository_metadata.malicious
- return False
-
def check_or_update_tool_shed_status_for_installed_repository( trans, repository ):
updated = False
tool_shed_status_dict = get_tool_shed_status_for_installed_repository( trans.app, repository )
@@ -157,19 +143,6 @@
shutil.move( filename, os.path.abspath( config_filename ) )
os.chmod( config_filename, 0644 )
-def copy_file_from_manifest( repo, ctx, filename, dir ):
- """Copy the latest version of the file named filename from the repository manifest to the directory to which dir refers."""
- for changeset in reversed_upper_bounded_changelog( repo, ctx ):
- changeset_ctx = repo.changectx( changeset )
- fctx = get_file_context_from_ctx( changeset_ctx, filename )
- if fctx and fctx not in [ 'DELETED' ]:
- file_path = os.path.join( dir, filename )
- fh = open( file_path, 'wb' )
- fh.write( fctx.data() )
- fh.close()
- return file_path
- return None
-
def create_or_update_tool_shed_repository( app, name, description, installed_changeset_revision, ctx_rev, repository_clone_url,
metadata_dict, status, current_changeset_revision=None, owner='', dist_to_shed=False ):
"""
@@ -337,7 +310,7 @@
for tool_dict in metadata[ 'tools' ]:
guid = tool_dict[ 'guid' ]
tool_config = tool_dict[ 'tool_config' ]
- file_name = strip_path( tool_config )
+ file_name = basic_util.strip_path( tool_config )
guids_and_configs[ guid ] = file_name
# Parse the shed_tool_conf file in which all of this repository's tools are defined and generate the tool_panel_dict.
tree, error_message = xml_util.parse_xml( shed_tool_conf )
@@ -389,7 +362,7 @@
def get_absolute_path_to_file_in_repository( repo_files_dir, file_name ):
"""Return the absolute path to a specified disk file contained in a repository."""
- stripped_file_name = strip_path( file_name )
+ stripped_file_name = basic_util.strip_path( file_name )
file_path = None
for root, dirs, files in os.walk( repo_files_dir ):
if root.find( '.hg' ) < 0:
@@ -416,25 +389,6 @@
except sqlalchemy.orm.exc.NoResultFound:
return None
-def get_config( config_file, repo, ctx, dir ):
- """Return the latest version of config_filename from the repository manifest."""
- config_file = strip_path( config_file )
- for changeset in reversed_upper_bounded_changelog( repo, ctx ):
- changeset_ctx = repo.changectx( changeset )
- for ctx_file in changeset_ctx.files():
- ctx_file_name = strip_path( ctx_file )
- if ctx_file_name == config_file:
- return get_named_tmpfile_from_ctx( changeset_ctx, ctx_file, dir )
- return None
-
-def get_config_from_disk( config_file, relative_install_dir ):
- for root, dirs, files in os.walk( relative_install_dir ):
- if root.find( '.hg' ) < 0:
- for name in files:
- if name == config_file:
- return os.path.abspath( os.path.join( root, name ) )
- return None
-
def get_ctx_rev( app, tool_shed_url, name, owner, changeset_revision ):
"""
Send a request to the tool shed to retrieve the ctx_rev for a repository defined by the
@@ -447,21 +401,6 @@
ctx_rev = common_util.tool_shed_get( app, tool_shed_url, url )
return ctx_rev
-def get_ctx_file_path_from_manifest( filename, repo, changeset_revision ):
- """
- Get the ctx file path for the latest revision of filename from the repository manifest up
- to the value of changeset_revision.
- """
- stripped_filename = strip_path( filename )
- for changeset in reversed_upper_bounded_changelog( repo, changeset_revision ):
- manifest_changeset_revision = str( repo.changectx( changeset ) )
- manifest_ctx = repo.changectx( changeset )
- for ctx_file in manifest_ctx.files():
- ctx_file_name = strip_path( ctx_file )
- if ctx_file_name == stripped_filename:
- return manifest_ctx, ctx_file
- return None, None
-
def get_current_repository_metadata_for_changeset_revision( app, repository, changeset_revision ):
encoded_repository_id = app.security.encode_id( repository.id )
repository_metadata = get_repository_metadata_by_changeset_revision( app,
@@ -537,40 +476,6 @@
dependent_downloadable_revisions.append( downloadable_revision )
return dependent_downloadable_revisions
-def get_file_context_from_ctx( ctx, filename ):
- """Return the mercurial file context for a specified file."""
- # We have to be careful in determining if we found the correct file because multiple files with
- # the same name may be in different directories within ctx if the files were moved within the change
- # set. For example, in the following ctx.files() list, the former may have been moved to the latter:
- # ['tmap_wrapper_0.0.19/tool_data_table_conf.xml.sample', 'tmap_wrapper_0.3.3/tool_data_table_conf.xml.sample'].
- # Another scenario is that the file has been deleted.
- deleted = False
- filename = strip_path( filename )
- for ctx_file in ctx.files():
- ctx_file_name = strip_path( ctx_file )
- if filename == ctx_file_name:
- try:
- # If the file was moved, its destination will be returned here.
- fctx = ctx[ ctx_file ]
- return fctx
- except LookupError, e:
- # Set deleted for now, and continue looking in case the file was moved instead of deleted.
- deleted = True
- if deleted:
- return 'DELETED'
- return None
-
-def get_file_type_str( changeset_revision, file_type ):
- if file_type == 'zip':
- file_type_str = '%s.zip' % changeset_revision
- elif file_type == 'bz2':
- file_type_str = '%s.tar.bz2' % changeset_revision
- elif file_type == 'gz':
- file_type_str = '%s.tar.gz' % changeset_revision
- else:
- file_type_str = ''
- return file_type_str
-
def get_ids_of_tool_shed_repositories_being_installed( trans, as_string=False ):
installing_repository_ids = []
new_status = trans.install_model.ToolShedRepository.installation_status.NEW
@@ -603,7 +508,7 @@
changeset_revisions = get_ordered_metadata_changeset_revisions( repository, repo, downloadable=False )
if changeset_revisions:
return changeset_revisions[ -1 ]
- return INITIAL_CHANGELOG_HASH
+ return hg_util.INITIAL_CHANGELOG_HASH
def get_latest_downloadable_changeset_revision( app, repository, repo ):
repository_tip = repository.tip( app )
@@ -613,30 +518,7 @@
changeset_revisions = get_ordered_metadata_changeset_revisions( repository, repo, downloadable=True )
if changeset_revisions:
return changeset_revisions[ -1 ]
- return INITIAL_CHANGELOG_HASH
-
-def get_named_tmpfile_from_ctx( ctx, filename, dir ):
- """Return a named temporary file created from a specified file with a given name included in a repository changeset revision."""
- filename = strip_path( filename )
- for ctx_file in ctx.files():
- ctx_file_name = strip_path( ctx_file )
- if filename == ctx_file_name:
- try:
- # If the file was moved, its destination file contents will be returned here.
- fctx = ctx[ ctx_file ]
- except LookupError, e:
- # Continue looking in case the file was moved.
- fctx = None
- continue
- if fctx:
- fh = tempfile.NamedTemporaryFile( 'wb', prefix="tmp-toolshed-gntfc", dir=dir )
- tmp_filename = fh.name
- fh.close()
- fh = open( tmp_filename, 'wb' )
- fh.write( fctx.data() )
- fh.close()
- return tmp_filename
- return None
+ return hg_util.INITIAL_CHANGELOG_HASH
def get_next_downloadable_changeset_revision( repository, repo, after_changeset_revision ):
"""
@@ -737,28 +619,6 @@
sorted_changeset_revisions = [ str( changeset_tup[ 1 ] ) for changeset_tup in sorted_changeset_tups ]
return sorted_changeset_revisions
-def get_previous_metadata_changeset_revision( repository, repo, before_changeset_revision, downloadable=True ):
- """
- Return the changeset_revision in the repository changelog that has associated metadata prior to the changeset to which
- before_changeset_revision refers. If there isn't one, return the hash value of an empty repository changelog, INITIAL_CHANGELOG_HASH.
- """
- changeset_revisions = get_ordered_metadata_changeset_revisions( repository, repo, downloadable=downloadable )
- if len( changeset_revisions ) == 1:
- changeset_revision = changeset_revisions[ 0 ]
- if changeset_revision == before_changeset_revision:
- return INITIAL_CHANGELOG_HASH
- return changeset_revision
- previous_changeset_revision = None
- for changeset_revision in changeset_revisions:
- if changeset_revision == before_changeset_revision:
- if previous_changeset_revision:
- return previous_changeset_revision
- else:
- # Return the hash value of an empty repository changelog - note that this will not be a valid changeset revision.
- return INITIAL_CHANGELOG_HASH
- else:
- previous_changeset_revision = changeset_revision
-
def get_prior_import_or_install_required_dict( trans, tsr_ids, repo_info_dicts ):
"""
This method is used in the Tool Shed when exporting a repository and its dependencies, and in Galaxy when a repository and its dependencies
@@ -974,7 +834,7 @@
else:
safe_str = ''
for i, line in enumerate( open( file_path ) ):
- safe_str = '%s%s' % ( safe_str, to_html_string( line ) )
+ safe_str = '%s%s' % ( safe_str, basic_util.to_html_string( line ) )
# Stop reading after string is larger than MAX_CONTENT_SIZE.
if len( safe_str ) > MAX_CONTENT_SIZE:
large_str = \
@@ -982,13 +842,17 @@
util.nice_size( MAX_CONTENT_SIZE )
safe_str = '%s%s' % ( safe_str, large_str )
break
- if len( safe_str ) > MAX_DISPLAY_SIZE:
- # Eliminate the middle of the file to display a file no larger than MAX_DISPLAY_SIZE. This may not be ideal if the file is larger
- # than MAX_CONTENT_SIZE.
+ if len( safe_str ) > basic_util.MAX_DISPLAY_SIZE:
+ # Eliminate the middle of the file to display a file no larger than basic_util.MAX_DISPLAY_SIZE.
+ # This may not be ideal if the file is larger than MAX_CONTENT_SIZE.
join_by_str = \
"<br/><br/>...some text eliminated here because file size is larger than maximum viewing size of %s...<br/><br/>" % \
- util.nice_size( MAX_DISPLAY_SIZE )
- safe_str = util.shrink_string_by_size( safe_str, MAX_DISPLAY_SIZE, join_by=join_by_str, left_larger=True, beginning_on_size_error=True )
+ util.nice_size( basic_util.MAX_DISPLAY_SIZE )
+ safe_str = util.shrink_string_by_size( safe_str,
+ basic_util.MAX_DISPLAY_SIZE,
+ join_by=join_by_str,
+ left_larger=True,
+ beginning_on_size_error=True )
return safe_str
def get_repository_files( trans, folder_path ):
@@ -1023,11 +887,6 @@
# This should never be reached - raise an exception?
return v, None
-def get_repository_heads( repo ):
- """Return current repository heads, which are changesets with no child changesets."""
- heads = [ repo[ h ] for h in repo.heads( None ) ]
- return heads
-
def get_repository_ids_requiring_prior_import_or_install( trans, tsr_ids, repository_dependencies ):
"""
This method is used in the Tool Shed when exporting a repository and its dependencies, and in Galaxy when a repository and its dependencies
@@ -1131,20 +990,13 @@
repository_tools_tups.append( ( relative_path, guid, tool ) )
return repository_tools_tups
-def get_reversed_changelog_changesets( repo ):
- """Return a list of changesets in reverse order from that provided by the repository manifest."""
- reversed_changelog = []
- for changeset in repo.changelog:
- reversed_changelog.insert( 0, changeset )
- return reversed_changelog
-
def get_shed_tool_conf_dict( app, shed_tool_conf ):
"""Return the in-memory version of the shed_tool_conf file, which is stored in the config_elems entry in the shed_tool_conf_dict associated with the file."""
for index, shed_tool_conf_dict in enumerate( app.toolbox.shed_tool_confs ):
if shed_tool_conf == shed_tool_conf_dict[ 'config_filename' ]:
return index, shed_tool_conf_dict
else:
- file_name = strip_path( shed_tool_conf_dict[ 'config_filename' ] )
+ file_name = basic_util.strip_path( shed_tool_conf_dict[ 'config_filename' ] )
if shed_tool_conf == file_name:
return index, shed_tool_conf_dict
@@ -1188,7 +1040,7 @@
if config_filename == shed_tool_conf:
return shed_tool_conf_dict[ 'tool_path' ]
else:
- file_name = strip_path( config_filename )
+ file_name = basic_util.strip_path( config_filename )
if file_name == shed_tool_conf:
return shed_tool_conf_dict[ 'tool_path' ]
return None
@@ -1331,9 +1183,10 @@
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
# Get the upper bound changeset revision.
upper_bound_changeset_revision = get_next_downloadable_changeset_revision( repository, repo, changeset_revision )
- # Build the list of changeset revision hashes defining each available update up to, but excluding, upper_bound_changeset_revision.
+ # Build the list of changeset revision hashes defining each available update up to, but excluding
+ # upper_bound_changeset_revision.
changeset_hashes = []
- for changeset in reversed_lower_upper_bounded_changelog( repo, changeset_revision, upper_bound_changeset_revision ):
+ for changeset in hg_util.reversed_lower_upper_bounded_changelog( repo, changeset_revision, upper_bound_changeset_revision ):
# Make sure to exclude upper_bound_changeset_revision.
if changeset != upper_bound_changeset_revision:
changeset_hashes.append( str( repo.changectx( changeset ) ) )
@@ -1477,7 +1330,10 @@
return False
def open_repository_files_folder( trans, folder_path ):
- """Return a list of dictionaries, each of which contains information for a file or directory contained within a directory in a repository file hierarchy."""
+ """
+ Return a list of dictionaries, each of which contains information for a file or directory contained
+ within a directory in a repository file hierarchy.
+ """
try:
files_list = get_repository_files( trans, folder_path )
except OSError, e:
@@ -1499,28 +1355,6 @@
folder_contents.append( node )
return folder_contents
-def pretty_print( dict=None ):
- if dict is not None:
- return json.to_json_string( dict, sort_keys=True, indent=4 )
-
-def remove_dir( dir ):
- """Attempt to remove a directory from disk."""
- if dir:
- if os.path.exists( dir ):
- try:
- shutil.rmtree( dir )
- except:
- pass
-
-def remove_file( file_name ):
- """Attempt to remove a file from disk."""
- if file_name:
- if os.path.exists( file_name ):
- try:
- os.remove( file_name )
- except:
- pass
-
def repository_was_previously_installed( trans, tool_shed_url, repository_name, repo_info_tuple ):
"""
Find out if a repository is already installed into Galaxy - there are several scenarios where this
@@ -1584,34 +1418,6 @@
trans.install_model.context.add( repository )
trans.install_model.context.flush()
-def reversed_lower_upper_bounded_changelog( repo, excluded_lower_bounds_changeset_revision, included_upper_bounds_changeset_revision ):
- """
- Return a reversed list of changesets in the repository changelog after the excluded_lower_bounds_changeset_revision, but up to and
- including the included_upper_bounds_changeset_revision. The value of excluded_lower_bounds_changeset_revision will be the value of
- INITIAL_CHANGELOG_HASH if no valid changesets exist before included_upper_bounds_changeset_revision.
- """
- # To set excluded_lower_bounds_changeset_revision, calling methods should do the following, where the value of changeset_revision
- # is a downloadable changeset_revision.
- # excluded_lower_bounds_changeset_revision = get_previous_metadata_changeset_revision( repository, repo, changeset_revision, downloadable=? )
- if excluded_lower_bounds_changeset_revision == INITIAL_CHANGELOG_HASH:
- appending_started = True
- else:
- appending_started = False
- reversed_changelog = []
- for changeset in repo.changelog:
- changeset_hash = str( repo.changectx( changeset ) )
- if appending_started:
- reversed_changelog.insert( 0, changeset )
- if changeset_hash == excluded_lower_bounds_changeset_revision and not appending_started:
- appending_started = True
- if changeset_hash == included_upper_bounds_changeset_revision:
- break
- return reversed_changelog
-
-def reversed_upper_bounded_changelog( repo, included_upper_bounds_changeset_revision ):
- """Return a reversed list of changesets in the repository changelog up to and including the included_upper_bounds_changeset_revision."""
- return reversed_lower_upper_bounded_changelog( repo, INITIAL_CHANGELOG_HASH, included_upper_bounds_changeset_revision )
-
def set_image_paths( app, encoded_repository_id, text ):
"""
Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy as well as image
@@ -1666,41 +1472,6 @@
return str( required_rd_tup[ 4 ] )
return 'False'
-def size_string( raw_text, size=MAX_DISPLAY_SIZE ):
- """Return a subset of a string (up to MAX_DISPLAY_SIZE) translated to a safe string for display in a browser."""
- if raw_text and len( raw_text ) >= size:
- large_str = '\nFile contents truncated because file size is larger than maximum viewing size of %s\n' % util.nice_size( size )
- raw_text = '%s%s' % ( raw_text[ 0:size ], large_str )
- return raw_text or ''
-
-def stringify( list ):
- if list:
- return ','.join( list )
- return ''
-
-def strip_path( fpath ):
- """Attempt to strip the path from a file name."""
- if not fpath:
- return fpath
- try:
- file_path, file_name = os.path.split( fpath )
- except:
- file_name = fpath
- return file_name
-
-def to_html_string( text ):
- """Translates the characters in text to an html string"""
- if text:
- try:
- text = unicodify( text )
- except UnicodeDecodeError, e:
- return "Error decoding string: %s" % str( e )
- text = unicode( markupsafe.escape( text ) )
- text = text.replace( '\n', '<br/>' )
- text = text.replace( ' ', ' ' )
- text = text.replace( ' ', ' ' )
- return text
-
def tool_shed_from_repository_clone_url( repository_clone_url ):
"""Given a repository clone URL, return the tool shed that contains the repository."""
return common_util.remove_protocol_and_user_from_clone_url( repository_clone_url ).split( '/repos/' )[ 0 ].rstrip( '/' )
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/util/tool_dependency_util.py
--- a/lib/tool_shed/util/tool_dependency_util.py
+++ b/lib/tool_shed/util/tool_dependency_util.py
@@ -7,6 +7,8 @@
from galaxy.model.orm import or_
import tool_shed.util.shed_util_common as suc
import tool_shed.repository_types.util as rt_util
+from tool_shed.util import basic_util
+from tool_shed.util import hg_util
from tool_shed.util import xml_util
from tool_shed.galaxy_install.tool_dependencies import td_common_util
@@ -85,7 +87,7 @@
if shed_config_dict.get( 'tool_path' ):
relative_install_dir = os.path.join( shed_config_dict.get( 'tool_path' ), relative_install_dir )
# Get the tool_dependencies.xml file from the repository.
- tool_dependencies_config = suc.get_config_from_disk( 'tool_dependencies.xml', relative_install_dir )
+ tool_dependencies_config = hg_util.get_config_from_disk( 'tool_dependencies.xml', relative_install_dir )
tree, error_message = xml_util.parse_xml( tool_dependencies_config )
if tree is None:
return tool_dependency_objects
@@ -629,7 +631,7 @@
error_message += ' prepared for re-installation.'
print error_message
tool_dependency.status = app.install_model.ToolDependency.installation_status.NEVER_INSTALLED
- suc.remove_dir( tool_dependency_install_dir )
+ basic_util.remove_dir( tool_dependency_install_dir )
can_install_tool_dependency = True
sa_session.add( tool_dependency )
sa_session.flush()
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb lib/tool_shed/util/tool_util.py
--- a/lib/tool_shed/util/tool_util.py
+++ b/lib/tool_shed/util/tool_util.py
@@ -13,6 +13,7 @@
from galaxy.util.expressions import ExpressionContext
from galaxy.web.form_builder import SelectField
from galaxy.tools.actions.upload import UploadToolAction
+from tool_shed.util import basic_util
from tool_shed.util import common_util
from tool_shed.util import hg_util
from tool_shed.util import xml_util
@@ -119,7 +120,7 @@
return False
if changeset_revision == repository.tip( trans.app ):
return True
- file_name = suc.strip_path( file_path )
+ file_name = basic_util.strip_path( file_path )
latest_version_of_file = get_latest_tool_config_revision_from_repository_manifest( repo, file_name, changeset_revision )
can_use_disk_file = filecmp.cmp( file_path, latest_version_of_file )
try:
@@ -142,7 +143,7 @@
if options and isinstance( options, dynamic_options.DynamicOptions ):
if options.tool_data_table or options.missing_tool_data_table_name:
# Make sure the repository contains a tool_data_table_conf.xml.sample file.
- sample_tool_data_table_conf = suc.get_config_from_disk( 'tool_data_table_conf.xml.sample', repo_dir )
+ sample_tool_data_table_conf = hg_util.get_config_from_disk( 'tool_data_table_conf.xml.sample', repo_dir )
if sample_tool_data_table_conf:
error, correction_msg = handle_sample_tool_data_table_conf_file( app, sample_tool_data_table_conf )
if error:
@@ -158,10 +159,10 @@
if options.index_file or options.missing_index_file:
# Make sure the repository contains the required xxx.loc.sample file.
index_file = options.index_file or options.missing_index_file
- index_file_name = suc.strip_path( index_file )
+ index_file_name = basic_util.strip_path( index_file )
sample_found = False
for sample_file in sample_files:
- sample_file_name = suc.strip_path( sample_file )
+ sample_file_name = basic_util.strip_path( sample_file )
if sample_file_name == '%s.sample' % index_file_name:
options.index_file = index_file_name
options.missing_index_file = None
@@ -206,7 +207,7 @@
"""
if dest_path is None:
dest_path = os.path.abspath( app.config.tool_data_path )
- sample_file_name = suc.strip_path( filename )
+ sample_file_name = basic_util.strip_path( filename )
copied_file = sample_file_name.replace( '.sample', '' )
full_source_path = os.path.abspath( filename )
full_destination_path = os.path.join( dest_path, sample_file_name )
@@ -312,7 +313,7 @@
{<Tool guid> : [{ tool_config : <tool_config_file>, id: <ToolSection id>, version : <ToolSection version>, name : <TooSection name>}]}
"""
tool_panel_dict = {}
- file_name = suc.strip_path( tool_config )
+ file_name = basic_util.strip_path( tool_config )
tool_section_dicts = generate_tool_section_dicts( tool_config=file_name, tool_sections=tool_sections )
tool_panel_dict[ guid ] = tool_section_dicts
return tool_panel_dict
@@ -412,11 +413,11 @@
This method is restricted to tool_config files rather than any file since it is likely that, with the exception of tool config files,
multiple files will have the same name in various directories within the repository.
"""
- stripped_filename = suc.strip_path( filename )
- for changeset in suc.reversed_upper_bounded_changelog( repo, changeset_revision ):
+ stripped_filename = basic_util.strip_path( filename )
+ for changeset in hg_util.reversed_upper_bounded_changelog( repo, changeset_revision ):
manifest_ctx = repo.changectx( changeset )
for ctx_file in manifest_ctx.files():
- ctx_file_name = suc.strip_path( ctx_file )
+ ctx_file_name = basic_util.strip_path( ctx_file )
if ctx_file_name == stripped_filename:
try:
fctx = manifest_ctx[ ctx_file ]
@@ -442,14 +443,14 @@
"""
deleted_sample_files = []
sample_files = []
- for changeset in suc.reversed_upper_bounded_changelog( repo, ctx ):
+ for changeset in hg_util.reversed_upper_bounded_changelog( repo, ctx ):
changeset_ctx = repo.changectx( changeset )
for ctx_file in changeset_ctx.files():
- ctx_file_name = suc.strip_path( ctx_file )
+ ctx_file_name = basic_util.strip_path( ctx_file )
# If we decide in the future that files deleted later in the changelog should not be used, we can use the following if statement.
# if ctx_file_name.endswith( '.sample' ) and ctx_file_name not in sample_files and ctx_file_name not in deleted_sample_files:
if ctx_file_name.endswith( '.sample' ) and ctx_file_name not in sample_files:
- fctx = suc.get_file_context_from_ctx( changeset_ctx, ctx_file )
+ fctx = hg_util.get_file_context_from_ctx( changeset_ctx, ctx_file )
if fctx in [ 'DELETED' ]:
# Since the possibly future used if statement above is commented out, the same file that was initially added will be
# discovered in an earlier changeset in the change log and fall through to the else block below. In other words, if
@@ -536,7 +537,7 @@
version_lineage = [ guid ]
# Get all ancestor guids of the received guid.
current_child_guid = guid
- for changeset in suc.reversed_upper_bounded_changelog( repo, repository_metadata.changeset_revision ):
+ for changeset in hg_util.reversed_upper_bounded_changelog( repo, repository_metadata.changeset_revision ):
ctx = repo.changectx( changeset )
rm = suc.get_repository_metadata_by_changeset_revision( trans.app, repository_id, str( ctx ) )
if rm:
@@ -546,9 +547,9 @@
current_child_guid = parent_guid
# Get all descendant guids of the received guid.
current_parent_guid = guid
- for changeset in suc.reversed_lower_upper_bounded_changelog( repo,
- repository_metadata.changeset_revision,
- repository.tip( trans.app ) ):
+ for changeset in hg_util.reversed_lower_upper_bounded_changelog( repo,
+ repository_metadata.changeset_revision,
+ repository.tip( trans.app ) ):
ctx = repo.changectx( changeset )
rm = suc.get_repository_metadata_by_changeset_revision( trans.app, repository_id, str( ctx ) )
if rm:
@@ -574,7 +575,7 @@
break
if missing_data_table_entry:
# The repository must contain a tool_data_table_conf.xml.sample file that includes all required entries for all tools in the repository.
- sample_tool_data_table_conf = suc.get_config_from_disk( 'tool_data_table_conf.xml.sample', relative_install_dir )
+ sample_tool_data_table_conf = hg_util.get_config_from_disk( 'tool_data_table_conf.xml.sample', relative_install_dir )
if sample_tool_data_table_conf:
# Add entries to the ToolDataTableManager's in-memory data_tables dictionary.
error, message = handle_sample_tool_data_table_conf_file( app, sample_tool_data_table_conf, persist=True )
@@ -598,11 +599,11 @@
params_with_missing_index_file = repository_tool.params_with_missing_index_file
for param in params_with_missing_index_file:
options = param.options
- missing_file_name = suc.strip_path( options.missing_index_file )
+ missing_file_name = basic_util.strip_path( options.missing_index_file )
if missing_file_name not in sample_files_copied:
# The repository must contain the required xxx.loc.sample file.
for sample_file in sample_files:
- sample_file_name = suc.strip_path( sample_file )
+ sample_file_name = basic_util.strip_path( sample_file )
if sample_file_name == '%s.sample' % missing_file_name:
copy_sample_file( app, sample_file )
if options.tool_data_table and options.tool_data_table.missing_index_file:
@@ -643,7 +644,7 @@
error, message = handle_sample_tool_data_table_conf_file( trans.app, tool_data_table_config )
if error:
log.debug( message )
- manifest_ctx, ctx_file = suc.get_ctx_file_path_from_manifest( tool_config_filename, repo, changeset_revision )
+ manifest_ctx, ctx_file = hg_util.get_ctx_file_path_from_manifest( tool_config_filename, repo, changeset_revision )
if manifest_ctx and ctx_file:
tool, message2 = load_tool_from_tmp_config( trans, repo, repository_id, manifest_ctx, ctx_file, work_dir )
message = concat_messages( message, message2 )
@@ -885,7 +886,7 @@
message = concat_messages( message, message2 )
else:
tool, message, sample_files = handle_sample_files_and_load_tool_from_tmp_config( trans, repo, repository_id, changeset_revision, tool_config_filename, work_dir )
- suc.remove_dir( work_dir )
+ basic_util.remove_dir( work_dir )
trans.app.config.tool_data_path = original_tool_data_path
# Reset the tool_data_tables by loading the empty tool_data_table_conf.xml file.
reset_tool_data_tables( trans.app )
@@ -911,7 +912,7 @@
def load_tool_from_tmp_config( trans, repo, repository_id, ctx, ctx_file, work_dir ):
tool = None
message = ''
- tmp_tool_config = suc.get_named_tmpfile_from_ctx( ctx, ctx_file, work_dir )
+ tmp_tool_config = hg_util.get_named_tmpfile_from_ctx( ctx, ctx_file, work_dir )
if tmp_tool_config:
element_tree, error_message = xml_util.parse_xml( tmp_tool_config )
if element_tree is None:
@@ -921,7 +922,7 @@
tmp_code_files = []
for code_elem in element_tree_root.findall( 'code' ):
code_file_name = code_elem.get( 'file' )
- tmp_code_file_name = suc.copy_file_from_manifest( repo, ctx, code_file_name, work_dir )
+ tmp_code_file_name = hg_util.copy_file_from_manifest( repo, ctx, code_file_name, work_dir )
if tmp_code_file_name:
tmp_code_files.append( tmp_code_file_name )
tool, valid, message = load_tool_from_config( trans.app, repository_id, tmp_tool_config )
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb templates/admin/review_tool_migration_stages.mako
--- a/templates/admin/review_tool_migration_stages.mako
+++ b/templates/admin/review_tool_migration_stages.mako
@@ -35,7 +35,7 @@
</p></div><table class="grid">
- <% from tool_shed.util.shed_util_common import to_html_string %>
+ <% from tool_shed.util.basic_util import to_html_string %>
%for stage in migration_stages_dict.keys():
<%
migration_command = 'sh ./scripts/migrate_tools/%04d_tools.sh' % stage
diff -r f6aa2d17d38430e92beb9da80f53c9989dd504ee -r dbc3d5c3506ed833dc453faa4f00797d95b8a8bb templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
--- a/templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
+++ b/templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
@@ -31,7 +31,7 @@
%for tool_dependency in repository.tool_dependencies:
<%
if tool_dependency.error_message:
- from tool_shed.util.shed_util_common import to_html_string
+ from tool_shed.util.basic_util import to_html_string
error_message = to_html_string( tool_dependency.error_message )
else:
error_message = ''
This diff is so big that we needed to truncate the remainder.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/eed45027e219/
Changeset: eed45027e219
Branch: next-stable
User: carlfeberhard
Date: 2014-05-28 15:24:01
Summary: Bug fix: allow tags to be added in display_base.mako by swapping the load order of autocomplete plugins (autocomplete still not working)
Affected #: 2 files
diff -r 26952d141e42bd936eab9465c2761325204f9531 -r eed45027e219df9c4fc87fcfc788380ecc500b1a templates/display_base.mako
--- a/templates/display_base.mako
+++ b/templates/display_base.mako
@@ -32,10 +32,19 @@
<%def name="javascripts()">
${parent.javascripts()}
- ${h.js( "libs/jquery/jstorage", "libs/jquery/jquery.autocomplete", "libs/jquery/jquery.rating",
- "galaxy.autocom_tagging" )}
- ${h.js( "galaxy.panels", "libs/jquery/jstorage", "libs/jquery/jquery.event.drag", "libs/jquery/jquery.event.hover",
- "libs/jquery/jquery.mousewheel", "libs/jquery/jquery-ui", "libs/require", "libs/farbtastic" )}
+ ${h.js(
+ "libs/jquery/jstorage",
+ "libs/jquery/jquery.rating",
+ "galaxy.panels",
+ "libs/jquery/jquery.event.drag",
+ "libs/jquery/jquery.event.hover",
+ "libs/jquery/jquery.mousewheel",
+ "libs/jquery/jquery-ui",
+ "libs/require",
+ "libs/farbtastic",
+ "libs/jquery/jquery.autocomplete",
+ "galaxy.autocom_tagging"
+ )}
<script type="text/javascript">
diff -r 26952d141e42bd936eab9465c2761325204f9531 -r eed45027e219df9c4fc87fcfc788380ecc500b1a templates/tagging_common.mako
--- a/templates/tagging_common.mako
+++ b/templates/tagging_common.mako
@@ -211,7 +211,7 @@
use_toggle_link: ${iff( use_toggle_link, 'true', 'false' )}
};
- $('#${elt_id}').find( 'input' ).autocomplete_tagging(options);
+ $('#${elt_id}').autocomplete_tagging(options);
</script>
## Use style to hide/display the tag area.
https://bitbucket.org/galaxy/galaxy-central/commits/f6aa2d17d384/
Changeset: f6aa2d17d384
User: carlfeberhard
Date: 2014-05-28 15:25:23
Summary: merge
Affected #: 2 files
diff -r 950e68808ba693d8eeec87b4be9ce78e4b864456 -r f6aa2d17d38430e92beb9da80f53c9989dd504ee templates/display_base.mako
--- a/templates/display_base.mako
+++ b/templates/display_base.mako
@@ -32,10 +32,19 @@
<%def name="javascripts()">
${parent.javascripts()}
- ${h.js( "libs/jquery/jstorage", "libs/jquery/jquery.autocomplete", "libs/jquery/jquery.rating",
- "galaxy.autocom_tagging" )}
- ${h.js( "galaxy.panels", "libs/jquery/jstorage", "libs/jquery/jquery.event.drag", "libs/jquery/jquery.event.hover",
- "libs/jquery/jquery.mousewheel", "libs/jquery/jquery-ui", "libs/require", "libs/farbtastic" )}
+ ${h.js(
+ "libs/jquery/jstorage",
+ "libs/jquery/jquery.rating",
+ "galaxy.panels",
+ "libs/jquery/jquery.event.drag",
+ "libs/jquery/jquery.event.hover",
+ "libs/jquery/jquery.mousewheel",
+ "libs/jquery/jquery-ui",
+ "libs/require",
+ "libs/farbtastic",
+ "libs/jquery/jquery.autocomplete",
+ "galaxy.autocom_tagging"
+ )}
<script type="text/javascript">
diff -r 950e68808ba693d8eeec87b4be9ce78e4b864456 -r f6aa2d17d38430e92beb9da80f53c9989dd504ee templates/tagging_common.mako
--- a/templates/tagging_common.mako
+++ b/templates/tagging_common.mako
@@ -211,7 +211,7 @@
use_toggle_link: ${iff( use_toggle_link, 'true', 'false' )}
};
- $('#${elt_id}').find( 'input' ).autocomplete_tagging(options);
+ $('#${elt_id}').autocomplete_tagging(options);
</script>
## Use style to hide/display the tag area.
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: jgoecks: Remove jStorage from Trackster requirements because it is not used.
by commits-noreply@bitbucket.org 27 May '14
by commits-noreply@bitbucket.org 27 May '14
27 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/950e68808ba6/
Changeset: 950e68808ba6
User: jgoecks
Date: 2014-05-27 23:42:10
Summary: Remove jStorage from Trackster requirements because it is not used.
Affected #: 1 file
diff -r be58520000c71329d6e63830fc5776604c7b8382 -r 950e68808ba693d8eeec87b4be9ce78e4b864456 static/scripts/viz/trackster.js
--- a/static/scripts/viz/trackster.js
+++ b/static/scripts/viz/trackster.js
@@ -8,7 +8,6 @@
[
// load js libraries
'utils/utils',
- 'libs/jquery/jstorage',
'libs/jquery/jquery.event.drag',
'libs/jquery/jquery.event.hover',
'libs/jquery/jquery.mousewheel',
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/26952d141e42/
Changeset: 26952d141e42
Branch: next-stable
User: dannon
Date: 2014-05-27 23:11:01
Summary: Fix dep_value being undefined at use. Someone needs to look at the logic here, this is purely an operational fix preventing a crash -- there's logic that still needs to happen somewhere in the chain here.
Affected #: 1 file
diff -r 7a6ce727849489c03fbf766bc98357af52d4ffb8 -r 26952d141e42bd936eab9465c2761325204f9531 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -894,6 +894,10 @@
# If we got this far, we can actually look at the dependencies
# to see if their values will not be available until runtime.
for dep_name in self.get_dependencies():
+ # This may not be completely correct, but it's possible to go
+ # through the layers indicated below without assigning dep_value,
+ # which is even worse. TODO fix it?
+ dep_value = None
if dep_name in context:
dep_value = context[ dep_name ]
else:
@@ -1063,7 +1067,7 @@
'value': value
})
return d
-
+
def _get_dbkey_names( self, trans=None ):
if not self.tool:
# Hack for unit tests, since we have no tool
@@ -1735,7 +1739,7 @@
field_name = "%s%s" % ( self.name, suffix )
field = form_builder.SelectField( field_name, multiple, None, self.refresh_on_change, refresh_on_change_values=self.refresh_on_change_values )
-
+
dataset_collection_matcher = DatasetCollectionMatcher( dataset_matcher )
for history_dataset_collection in history.active_dataset_collections:
https://bitbucket.org/galaxy/galaxy-central/commits/be58520000c7/
Changeset: be58520000c7
User: dannon
Date: 2014-05-27 23:11:13
Summary: merge next-stable.
Affected #: 1 file
diff -r fefcf6f47abe795b392348fbb901ffdf7b5c61ab -r be58520000c71329d6e63830fc5776604c7b8382 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -894,6 +894,10 @@
# If we got this far, we can actually look at the dependencies
# to see if their values will not be available until runtime.
for dep_name in self.get_dependencies():
+ # This may not be completely correct, but it's possible to go
+ # through the layers indicated below without assigning dep_value,
+ # which is even worse. TODO fix it?
+ dep_value = None
if dep_name in context:
dep_value = context[ dep_name ]
else:
@@ -1063,7 +1067,7 @@
'value': value
})
return d
-
+
def _get_dbkey_names( self, trans=None ):
if not self.tool:
# Hack for unit tests, since we have no tool
@@ -1735,7 +1739,7 @@
field_name = "%s%s" % ( self.name, suffix )
field = form_builder.SelectField( field_name, multiple, None, self.refresh_on_change, refresh_on_change_values=self.refresh_on_change_values )
-
+
dataset_collection_matcher = DatasetCollectionMatcher( dataset_matcher )
for history_dataset_collection in history.active_dataset_collections:
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
8 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/7d0451a2b957/
Changeset: 7d0451a2b957
Branch: next-stable
User: jmchilton
Date: 2014-05-27 19:52:41
Summary: Bugfix: Hadn't implemented clean way to iterate of collection element names in tools.
Perhaps stretching the definition of bugfix here to get this in next-stable.
Affected #: 2 files
diff -r bdff5a8cc4b91ec6deff722884bde7357e67e6ef -r 7d0451a2b957b1b39439eb8c47df8eb7ed7a82b0 lib/galaxy/tools/wrappers.py
--- a/lib/galaxy/tools/wrappers.py
+++ b/lib/galaxy/tools/wrappers.py
@@ -272,6 +272,12 @@
def is_collection( self ):
return True
+ def __getitem__( self, key ):
+ if isinstance( key, int ):
+ return self.element_instance_list[ key ]
+ else:
+ return self.element_instances[ key ]
+
def __getattr__( self, key ):
return self.element_instances[ key ]
diff -r bdff5a8cc4b91ec6deff722884bde7357e67e6ef -r 7d0451a2b957b1b39439eb8c47df8eb7ed7a82b0 test/functional/tools/collection_paired_test.xml
--- a/test/functional/tools/collection_paired_test.xml
+++ b/test/functional/tools/collection_paired_test.xml
@@ -1,6 +1,6 @@
<tool id="collection_paired_test" name="paired_test" version="0.1.0"><command>
- cat $f1.forward $f1.reverse >> $out1;
+ cat $f1.forward $f1['reverse'] >> $out1;
</command><inputs><param name="f1" type="data_collection" collection_type="paired" />
https://bitbucket.org/galaxy/galaxy-central/commits/b9a3482a1a3f/
Changeset: b9a3482a1a3f
Branch: next-stable
User: jmchilton
Date: 2014-05-27 19:52:41
Summary: Bugfix: Workflow editor mapping icon bug.
Affected #: 1 file
diff -r 7d0451a2b957b1b39439eb8c47df8eb7ed7a82b0 -r b9a3482a1a3fbde50674f7c65a72bf8a8ecfe51d static/scripts/galaxy.workflow_editor.canvas.js
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -133,11 +133,11 @@
onMouseEnter: function( e ) {
var model = this.model;
if( ! model.terminal.connected() && model.mapOver.isCollection ) {
- this.$el.color( "red" );
+ this.$el.css( "color", "red" );
}
},
onMouseLeave: function( e ) {
- this.$el.color( "blue" );
+ this.$el.css( "color", "black" );
},
onClick: function( e ) {
var model = this.model;
https://bitbucket.org/galaxy/galaxy-central/commits/d08a3430dc15/
Changeset: d08a3430dc15
Branch: next-stable
User: jmchilton
Date: 2014-05-27 19:52:41
Summary: Bugfix: More left/right -> forward/reverse fixes.
Affected #: 1 file
diff -r b9a3482a1a3fbde50674f7c65a72bf8a8ecfe51d -r d08a3430dc152b4c98ac304377ede5582e2bd450 test/functional/tools/collection_nested_test.xml
--- a/test/functional/tools/collection_nested_test.xml
+++ b/test/functional/tools/collection_nested_test.xml
@@ -16,8 +16,8 @@
<collection type="list:paired"><element name="i1"><collection type="paired">
- <element name="left" value="simple_line.txt" />
- <element name="right" value="simple_line_alternative.txt" />
+ <element name="forward" value="simple_line.txt" />
+ <element name="reverse" value="simple_line_alternative.txt" /></collection></element></collection>
@@ -37,8 +37,8 @@
<test><param name="f1"><collection type="paired">
- <element name="left" value="simple_line.txt" />
- <element name="right" value="simple_line_alternative.txt" />
+ <element name="forward" value="simple_line.txt" />
+ <element name="reverse" value="simple_line_alternative.txt" /></collection></param><output name="out1">
https://bitbucket.org/galaxy/galaxy-central/commits/aec79a7e67a4/
Changeset: aec79a7e67a4
Branch: next-stable
User: jmchilton
Date: 2014-05-27 19:52:41
Summary: Unit tests (would have prevented bug in 4044a2a and similar bugs).
Affected #: 1 file
diff -r d08a3430dc152b4c98ac304377ede5582e2bd450 -r aec79a7e67a4ea73054a868162074a793338a982 test/qunit/tests/workflow_editor_tests.js
--- a/test/qunit/tests/workflow_editor_tests.js
+++ b/test/qunit/tests/workflow_editor_tests.js
@@ -482,6 +482,17 @@
var c = new Connector( outputTerminal, terminal );
return c;
+ },
+ connectAttachedMappedOutput: function( ) {
+ this.view.addDataInput( { name: "TestName", extensions: [ "txt" ], input_type: "dataset_collection" } );
+ var terminal = this.view.node.input_terminals[ "TestName" ];
+
+ var outputTerminal = new OutputTerminal( { name: "TestOuptut", datatypes: [ "txt" ] } );
+ outputTerminal.node = { markChanged: function() {}, post_job_actions: [], hasMappedOverInputTerminals: function() { return false; } };
+ outputTerminal.terminalMapping = { disableMapOver: function() {}, mapOver: new CollectionTypeDescription( "list" ) };
+ var c = new Connector( outputTerminal, terminal );
+
+ return c;
}
} );
@@ -519,6 +530,14 @@
ok( connector.handle2 === terminal );
} );
+ test( "replacing mapped terminal on data collection input update preserves connections", function() {
+ var connector = this.connectAttachedMappedOutput();
+ var newElement = $("<div class='inputs'></div>");
+ this.view.addDataInput( { name: "TestName", extensions: ["txt"], input_type: "dataset_collection" }, newElement );
+ var terminal = newElement.find(".input-terminal")[ 0 ].terminal;
+ ok( connector.handle2 === terminal );
+ } );
+
test( "replacing terminal on data input destroys invalid connections", function() {
var connector = this.connectAttachedTerminal( "txt", "txt" );
var newElement = $("<div class='inputs'></div>");
@@ -529,6 +548,26 @@
ok( connector_destroy_spy.called );
} );
+ test( "replacing terminal on data input with collection changes mapping view type", function() {
+ var connector = this.connectAttachedTerminal( "txt", "txt" );
+ var newElement = $("<div class='inputs'></div>");
+ var connector_destroy_spy = sinon.spy( connector, "destroy" );
+ this.view.addDataInput( { name: "TestName", extensions: ["txt"], input_type: "dataset_collection" }, newElement );
+ // Input type changed to dataset_collection - old connections are reset.
+ // Would be nice to preserve these connections and make them map over.
+ var terminal = newElement.find(".input-terminal")[ 0 ].terminal;
+ ok( connector_destroy_spy.called );
+ } );
+
+ test( "replacing terminal on data collection input with simple input changes mapping view type", function() {
+ var connector = this.connectAttachedMappedOutput();
+ var newElement = $("<div class='inputs'></div>");
+ var connector_destroy_spy = sinon.spy( connector, "destroy" );
+ this.view.addDataInput( { name: "TestName", extensions: ["txt"], input_type: "dataset" }, newElement );
+ var terminal = newElement.find(".input-terminal")[ 0 ].terminal;
+ ok( connector_destroy_spy.called );
+ } );
+
/* global InputTerminalView */
module( "Input terminal view", {
setup: function() {
https://bitbucket.org/galaxy/galaxy-central/commits/289e48b4f3b2/
Changeset: 289e48b4f3b2
Branch: next-stable
User: jmchilton
Date: 2014-05-27 19:52:41
Summary: Bugfix: Fix another attachable bug related to collections and multiple inputs (like 1882eff).
With unit tests that would have prevented both bugs.
Affected #: 2 files
diff -r aec79a7e67a4ea73054a868162074a793338a982 -r 289e48b4f3b20eca95063f3dcd6a8cfb9d3407e5 static/scripts/galaxy.workflow_editor.canvas.js
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -398,6 +398,9 @@
var thisMapOver = this.mapOver();
if( otherCollectionType.isCollection ) {
if( this.multiple ) {
+ if( this.connected() ) {
+ return false;
+ }
if( otherCollectionType.rank == 1 ) {
return this._producesAcceptableDatatype( other );
} else {
diff -r aec79a7e67a4ea73054a868162074a793338a982 -r 289e48b4f3b20eca95063f3dcd6a8cfb9d3407e5 test/qunit/tests/workflow_editor_tests.js
--- a/test/qunit/tests/workflow_editor_tests.js
+++ b/test/qunit/tests/workflow_editor_tests.js
@@ -917,6 +917,24 @@
this.verifyNotAttachable( inputTerminal1, "list:paired" );
} );
+ test( "unconnected multiple inputs can be connected to rank 1 collections", function() {
+ var inputTerminal1 = this.newInputTerminal( null, { multiple: true } );
+ this.verifyAttachable( inputTerminal1, "list" );
+ } );
+
+ test( "connected multiple input cannot be connected to collections", function() {
+ var inputTerminal1 = this.newInputTerminal( null, { multiple: true } );
+ var connectedInput1 = this.addConnectedInput( inputTerminal1 );
+ this.addConnectedOutput( connectedInput1 );
+ // Normally could do this reduction, but cannot because input already connected.
+ this.verifyNotAttachable( connectedInput1, "list" );
+ } );
+
+ test( "unconnected multiple inputs cannot be connected to rank > 1 collections (yet...)", function() {
+ var inputTerminal1 = this.newInputTerminal( null, { multiple: true } );
+ this.verifyNotAttachable( inputTerminal1, "list:paired" );
+ } );
+
test( "resetMappingIfNeeded does nothing if not mapped", function() {
var inputTerminal1 = this.newInputTerminal();
inputTerminal1.resetMappingIfNeeded();
https://bitbucket.org/galaxy/galaxy-central/commits/df97683f7ee0/
Changeset: df97683f7ee0
Branch: next-stable
User: jmchilton
Date: 2014-05-27 19:52:41
Summary: Bugfix: Workflow editor would unnessecarily leave nodes in mapped over state when no connections force it.
Other small code harmonizations to do this cleanly.
Affected #: 2 files
diff -r 289e48b4f3b20eca95063f3dcd6a8cfb9d3407e5 -r df97683f7ee0c4370161f26ce862b83283b92219 static/scripts/galaxy.workflow_editor.canvas.js
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -226,7 +226,16 @@
},
resetMappingIfNeeded: function( ) {
- if( ! this.node.hasMappedOverInputTerminals() ) {
+ // If inputs were only mapped over to preserve
+ // an output just disconnected reset these...
+ if( ! this.node.hasConnectedOutputTerminals() && ! this.node.hasConnectedMappedInputTerminals()){
+ _.each( this.node.mappedInputTerminals(), function( mappedInput ) {
+ mappedInput.resetMappingIfNeeded();
+ } );
+ }
+
+ var noMappedInputs = ! this.node.hasMappedOverInputTerminals();
+ if( noMappedInputs ) {
this.resetMapping();
}
},
@@ -654,13 +663,16 @@
this.tool_errors = {};
},
connectedOutputTerminals: function() {
- var connected_outputs = [];
- $.each( this.output_terminals, function( _, t ) {
+ return this._connectedTerminals( this.output_terminals );
+ },
+ _connectedTerminals: function( terminals ) {
+ var connectedTerminals = [];
+ $.each( terminals, function( _, t ) {
if( t.connectors.length > 0 ) {
- connected_outputs.push( t );
+ connectedTerminals.push( t );
}
} );
- return connected_outputs;
+ return connectedTerminals;
},
hasConnectedOutputTerminals: function() {
// return this.connectedOutputTerminals().length > 0; <- optimized this
@@ -686,9 +698,9 @@
}
return false;
},
- _connectedMappedTerminals: function( all_terminals ) {
+ _connectedMappedTerminals: function( terminals ) {
var mapped_outputs = [];
- $.each( all_terminals, function( _, t ) {
+ $.each( terminals, function( _, t ) {
var mapOver = t.mapOver();
if( mapOver.isCollection ) {
if( t.connectors.length > 0 ) {
@@ -698,6 +710,19 @@
});
return mapped_outputs;
},
+ mappedInputTerminals: function() {
+ return this._mappedTerminals( this.input_terminals );
+ },
+ _mappedTerminals: function( terminals ) {
+ var mappedTerminals = [];
+ $.each( terminals, function( _, t ) {
+ var mapOver = t.mapOver();
+ if( mapOver.isCollection ) {
+ mappedTerminals.push( t );
+ }
+ } );
+ return mappedTerminals;
+ },
hasMappedOverInputTerminals: function() {
var found = false;
_.each( this.input_terminals, function( t ) {
@@ -708,11 +733,6 @@
} );
return found;
},
- forceDisconnectOutputs: function() {
- _.each( this.output_terminals, function( terminal ) {
- terminal.disconnectAll();
- } );
- },
redraw : function () {
$.each( this.input_terminals, function( _, t ) {
t.redraw();
diff -r 289e48b4f3b20eca95063f3dcd6a8cfb9d3407e5 -r df97683f7ee0c4370161f26ce862b83283b92219 test/qunit/tests/workflow_editor_tests.js
--- a/test/qunit/tests/workflow_editor_tests.js
+++ b/test/qunit/tests/workflow_editor_tests.js
@@ -477,7 +477,7 @@
var terminal = this.view.node.input_terminals[ "TestName" ];
var outputTerminal = new OutputTerminal( { name: "TestOuptut", datatypes: [ outputType ] } );
- outputTerminal.node = { markChanged: function() {}, post_job_actions: [], hasMappedOverInputTerminals: function() { return false; } };
+ outputTerminal.node = { markChanged: function() {}, post_job_actions: [], hasMappedOverInputTerminals: function() { return false; }, hasConnectedOutputTerminals: function() { return true; } };
outputTerminal.terminalMapping = { disableMapOver: function() {}, mapOver: NULL_COLLECTION_TYPE_DESCRIPTION };
var c = new Connector( outputTerminal, terminal );
@@ -488,7 +488,7 @@
var terminal = this.view.node.input_terminals[ "TestName" ];
var outputTerminal = new OutputTerminal( { name: "TestOuptut", datatypes: [ "txt" ] } );
- outputTerminal.node = { markChanged: function() {}, post_job_actions: [], hasMappedOverInputTerminals: function() { return false; } };
+ outputTerminal.node = { markChanged: function() {}, post_job_actions: [], hasMappedOverInputTerminals: function() { return false; }, hasConnectedOutputTerminals: function() { return true; } };
outputTerminal.terminalMapping = { disableMapOver: function() {}, mapOver: new CollectionTypeDescription( "list" ) };
var c = new Connector( outputTerminal, terminal );
https://bitbucket.org/galaxy/galaxy-central/commits/7a6ce7278494/
Changeset: 7a6ce7278494
Branch: next-stable
User: jmchilton
Date: 2014-05-27 19:53:06
Summary: Pack scripts.
Affected #: 1 file
diff -r df97683f7ee0c4370161f26ce862b83283b92219 -r 7a6ce727849489c03fbf766bc98357af52d4ffb8 static/scripts/packed/galaxy.workflow_editor.canvas.js
--- a/static/scripts/packed/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/packed/galaxy.workflow_editor.canvas.js
@@ -1,1 +1,1 @@
-function CollectionTypeDescription(a){this.collectionType=a;this.isCollection=true;this.rank=a.split(":").length}$.extend(CollectionTypeDescription.prototype,{append:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return this}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return otherCollectionType}return new CollectionTypeDescription(this.collectionType+":"+a.collectionType)},canMatch:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return true}return a.collectionType==this.collectionType},canMapOver:function(b){if(b===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(b===ANY_COLLECTION_TYPE_DESCRIPTION){return false}if(this.rank<=b.rank){return false}var a=b.collectionType;return this._endsWith(this.collectionType,a)},effectiveMapOver:function(a){var c=a.collectionType;var b=this.collectionType.substring(0,this.collectionType.length-c.length-1);return new CollectionTypeDescription(b)},equal:function(a){return a.collectionType==this.collectionType},toString:function(){return"CollectionType["+this.collectionType+"]"},_endsWith:function(b,a){return b.indexOf(a,b.length-a.length)!==-1}});NULL_COLLECTION_TYPE_DESCRIPTION={isCollection:false,canMatch:function(a){return false},canMapOver:function(a){return false},toString:function(){return"NullCollectionType[]"},append:function(a){return a},equal:function(a){return a===this}};ANY_COLLECTION_TYPE_DESCRIPTION={isCollection:true,canMatch:function(a){return NULL_COLLECTION_TYPE_DESCRIPTION!==a},canMapOver:function(a){return false},toString:function(){return"AnyCollectionType[]"},append:function(a){throw"Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"},equal:function(a){return a===this}};var TerminalMapping=Backbone.Model.extend({initialize:function(a){this.mapOver=a.mapOver||NULL_COLLECTION_TYPE_DESCRIPTION;this.terminal=a.terminal;this.terminal.terminalMapping=this},disableMapOver:function(){this.setMapOver(NULL_COLLECTION_TYPE_DESCRIPTION)},setMapOver:function(a){this.mapOver=a;this.trigger("change")}});var TerminalMappingView=Backbone.View.extend({tagName:"div",className:"fa-icon-button fa fa-folder-o",initialize:function(b){var a="Run tool in parallel over collection";this.$el.tooltip({delay:500,title:a});this.model.bind("change",_.bind(this.render,this))},render:function(){if(this.model.mapOver.isCollection){this.$el.show()}else{this.$el.hide()}},});var InputTerminalMappingView=TerminalMappingView.extend({events:{click:"onClick",mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",},onMouseEnter:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){this.$el.color("red")}},onMouseLeave:function(a){this.$el.color("blue")},onClick:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){a.terminal.resetMapping()}},});var InputTerminalMapping=TerminalMapping;var InputCollectionTerminalMapping=TerminalMapping;var OutputTerminalMapping=TerminalMapping;var OutputTerminalMappingView=TerminalMappingView;var InputCollectionTerminalMappingView=InputTerminalMappingView;var OutputCollectionTerminalMapping=TerminalMapping;var OutputCollectionTerminalMappingView=TerminalMappingView;var Terminal=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.connectors=[]},connect:function(a){this.connectors.push(a);if(this.node){this.node.markChanged()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.markChanged();this.resetMappingIfNeeded()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})},destroyInvalidConnections:function(){_.each(this.connectors,function(a){a.destroyIfInvalid()})},setMapOver:function(a){if(this.multiple){return}if(!this.mapOver().equal(a)){this.terminalMapping.setMapOver(a);_.each(this.node.output_terminals,function(b){b.setMapOver(a)})}},mapOver:function(){if(!this.terminalMapping){return NULL_COLLECTION_TYPE_DESCRIPTION}else{return this.terminalMapping.mapOver}},isMappedOver:function(){return this.terminalMapping&&this.terminalMapping.mapOver.isCollection},resetMapping:function(){this.terminalMapping.disableMapOver()},resetMappingIfNeeded:function(){},});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes},resetMappingIfNeeded:function(){if(!this.node.hasMappedOverInputTerminals()){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();_.each(this.connectors,function(a){var b=a.handle2;if(b){b.resetMappingIfNeeded();a.destroyIfInvalid()}})}});var BaseInputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.update(a.input)},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},resetMappingIfNeeded:function(){var b=this.mapOver();if(!b.isCollection){return}var a=this.node.hasConnectedMappedInputTerminals()||(!this.node.hasConnectedOutputTerminals());if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();if(!this.node.hasMappedOverInputTerminals()){_.each(this.node.output_terminals,function(a){a.resetMapping()})}},connected:function(){return this.connectors.length!==0},_inputFilled:function(){var a;if(!this.connected()){a=false}else{if(this.multiple){if(!this.connected()){a=false}else{var b=this.connectors[0].handle1;if(b===null){a=false}else{if(b.isDataCollectionInput||b.isMappedOver()||b.datatypes.indexOf("input_collection")>0){a=true}else{a=false}}}}else{a=true}}return a},_mappingConstraints:function(){if(!this.node){return[]}var b=this.mapOver();if(b.isCollection){return[b]}var a=[];if(!this.node.hasConnectedOutputTerminals()){_.each(this.node.connectedMappedInputTerminals(),function(c){a.push(c.mapOver())})}else{a.push(_.first(_.values(this.node.output_terminals)).mapOver())}return a},_producesAcceptableDatatype:function(a){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){var h=f[b];if(h=="input"||h=="input_collection"||issubtype(f[b],this.datatypes[c])){return true}}}return false},_otherCollectionType:function(a){var c=NULL_COLLECTION_TYPE_DESCRIPTION;if(a.isDataCollectionInput){c=a.collectionType}else{var b=a.mapOver();if(b.isCollection){c=b}}return c},});var InputTerminal=BaseInputTerminal.extend({update:function(a){this.datatypes=a.extensions;this.multiple=a.multiple;this.collection=false},connect:function(a){BaseInputTerminal.prototype.connect.call(this,a);var b=a.handle1;if(!b){return}var c=this._otherCollectionType(b);if(c.isCollection){this.setMapOver(c)}},attachable:function(b){var d=this._otherCollectionType(b);var a=this.mapOver();if(d.isCollection){if(this.multiple){if(d.rank==1){return this._producesAcceptableDatatype(b)}else{return false}}if(a.isCollection&&a.canMatch(d)){return this._producesAcceptableDatatype(b)}else{var c=this._mappingConstraints();if(c.every(_.bind(d.canMatch,d))){return this._producesAcceptableDatatype(b)}else{return false}}}else{if(a.isCollection){return false}}return this._producesAcceptableDatatype(b)}});var InputCollectionTerminal=BaseInputTerminal.extend({update:function(a){this.multiple=false;this.collection=true;this.datatypes=a.extensions;if(a.collection_type){this.collectionType=new CollectionTypeDescription(a.collection_type)}else{this.collectionType=ANY_COLLECTION_TYPE_DESCRIPTION}},connect:function(b){BaseInputTerminal.prototype.connect.call(this,b);var a=b.handle1;if(!a){return}var c=this._effectiveMapOver(a);this.setMapOver(c)},_effectiveMapOver:function(a){var b=this.collectionType;var c=this._otherCollectionType(a);if(!b.canMatch(c)){return c.effectiveMapOver(b)}else{return NULL_COLLECTION_TYPE_DESCRIPTION}},_effectiveCollectionType:function(){var b=this.collectionType;var a=this.mapOver();return a.append(b)},attachable:function(b){var g=this._otherCollectionType(b);if(g.isCollection){var f=this._effectiveCollectionType();var a=this.mapOver();if(f.canMatch(g)){return this._producesAcceptableDatatype(b)}else{if(a.isCollection){return false}else{if(g.canMapOver(this.collectionType)){var d=this._effectiveMapOver(b);if(!d.isCollection){return false}var c=this._mappingConstraints();if(c.every(d.canMatch)){return this._producesAcceptableDatatype(b)}}}}}return false}});var OutputCollectionTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes;this.collectionType=new CollectionTypeDescription(a.collection_type);this.isDataCollectionInput=true},update:function(a){var b=new CollectionTypeDescription(a.collection_type);if(b.collectionType!=this.collectionType.collectionType){_.each(this.connectors,function(c){c.destroy()})}this.collectionType=b}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},destroyIfInvalid:function(){if(this.handle1&&this.handle2&&!this.handle2.attachable(this.handle1)){this.destroy()}},redraw:function(){var f=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}f.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var v=function(c){return $(c).offset().left-f.offset().left};var p=function(c){return $(c).offset().top-f.offset().top};if(!this.handle1||!this.handle2){return}var o=v(this.handle1.element)+5;var n=p(this.handle1.element)+5;var x=v(this.handle2.element)+5;var u=p(this.handle2.element)+5;var k=100;var r=Math.min(o,x);var a=Math.max(o,x);var q=Math.min(n,u);var B=Math.max(n,u);var d=Math.min(Math.max(Math.abs(B-q)/2,100),300);var w=r-k;var A=q-k;var y=a-r+2*k;var s=B-q+2*k;this.canvas.style.left=w+"px";this.canvas.style.top=A+"px";this.canvas.setAttribute("width",y);this.canvas.setAttribute("height",s);o-=w;n-=A;x-=w;u-=A;var z=this.canvas.getContext("2d"),h=null,l=null;var g=1;if(this.handle1&&this.handle1.isMappedOver()){var h=[-6,-3,0,3,6];g=5}else{var h=[0]}if(this.handle2&&this.handle2.isMappedOver()){var l=[-6,-3,0,3,6];g=5}else{var l=[0]}var b=this;for(var t=0;t<g;t++){var m=5,j=7;if(h.length>1||l.length>1){m=1;j=3}b.draw_outlined_curve(o,n,x,u,d,m,j,h[t%h.length],l[t%l.length])}},draw_outlined_curve:function(j,i,l,k,a,b,f,g,d){var g=g||0;var d=d||0;var h=this.canvas.getContext("2d");h.lineCap="round";h.strokeStyle=this.outer_color;h.lineWidth=f;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke();h.strokeStyle=this.inner_color;h.lineWidth=b;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},connectedOutputTerminals:function(){var a=[];$.each(this.output_terminals,function(b,c){if(c.connectors.length>0){a.push(c)}});return a},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a){if(a[b].connectors.length>0){return true}}return false},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var c=this.input_terminals;for(var b in c){var a=c[b];if(a.connectors.length>0&&a.isMappedOver()){return true}}return false},_connectedMappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){if(d.connectors.length>0){a.push(d)}}});return a},hasMappedOverInputTerminals:function(){var a=false;_.each(this.input_terminals,function(b){var c=b.mapOver();if(c.isCollection){a=true}});return a},forceDisconnectOutputs:function(){_.each(this.output_terminals,function(a){a.disconnectAll()})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(b){if(b.type){this.type=b.type}this.name=b.name;this.form_html=b.form_html;this.tool_state=b.tool_state;this.tool_errors=b.tool_errors;this.tooltip=b.tooltip?b.tooltip:"";this.annotation=b.annotation;this.post_job_actions=b.post_job_actions?b.post_job_actions:{};this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var a=this;var c=new NodeView({el:this.element[0],node:a,});a.nodeView=c;$.each(b.data_inputs,function(f,d){c.addDataInput(d)});if((b.data_inputs.length>0)&&(b.data_outputs.length>0)){c.addRule()}$.each(b.data_outputs,function(f,d){c.addDataOutput(d)});c.render();workflow.node_changed(this)},update_field_data:function(d){var c=this;nodeView=c.nodeView;this.tool_state=d.tool_state;this.form_html=d.form_html;this.tool_errors=d.tool_errors;this.annotation=d.annotation;if("post_job_actions" in d){var f=$.parseJSON(d.post_job_actions);this.post_job_actions=f?f:{}}c.nodeView.renderToolErrors();var g=nodeView.$("div.inputs");var a=nodeView.newInputsDiv();var b={};_.each(d.data_inputs,function(h){var i=c.nodeView.addDataInput(h,a);b[h.name]=i});_.each(_.difference(_.values(nodeView.terminalViews),_.values(b)),function(h){h.el.terminal.destroy()});nodeView.terminalViews=b;if(d.data_outputs.length==1&&"collection_type" in d.data_outputs[0]){nodeView.updateDataOutput(d.data_outputs[0])}g.replaceWith(a);this.markChanged();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},markChanged:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node(f.type,f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if(!$.isArray(h)){h=[h]}$.each(h,function(k,j){var m=wf.nodes[j.id];var n=new Connector();n.connect(m.output_terminals[j.output_name],d.input_terminals[i]);n.redraw()})}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node({element:i});g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+galaxy_config.root+"static/images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<div>").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}function add_node(b,d,a){var c=prebuild_node(b,d,a);workflow.add_node(c);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview();workflow.activate_node(c);return c}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}var NodeView=Backbone.View.extend({initialize:function(a){this.node=a.node;this.output_width=Math.max(150,this.$el.width());this.tool_body=this.$el.find(".toolFormBody");this.tool_body.find("div").remove();this.newInputsDiv().appendTo(this.tool_body);this.terminalViews={};this.outputTerminlViews={}},render:function(){this.renderToolErrors();this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){if(this.node.tool_errors){this.$el.addClass("tool-node-error")}else{this.$el.removeClass("tool-node-error")}},newInputsDiv:function(){return $("<div class='inputs'></div>")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("<div class='rule'></div>"))},addDataInput:function(i,d){var j=true;if(!d){d=this.$(".inputs");j=false}var f=this.terminalViews[i.name];var h=(i.input_type=="dataset_collection")?InputCollectionTerminalView:InputTerminalView;if(f&&!(f instanceof h)){f.el.terminal.destroy();f=null}if(!f){f=new h({node:this.node,input:i})}else{var g=f.el.terminal;g.update(i);g.destroyInvalidConnections()}this.terminalViews[i.name]=f;var c=f.el;var b=new DataInputView({terminalElement:c,input:i,nodeView:this,skipResize:j});var a=b.$el;d.append(a.prepend(f.terminalElements()));return f},addDataOutput:function(a){var d=(a.collection_type)?OutputCollectionTerminalView:OutputTerminalView;var c=new d({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(c.terminalElements()))},updateDataOutput:function(b){var a=this.node.output_terminals[b.name];a.update(b)}});var DataInputView=Backbone.View.extend({className:"form-row dataRow input-data-row",initialize:function(a){this.input=a.input;this.nodeView=a.nodeView;this.terminalElement=a.terminalElement;this.$el.attr("name",this.input.name).html(this.input.label);if(!a.skipResize){this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth());this.$el.css({position:"",left:"",top:"",display:""});this.$el.remove()}},});var OutputCalloutView=Backbone.View.extend({tagName:"div",initialize:function(b){this.label=b.label;this.node=b.node;this.output=b.output;var a=this;this.$el.attr("class","callout "+this.label).css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(a.output.name,a.node.workflow_outputs)!=-1){a.node.workflow_outputs.splice($.inArray(a.output.name,a.node.workflow_outputs),1);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{a.node.workflow_outputs.push(a.output.name);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Mark dataset as a workflow output. All unmarked datasets will be hidden."});this.$el.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});this.$el.show();this.resetImage()},resetImage:function(){if($.inArray(this.output.name,this.node.workflow_outputs)===-1){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}},hoverImage:function(){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")}});var DataOutputView=Backbone.View.extend({className:"form-row dataRow",initialize:function(c){this.output=c.output;this.terminalElement=c.terminalElement;this.nodeView=c.nodeView;var a=this.output;var b=a.name;var f=this.nodeView.node;var d=a.extensions.indexOf("input")>=0||a.extensions.indexOf("input_collection")>=0;if(!d){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(f.type=="tool"){var g=new OutputCalloutView({label:b,output:a,node:f,});this.$el.append(g.el);this.$el.hover(function(){g.hoverImage()},function(){g.resetImage()})}this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth()+17);this.$el.css({position:"",left:"",top:"",display:""}).detach()}});var TerminalView=Backbone.View.extend({setupMappingView:function(b){var c=new this.terminalMappingClass({terminal:b});var a=new this.terminalMappingViewClass({model:c});a.render();b.terminalMappingView=a;this.terminalMappingView=a},terminalElements:function(){if(this.terminalMappingView){return[this.terminalMappingView.el,this.el]}else{return[this.el]}}});var BaseInputTerminalView=TerminalView.extend({className:"terminal input-terminal",initialize:function(c){var f=c.node;var a=c.input;var b=a.name;var d=this.terminalForInput(a);if(!d.multiple){this.setupMappingView(d)}this.el.terminal=d;d.node=f;d.name=b;f.input_terminals[b]=d},events:{dropinit:"onDropInit",dropstart:"onDropStart",dropend:"onDropEnd",drop:"onDrop",hover:"onHover",},onDropInit:function(b,c){var a=this.el.terminal;return $(c.drag).hasClass("output-terminal")&&a.canAccept(c.drag.terminal)},onDropStart:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#BBFFBB"}},onDropEnd:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#FFFFFF"}},onDrop:function(b,c){var a=this.el.terminal;new Connector(c.drag.terminal,a).redraw()},onHover:function(){var c=this.el;var b=c.terminal;if(b.connectors.length>0){var a=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='button'></div>").append($("<div/>").addClass("fa-icon-button fa fa-times").click(function(){$.each(b.connectors,function(f,d){if(d){d.destroy()}});a.remove()}))).bind("mouseleave",function(){$(this).remove()});a.css({top:$(c).offset().top-2,left:$(c).offset().left-a.width(),"padding-right":$(c).width()}).show()}},});var InputTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputTerminalMapping,terminalMappingViewClass:InputTerminalMappingView,terminalForInput:function(a){return new InputTerminal({element:this.el,input:a})},});var InputCollectionTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputCollectionTerminalMapping,terminalMappingViewClass:InputCollectionTerminalMappingView,terminalForInput:function(a){return new InputCollectionTerminal({element:this.el,input:a})},});var BaseOutputTerminalView=TerminalView.extend({className:"terminal output-terminal",initialize:function(c){var f=c.node;var a=c.output;var b=a.name;var d=this.terminalForOutput(a);this.setupMappingView(d);this.el.terminal=d;d.node=f;d.name=b;f.output_terminals[b]=d},events:{drag:"onDrag",dragstart:"onDragStart",dragend:"onDragEnd",},onDrag:function(b,c){var a=function(){var f=$(c.proxy).offsetParent().offset(),d=c.offsetX-f.left,g=c.offsetY-f.top;$(c.proxy).css({left:d,top:g});c.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};a();$("#canvas-container").get(0).scroll_panel.test(b,a)},onDragStart:function(b,f){$(f.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var a=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);a.terminal=new OutputTerminal({element:a});var g=new Connector();g.dragging=true;g.connect(this.el.terminal,a.terminal);return a},onDragEnd:function(a,b){b.proxy.terminal.connectors[0].destroy();$(b.proxy).remove();$(b.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()}});var OutputTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputTerminalMapping,terminalMappingViewClass:OutputTerminalMappingView,terminalForOutput:function(a){var c=a.extensions;var b=new OutputTerminal({element:this.el,datatypes:c});return b},});var OutputCollectionTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputCollectionTerminalMapping,terminalMappingViewClass:OutputCollectionTerminalMappingView,terminalForOutput:function(a){var c=a.collection_type;var b=new OutputCollectionTerminal({element:this.el,collection_type:c,datatypes:a.extensions});return b},});function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.tool_errors){i.fillStyle="#FFCCCC";i.strokeStyle="#AA6666"}else{if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
\ No newline at end of file
+function CollectionTypeDescription(a){this.collectionType=a;this.isCollection=true;this.rank=a.split(":").length}$.extend(CollectionTypeDescription.prototype,{append:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return this}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return otherCollectionType}return new CollectionTypeDescription(this.collectionType+":"+a.collectionType)},canMatch:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return true}return a.collectionType==this.collectionType},canMapOver:function(b){if(b===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(b===ANY_COLLECTION_TYPE_DESCRIPTION){return false}if(this.rank<=b.rank){return false}var a=b.collectionType;return this._endsWith(this.collectionType,a)},effectiveMapOver:function(a){var c=a.collectionType;var b=this.collectionType.substring(0,this.collectionType.length-c.length-1);return new CollectionTypeDescription(b)},equal:function(a){return a.collectionType==this.collectionType},toString:function(){return"CollectionType["+this.collectionType+"]"},_endsWith:function(b,a){return b.indexOf(a,b.length-a.length)!==-1}});NULL_COLLECTION_TYPE_DESCRIPTION={isCollection:false,canMatch:function(a){return false},canMapOver:function(a){return false},toString:function(){return"NullCollectionType[]"},append:function(a){return a},equal:function(a){return a===this}};ANY_COLLECTION_TYPE_DESCRIPTION={isCollection:true,canMatch:function(a){return NULL_COLLECTION_TYPE_DESCRIPTION!==a},canMapOver:function(a){return false},toString:function(){return"AnyCollectionType[]"},append:function(a){throw"Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"},equal:function(a){return a===this}};var TerminalMapping=Backbone.Model.extend({initialize:function(a){this.mapOver=a.mapOver||NULL_COLLECTION_TYPE_DESCRIPTION;this.terminal=a.terminal;this.terminal.terminalMapping=this},disableMapOver:function(){this.setMapOver(NULL_COLLECTION_TYPE_DESCRIPTION)},setMapOver:function(a){this.mapOver=a;this.trigger("change")}});var TerminalMappingView=Backbone.View.extend({tagName:"div",className:"fa-icon-button fa fa-folder-o",initialize:function(b){var a="Run tool in parallel over collection";this.$el.tooltip({delay:500,title:a});this.model.bind("change",_.bind(this.render,this))},render:function(){if(this.model.mapOver.isCollection){this.$el.show()}else{this.$el.hide()}},});var InputTerminalMappingView=TerminalMappingView.extend({events:{click:"onClick",mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",},onMouseEnter:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){this.$el.css("color","red")}},onMouseLeave:function(a){this.$el.css("color","black")},onClick:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){a.terminal.resetMapping()}},});var InputTerminalMapping=TerminalMapping;var InputCollectionTerminalMapping=TerminalMapping;var OutputTerminalMapping=TerminalMapping;var OutputTerminalMappingView=TerminalMappingView;var InputCollectionTerminalMappingView=InputTerminalMappingView;var OutputCollectionTerminalMapping=TerminalMapping;var OutputCollectionTerminalMappingView=TerminalMappingView;var Terminal=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.connectors=[]},connect:function(a){this.connectors.push(a);if(this.node){this.node.markChanged()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.markChanged();this.resetMappingIfNeeded()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})},destroyInvalidConnections:function(){_.each(this.connectors,function(a){a.destroyIfInvalid()})},setMapOver:function(a){if(this.multiple){return}if(!this.mapOver().equal(a)){this.terminalMapping.setMapOver(a);_.each(this.node.output_terminals,function(b){b.setMapOver(a)})}},mapOver:function(){if(!this.terminalMapping){return NULL_COLLECTION_TYPE_DESCRIPTION}else{return this.terminalMapping.mapOver}},isMappedOver:function(){return this.terminalMapping&&this.terminalMapping.mapOver.isCollection},resetMapping:function(){this.terminalMapping.disableMapOver()},resetMappingIfNeeded:function(){},});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes},resetMappingIfNeeded:function(){if(!this.node.hasConnectedOutputTerminals()&&!this.node.hasConnectedMappedInputTerminals()){_.each(this.node.mappedInputTerminals(),function(b){b.resetMappingIfNeeded()})}var a=!this.node.hasMappedOverInputTerminals();if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();_.each(this.connectors,function(a){var b=a.handle2;if(b){b.resetMappingIfNeeded();a.destroyIfInvalid()}})}});var BaseInputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.update(a.input)},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},resetMappingIfNeeded:function(){var b=this.mapOver();if(!b.isCollection){return}var a=this.node.hasConnectedMappedInputTerminals()||(!this.node.hasConnectedOutputTerminals());if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();if(!this.node.hasMappedOverInputTerminals()){_.each(this.node.output_terminals,function(a){a.resetMapping()})}},connected:function(){return this.connectors.length!==0},_inputFilled:function(){var a;if(!this.connected()){a=false}else{if(this.multiple){if(!this.connected()){a=false}else{var b=this.connectors[0].handle1;if(b===null){a=false}else{if(b.isDataCollectionInput||b.isMappedOver()||b.datatypes.indexOf("input_collection")>0){a=true}else{a=false}}}}else{a=true}}return a},_mappingConstraints:function(){if(!this.node){return[]}var b=this.mapOver();if(b.isCollection){return[b]}var a=[];if(!this.node.hasConnectedOutputTerminals()){_.each(this.node.connectedMappedInputTerminals(),function(c){a.push(c.mapOver())})}else{a.push(_.first(_.values(this.node.output_terminals)).mapOver())}return a},_producesAcceptableDatatype:function(a){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){var h=f[b];if(h=="input"||h=="input_collection"||issubtype(f[b],this.datatypes[c])){return true}}}return false},_otherCollectionType:function(a){var c=NULL_COLLECTION_TYPE_DESCRIPTION;if(a.isDataCollectionInput){c=a.collectionType}else{var b=a.mapOver();if(b.isCollection){c=b}}return c},});var InputTerminal=BaseInputTerminal.extend({update:function(a){this.datatypes=a.extensions;this.multiple=a.multiple;this.collection=false},connect:function(a){BaseInputTerminal.prototype.connect.call(this,a);var b=a.handle1;if(!b){return}var c=this._otherCollectionType(b);if(c.isCollection){this.setMapOver(c)}},attachable:function(b){var d=this._otherCollectionType(b);var a=this.mapOver();if(d.isCollection){if(this.multiple){if(this.connected()){return false}if(d.rank==1){return this._producesAcceptableDatatype(b)}else{return false}}if(a.isCollection&&a.canMatch(d)){return this._producesAcceptableDatatype(b)}else{var c=this._mappingConstraints();if(c.every(_.bind(d.canMatch,d))){return this._producesAcceptableDatatype(b)}else{return false}}}else{if(a.isCollection){return false}}return this._producesAcceptableDatatype(b)}});var InputCollectionTerminal=BaseInputTerminal.extend({update:function(a){this.multiple=false;this.collection=true;this.datatypes=a.extensions;if(a.collection_type){this.collectionType=new CollectionTypeDescription(a.collection_type)}else{this.collectionType=ANY_COLLECTION_TYPE_DESCRIPTION}},connect:function(b){BaseInputTerminal.prototype.connect.call(this,b);var a=b.handle1;if(!a){return}var c=this._effectiveMapOver(a);this.setMapOver(c)},_effectiveMapOver:function(a){var b=this.collectionType;var c=this._otherCollectionType(a);if(!b.canMatch(c)){return c.effectiveMapOver(b)}else{return NULL_COLLECTION_TYPE_DESCRIPTION}},_effectiveCollectionType:function(){var b=this.collectionType;var a=this.mapOver();return a.append(b)},attachable:function(b){var g=this._otherCollectionType(b);if(g.isCollection){var f=this._effectiveCollectionType();var a=this.mapOver();if(f.canMatch(g)){return this._producesAcceptableDatatype(b)}else{if(a.isCollection){return false}else{if(g.canMapOver(this.collectionType)){var d=this._effectiveMapOver(b);if(!d.isCollection){return false}var c=this._mappingConstraints();if(c.every(d.canMatch)){return this._producesAcceptableDatatype(b)}}}}}return false}});var OutputCollectionTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes;this.collectionType=new CollectionTypeDescription(a.collection_type);this.isDataCollectionInput=true},update:function(a){var b=new CollectionTypeDescription(a.collection_type);if(b.collectionType!=this.collectionType.collectionType){_.each(this.connectors,function(c){c.destroy()})}this.collectionType=b}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},destroyIfInvalid:function(){if(this.handle1&&this.handle2&&!this.handle2.attachable(this.handle1)){this.destroy()}},redraw:function(){var f=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}f.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var v=function(c){return $(c).offset().left-f.offset().left};var p=function(c){return $(c).offset().top-f.offset().top};if(!this.handle1||!this.handle2){return}var o=v(this.handle1.element)+5;var n=p(this.handle1.element)+5;var x=v(this.handle2.element)+5;var u=p(this.handle2.element)+5;var k=100;var r=Math.min(o,x);var a=Math.max(o,x);var q=Math.min(n,u);var B=Math.max(n,u);var d=Math.min(Math.max(Math.abs(B-q)/2,100),300);var w=r-k;var A=q-k;var y=a-r+2*k;var s=B-q+2*k;this.canvas.style.left=w+"px";this.canvas.style.top=A+"px";this.canvas.setAttribute("width",y);this.canvas.setAttribute("height",s);o-=w;n-=A;x-=w;u-=A;var z=this.canvas.getContext("2d"),h=null,l=null;var g=1;if(this.handle1&&this.handle1.isMappedOver()){var h=[-6,-3,0,3,6];g=5}else{var h=[0]}if(this.handle2&&this.handle2.isMappedOver()){var l=[-6,-3,0,3,6];g=5}else{var l=[0]}var b=this;for(var t=0;t<g;t++){var m=5,j=7;if(h.length>1||l.length>1){m=1;j=3}b.draw_outlined_curve(o,n,x,u,d,m,j,h[t%h.length],l[t%l.length])}},draw_outlined_curve:function(j,i,l,k,a,b,f,g,d){var g=g||0;var d=d||0;var h=this.canvas.getContext("2d");h.lineCap="round";h.strokeStyle=this.outer_color;h.lineWidth=f;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke();h.strokeStyle=this.inner_color;h.lineWidth=b;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},connectedOutputTerminals:function(){return this._connectedTerminals(this.output_terminals)},_connectedTerminals:function(b){var a=[];$.each(b,function(c,d){if(d.connectors.length>0){a.push(d)}});return a},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a){if(a[b].connectors.length>0){return true}}return false},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var c=this.input_terminals;for(var b in c){var a=c[b];if(a.connectors.length>0&&a.isMappedOver()){return true}}return false},_connectedMappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){if(d.connectors.length>0){a.push(d)}}});return a},mappedInputTerminals:function(){return this._mappedTerminals(this.input_terminals)},_mappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){a.push(d)}});return a},hasMappedOverInputTerminals:function(){var a=false;_.each(this.input_terminals,function(b){var c=b.mapOver();if(c.isCollection){a=true}});return a},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(b){if(b.type){this.type=b.type}this.name=b.name;this.form_html=b.form_html;this.tool_state=b.tool_state;this.tool_errors=b.tool_errors;this.tooltip=b.tooltip?b.tooltip:"";this.annotation=b.annotation;this.post_job_actions=b.post_job_actions?b.post_job_actions:{};this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var a=this;var c=new NodeView({el:this.element[0],node:a,});a.nodeView=c;$.each(b.data_inputs,function(f,d){c.addDataInput(d)});if((b.data_inputs.length>0)&&(b.data_outputs.length>0)){c.addRule()}$.each(b.data_outputs,function(f,d){c.addDataOutput(d)});c.render();workflow.node_changed(this)},update_field_data:function(d){var c=this;nodeView=c.nodeView;this.tool_state=d.tool_state;this.form_html=d.form_html;this.tool_errors=d.tool_errors;this.annotation=d.annotation;if("post_job_actions" in d){var f=$.parseJSON(d.post_job_actions);this.post_job_actions=f?f:{}}c.nodeView.renderToolErrors();var g=nodeView.$("div.inputs");var a=nodeView.newInputsDiv();var b={};_.each(d.data_inputs,function(h){var i=c.nodeView.addDataInput(h,a);b[h.name]=i});_.each(_.difference(_.values(nodeView.terminalViews),_.values(b)),function(h){h.el.terminal.destroy()});nodeView.terminalViews=b;if(d.data_outputs.length==1&&"collection_type" in d.data_outputs[0]){nodeView.updateDataOutput(d.data_outputs[0])}g.replaceWith(a);this.markChanged();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},markChanged:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node(f.type,f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if(!$.isArray(h)){h=[h]}$.each(h,function(k,j){var m=wf.nodes[j.id];var n=new Connector();n.connect(m.output_terminals[j.output_name],d.input_terminals[i]);n.redraw()})}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node({element:i});g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+galaxy_config.root+"static/images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<div>").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}function add_node(b,d,a){var c=prebuild_node(b,d,a);workflow.add_node(c);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview();workflow.activate_node(c);return c}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}var NodeView=Backbone.View.extend({initialize:function(a){this.node=a.node;this.output_width=Math.max(150,this.$el.width());this.tool_body=this.$el.find(".toolFormBody");this.tool_body.find("div").remove();this.newInputsDiv().appendTo(this.tool_body);this.terminalViews={};this.outputTerminlViews={}},render:function(){this.renderToolErrors();this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){if(this.node.tool_errors){this.$el.addClass("tool-node-error")}else{this.$el.removeClass("tool-node-error")}},newInputsDiv:function(){return $("<div class='inputs'></div>")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("<div class='rule'></div>"))},addDataInput:function(i,d){var j=true;if(!d){d=this.$(".inputs");j=false}var f=this.terminalViews[i.name];var h=(i.input_type=="dataset_collection")?InputCollectionTerminalView:InputTerminalView;if(f&&!(f instanceof h)){f.el.terminal.destroy();f=null}if(!f){f=new h({node:this.node,input:i})}else{var g=f.el.terminal;g.update(i);g.destroyInvalidConnections()}this.terminalViews[i.name]=f;var c=f.el;var b=new DataInputView({terminalElement:c,input:i,nodeView:this,skipResize:j});var a=b.$el;d.append(a.prepend(f.terminalElements()));return f},addDataOutput:function(a){var d=(a.collection_type)?OutputCollectionTerminalView:OutputTerminalView;var c=new d({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(c.terminalElements()))},updateDataOutput:function(b){var a=this.node.output_terminals[b.name];a.update(b)}});var DataInputView=Backbone.View.extend({className:"form-row dataRow input-data-row",initialize:function(a){this.input=a.input;this.nodeView=a.nodeView;this.terminalElement=a.terminalElement;this.$el.attr("name",this.input.name).html(this.input.label);if(!a.skipResize){this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth());this.$el.css({position:"",left:"",top:"",display:""});this.$el.remove()}},});var OutputCalloutView=Backbone.View.extend({tagName:"div",initialize:function(b){this.label=b.label;this.node=b.node;this.output=b.output;var a=this;this.$el.attr("class","callout "+this.label).css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(a.output.name,a.node.workflow_outputs)!=-1){a.node.workflow_outputs.splice($.inArray(a.output.name,a.node.workflow_outputs),1);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{a.node.workflow_outputs.push(a.output.name);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Mark dataset as a workflow output. All unmarked datasets will be hidden."});this.$el.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});this.$el.show();this.resetImage()},resetImage:function(){if($.inArray(this.output.name,this.node.workflow_outputs)===-1){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}},hoverImage:function(){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")}});var DataOutputView=Backbone.View.extend({className:"form-row dataRow",initialize:function(c){this.output=c.output;this.terminalElement=c.terminalElement;this.nodeView=c.nodeView;var a=this.output;var b=a.name;var f=this.nodeView.node;var d=a.extensions.indexOf("input")>=0||a.extensions.indexOf("input_collection")>=0;if(!d){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(f.type=="tool"){var g=new OutputCalloutView({label:b,output:a,node:f,});this.$el.append(g.el);this.$el.hover(function(){g.hoverImage()},function(){g.resetImage()})}this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth()+17);this.$el.css({position:"",left:"",top:"",display:""}).detach()}});var TerminalView=Backbone.View.extend({setupMappingView:function(b){var c=new this.terminalMappingClass({terminal:b});var a=new this.terminalMappingViewClass({model:c});a.render();b.terminalMappingView=a;this.terminalMappingView=a},terminalElements:function(){if(this.terminalMappingView){return[this.terminalMappingView.el,this.el]}else{return[this.el]}}});var BaseInputTerminalView=TerminalView.extend({className:"terminal input-terminal",initialize:function(c){var f=c.node;var a=c.input;var b=a.name;var d=this.terminalForInput(a);if(!d.multiple){this.setupMappingView(d)}this.el.terminal=d;d.node=f;d.name=b;f.input_terminals[b]=d},events:{dropinit:"onDropInit",dropstart:"onDropStart",dropend:"onDropEnd",drop:"onDrop",hover:"onHover",},onDropInit:function(b,c){var a=this.el.terminal;return $(c.drag).hasClass("output-terminal")&&a.canAccept(c.drag.terminal)},onDropStart:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#BBFFBB"}},onDropEnd:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#FFFFFF"}},onDrop:function(b,c){var a=this.el.terminal;new Connector(c.drag.terminal,a).redraw()},onHover:function(){var c=this.el;var b=c.terminal;if(b.connectors.length>0){var a=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='button'></div>").append($("<div/>").addClass("fa-icon-button fa fa-times").click(function(){$.each(b.connectors,function(f,d){if(d){d.destroy()}});a.remove()}))).bind("mouseleave",function(){$(this).remove()});a.css({top:$(c).offset().top-2,left:$(c).offset().left-a.width(),"padding-right":$(c).width()}).show()}},});var InputTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputTerminalMapping,terminalMappingViewClass:InputTerminalMappingView,terminalForInput:function(a){return new InputTerminal({element:this.el,input:a})},});var InputCollectionTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputCollectionTerminalMapping,terminalMappingViewClass:InputCollectionTerminalMappingView,terminalForInput:function(a){return new InputCollectionTerminal({element:this.el,input:a})},});var BaseOutputTerminalView=TerminalView.extend({className:"terminal output-terminal",initialize:function(c){var f=c.node;var a=c.output;var b=a.name;var d=this.terminalForOutput(a);this.setupMappingView(d);this.el.terminal=d;d.node=f;d.name=b;f.output_terminals[b]=d},events:{drag:"onDrag",dragstart:"onDragStart",dragend:"onDragEnd",},onDrag:function(b,c){var a=function(){var f=$(c.proxy).offsetParent().offset(),d=c.offsetX-f.left,g=c.offsetY-f.top;$(c.proxy).css({left:d,top:g});c.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};a();$("#canvas-container").get(0).scroll_panel.test(b,a)},onDragStart:function(b,f){$(f.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var a=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);a.terminal=new OutputTerminal({element:a});var g=new Connector();g.dragging=true;g.connect(this.el.terminal,a.terminal);return a},onDragEnd:function(a,b){b.proxy.terminal.connectors[0].destroy();$(b.proxy).remove();$(b.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()}});var OutputTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputTerminalMapping,terminalMappingViewClass:OutputTerminalMappingView,terminalForOutput:function(a){var c=a.extensions;var b=new OutputTerminal({element:this.el,datatypes:c});return b},});var OutputCollectionTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputCollectionTerminalMapping,terminalMappingViewClass:OutputCollectionTerminalMappingView,terminalForOutput:function(a){var c=a.collection_type;var b=new OutputCollectionTerminal({element:this.el,collection_type:c,datatypes:a.extensions});return b},});function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.tool_errors){i.fillStyle="#FFCCCC";i.strokeStyle="#AA6666"}else{if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
\ No newline at end of file
https://bitbucket.org/galaxy/galaxy-central/commits/fefcf6f47abe/
Changeset: fefcf6f47abe
User: jmchilton
Date: 2014-05-27 19:53:20
Summary: Merge next-stable.
Affected #: 6 files
diff -r 472fa62aaf72a1b7c8a2520665efdc833d2ab5fc -r fefcf6f47abe795b392348fbb901ffdf7b5c61ab lib/galaxy/tools/wrappers.py
--- a/lib/galaxy/tools/wrappers.py
+++ b/lib/galaxy/tools/wrappers.py
@@ -272,6 +272,12 @@
def is_collection( self ):
return True
+ def __getitem__( self, key ):
+ if isinstance( key, int ):
+ return self.element_instance_list[ key ]
+ else:
+ return self.element_instances[ key ]
+
def __getattr__( self, key ):
return self.element_instances[ key ]
diff -r 472fa62aaf72a1b7c8a2520665efdc833d2ab5fc -r fefcf6f47abe795b392348fbb901ffdf7b5c61ab static/scripts/galaxy.workflow_editor.canvas.js
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -133,11 +133,11 @@
onMouseEnter: function( e ) {
var model = this.model;
if( ! model.terminal.connected() && model.mapOver.isCollection ) {
- this.$el.color( "red" );
+ this.$el.css( "color", "red" );
}
},
onMouseLeave: function( e ) {
- this.$el.color( "blue" );
+ this.$el.css( "color", "black" );
},
onClick: function( e ) {
var model = this.model;
@@ -226,7 +226,16 @@
},
resetMappingIfNeeded: function( ) {
- if( ! this.node.hasMappedOverInputTerminals() ) {
+ // If inputs were only mapped over to preserve
+ // an output just disconnected reset these...
+ if( ! this.node.hasConnectedOutputTerminals() && ! this.node.hasConnectedMappedInputTerminals()){
+ _.each( this.node.mappedInputTerminals(), function( mappedInput ) {
+ mappedInput.resetMappingIfNeeded();
+ } );
+ }
+
+ var noMappedInputs = ! this.node.hasMappedOverInputTerminals();
+ if( noMappedInputs ) {
this.resetMapping();
}
},
@@ -398,6 +407,9 @@
var thisMapOver = this.mapOver();
if( otherCollectionType.isCollection ) {
if( this.multiple ) {
+ if( this.connected() ) {
+ return false;
+ }
if( otherCollectionType.rank == 1 ) {
return this._producesAcceptableDatatype( other );
} else {
@@ -651,13 +663,16 @@
this.tool_errors = {};
},
connectedOutputTerminals: function() {
- var connected_outputs = [];
- $.each( this.output_terminals, function( _, t ) {
+ return this._connectedTerminals( this.output_terminals );
+ },
+ _connectedTerminals: function( terminals ) {
+ var connectedTerminals = [];
+ $.each( terminals, function( _, t ) {
if( t.connectors.length > 0 ) {
- connected_outputs.push( t );
+ connectedTerminals.push( t );
}
} );
- return connected_outputs;
+ return connectedTerminals;
},
hasConnectedOutputTerminals: function() {
// return this.connectedOutputTerminals().length > 0; <- optimized this
@@ -683,9 +698,9 @@
}
return false;
},
- _connectedMappedTerminals: function( all_terminals ) {
+ _connectedMappedTerminals: function( terminals ) {
var mapped_outputs = [];
- $.each( all_terminals, function( _, t ) {
+ $.each( terminals, function( _, t ) {
var mapOver = t.mapOver();
if( mapOver.isCollection ) {
if( t.connectors.length > 0 ) {
@@ -695,6 +710,19 @@
});
return mapped_outputs;
},
+ mappedInputTerminals: function() {
+ return this._mappedTerminals( this.input_terminals );
+ },
+ _mappedTerminals: function( terminals ) {
+ var mappedTerminals = [];
+ $.each( terminals, function( _, t ) {
+ var mapOver = t.mapOver();
+ if( mapOver.isCollection ) {
+ mappedTerminals.push( t );
+ }
+ } );
+ return mappedTerminals;
+ },
hasMappedOverInputTerminals: function() {
var found = false;
_.each( this.input_terminals, function( t ) {
@@ -705,11 +733,6 @@
} );
return found;
},
- forceDisconnectOutputs: function() {
- _.each( this.output_terminals, function( terminal ) {
- terminal.disconnectAll();
- } );
- },
redraw : function () {
$.each( this.input_terminals, function( _, t ) {
t.redraw();
diff -r 472fa62aaf72a1b7c8a2520665efdc833d2ab5fc -r fefcf6f47abe795b392348fbb901ffdf7b5c61ab static/scripts/packed/galaxy.workflow_editor.canvas.js
--- a/static/scripts/packed/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/packed/galaxy.workflow_editor.canvas.js
@@ -1,1 +1,1 @@
-function CollectionTypeDescription(a){this.collectionType=a;this.isCollection=true;this.rank=a.split(":").length}$.extend(CollectionTypeDescription.prototype,{append:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return this}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return otherCollectionType}return new CollectionTypeDescription(this.collectionType+":"+a.collectionType)},canMatch:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return true}return a.collectionType==this.collectionType},canMapOver:function(b){if(b===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(b===ANY_COLLECTION_TYPE_DESCRIPTION){return false}if(this.rank<=b.rank){return false}var a=b.collectionType;return this._endsWith(this.collectionType,a)},effectiveMapOver:function(a){var c=a.collectionType;var b=this.collectionType.substring(0,this.collectionType.length-c.length-1);return new CollectionTypeDescription(b)},equal:function(a){return a.collectionType==this.collectionType},toString:function(){return"CollectionType["+this.collectionType+"]"},_endsWith:function(b,a){return b.indexOf(a,b.length-a.length)!==-1}});NULL_COLLECTION_TYPE_DESCRIPTION={isCollection:false,canMatch:function(a){return false},canMapOver:function(a){return false},toString:function(){return"NullCollectionType[]"},append:function(a){return a},equal:function(a){return a===this}};ANY_COLLECTION_TYPE_DESCRIPTION={isCollection:true,canMatch:function(a){return NULL_COLLECTION_TYPE_DESCRIPTION!==a},canMapOver:function(a){return false},toString:function(){return"AnyCollectionType[]"},append:function(a){throw"Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"},equal:function(a){return a===this}};var TerminalMapping=Backbone.Model.extend({initialize:function(a){this.mapOver=a.mapOver||NULL_COLLECTION_TYPE_DESCRIPTION;this.terminal=a.terminal;this.terminal.terminalMapping=this},disableMapOver:function(){this.setMapOver(NULL_COLLECTION_TYPE_DESCRIPTION)},setMapOver:function(a){this.mapOver=a;this.trigger("change")}});var TerminalMappingView=Backbone.View.extend({tagName:"div",className:"fa-icon-button fa fa-folder-o",initialize:function(b){var a="Run tool in parallel over collection";this.$el.tooltip({delay:500,title:a});this.model.bind("change",_.bind(this.render,this))},render:function(){if(this.model.mapOver.isCollection){this.$el.show()}else{this.$el.hide()}},});var InputTerminalMappingView=TerminalMappingView.extend({events:{click:"onClick",mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",},onMouseEnter:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){this.$el.color("red")}},onMouseLeave:function(a){this.$el.color("blue")},onClick:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){a.terminal.resetMapping()}},});var InputTerminalMapping=TerminalMapping;var InputCollectionTerminalMapping=TerminalMapping;var OutputTerminalMapping=TerminalMapping;var OutputTerminalMappingView=TerminalMappingView;var InputCollectionTerminalMappingView=InputTerminalMappingView;var OutputCollectionTerminalMapping=TerminalMapping;var OutputCollectionTerminalMappingView=TerminalMappingView;var Terminal=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.connectors=[]},connect:function(a){this.connectors.push(a);if(this.node){this.node.markChanged()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.markChanged();this.resetMappingIfNeeded()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})},destroyInvalidConnections:function(){_.each(this.connectors,function(a){a.destroyIfInvalid()})},setMapOver:function(a){if(this.multiple){return}if(!this.mapOver().equal(a)){this.terminalMapping.setMapOver(a);_.each(this.node.output_terminals,function(b){b.setMapOver(a)})}},mapOver:function(){if(!this.terminalMapping){return NULL_COLLECTION_TYPE_DESCRIPTION}else{return this.terminalMapping.mapOver}},isMappedOver:function(){return this.terminalMapping&&this.terminalMapping.mapOver.isCollection},resetMapping:function(){this.terminalMapping.disableMapOver()},resetMappingIfNeeded:function(){},});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes},resetMappingIfNeeded:function(){if(!this.node.hasMappedOverInputTerminals()){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();_.each(this.connectors,function(a){var b=a.handle2;if(b){b.resetMappingIfNeeded();a.destroyIfInvalid()}})}});var BaseInputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.update(a.input)},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},resetMappingIfNeeded:function(){var b=this.mapOver();if(!b.isCollection){return}var a=this.node.hasConnectedMappedInputTerminals()||(!this.node.hasConnectedOutputTerminals());if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();if(!this.node.hasMappedOverInputTerminals()){_.each(this.node.output_terminals,function(a){a.resetMapping()})}},connected:function(){return this.connectors.length!==0},_inputFilled:function(){var a;if(!this.connected()){a=false}else{if(this.multiple){if(!this.connected()){a=false}else{var b=this.connectors[0].handle1;if(b===null){a=false}else{if(b.isDataCollectionInput||b.isMappedOver()||b.datatypes.indexOf("input_collection")>0){a=true}else{a=false}}}}else{a=true}}return a},_mappingConstraints:function(){if(!this.node){return[]}var b=this.mapOver();if(b.isCollection){return[b]}var a=[];if(!this.node.hasConnectedOutputTerminals()){_.each(this.node.connectedMappedInputTerminals(),function(c){a.push(c.mapOver())})}else{a.push(_.first(_.values(this.node.output_terminals)).mapOver())}return a},_producesAcceptableDatatype:function(a){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){var h=f[b];if(h=="input"||h=="input_collection"||issubtype(f[b],this.datatypes[c])){return true}}}return false},_otherCollectionType:function(a){var c=NULL_COLLECTION_TYPE_DESCRIPTION;if(a.isDataCollectionInput){c=a.collectionType}else{var b=a.mapOver();if(b.isCollection){c=b}}return c},});var InputTerminal=BaseInputTerminal.extend({update:function(a){this.datatypes=a.extensions;this.multiple=a.multiple;this.collection=false},connect:function(a){BaseInputTerminal.prototype.connect.call(this,a);var b=a.handle1;if(!b){return}var c=this._otherCollectionType(b);if(c.isCollection){this.setMapOver(c)}},attachable:function(b){var d=this._otherCollectionType(b);var a=this.mapOver();if(d.isCollection){if(this.multiple){if(d.rank==1){return this._producesAcceptableDatatype(b)}else{return false}}if(a.isCollection&&a.canMatch(d)){return this._producesAcceptableDatatype(b)}else{var c=this._mappingConstraints();if(c.every(_.bind(d.canMatch,d))){return this._producesAcceptableDatatype(b)}else{return false}}}else{if(a.isCollection){return false}}return this._producesAcceptableDatatype(b)}});var InputCollectionTerminal=BaseInputTerminal.extend({update:function(a){this.multiple=false;this.collection=true;this.datatypes=a.extensions;if(a.collection_type){this.collectionType=new CollectionTypeDescription(a.collection_type)}else{this.collectionType=ANY_COLLECTION_TYPE_DESCRIPTION}},connect:function(b){BaseInputTerminal.prototype.connect.call(this,b);var a=b.handle1;if(!a){return}var c=this._effectiveMapOver(a);this.setMapOver(c)},_effectiveMapOver:function(a){var b=this.collectionType;var c=this._otherCollectionType(a);if(!b.canMatch(c)){return c.effectiveMapOver(b)}else{return NULL_COLLECTION_TYPE_DESCRIPTION}},_effectiveCollectionType:function(){var b=this.collectionType;var a=this.mapOver();return a.append(b)},attachable:function(b){var g=this._otherCollectionType(b);if(g.isCollection){var f=this._effectiveCollectionType();var a=this.mapOver();if(f.canMatch(g)){return this._producesAcceptableDatatype(b)}else{if(a.isCollection){return false}else{if(g.canMapOver(this.collectionType)){var d=this._effectiveMapOver(b);if(!d.isCollection){return false}var c=this._mappingConstraints();if(c.every(d.canMatch)){return this._producesAcceptableDatatype(b)}}}}}return false}});var OutputCollectionTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes;this.collectionType=new CollectionTypeDescription(a.collection_type);this.isDataCollectionInput=true},update:function(a){var b=new CollectionTypeDescription(a.collection_type);if(b.collectionType!=this.collectionType.collectionType){_.each(this.connectors,function(c){c.destroy()})}this.collectionType=b}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},destroyIfInvalid:function(){if(this.handle1&&this.handle2&&!this.handle2.attachable(this.handle1)){this.destroy()}},redraw:function(){var f=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}f.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var v=function(c){return $(c).offset().left-f.offset().left};var p=function(c){return $(c).offset().top-f.offset().top};if(!this.handle1||!this.handle2){return}var o=v(this.handle1.element)+5;var n=p(this.handle1.element)+5;var x=v(this.handle2.element)+5;var u=p(this.handle2.element)+5;var k=100;var r=Math.min(o,x);var a=Math.max(o,x);var q=Math.min(n,u);var B=Math.max(n,u);var d=Math.min(Math.max(Math.abs(B-q)/2,100),300);var w=r-k;var A=q-k;var y=a-r+2*k;var s=B-q+2*k;this.canvas.style.left=w+"px";this.canvas.style.top=A+"px";this.canvas.setAttribute("width",y);this.canvas.setAttribute("height",s);o-=w;n-=A;x-=w;u-=A;var z=this.canvas.getContext("2d"),h=null,l=null;var g=1;if(this.handle1&&this.handle1.isMappedOver()){var h=[-6,-3,0,3,6];g=5}else{var h=[0]}if(this.handle2&&this.handle2.isMappedOver()){var l=[-6,-3,0,3,6];g=5}else{var l=[0]}var b=this;for(var t=0;t<g;t++){var m=5,j=7;if(h.length>1||l.length>1){m=1;j=3}b.draw_outlined_curve(o,n,x,u,d,m,j,h[t%h.length],l[t%l.length])}},draw_outlined_curve:function(j,i,l,k,a,b,f,g,d){var g=g||0;var d=d||0;var h=this.canvas.getContext("2d");h.lineCap="round";h.strokeStyle=this.outer_color;h.lineWidth=f;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke();h.strokeStyle=this.inner_color;h.lineWidth=b;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},connectedOutputTerminals:function(){var a=[];$.each(this.output_terminals,function(b,c){if(c.connectors.length>0){a.push(c)}});return a},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a){if(a[b].connectors.length>0){return true}}return false},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var c=this.input_terminals;for(var b in c){var a=c[b];if(a.connectors.length>0&&a.isMappedOver()){return true}}return false},_connectedMappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){if(d.connectors.length>0){a.push(d)}}});return a},hasMappedOverInputTerminals:function(){var a=false;_.each(this.input_terminals,function(b){var c=b.mapOver();if(c.isCollection){a=true}});return a},forceDisconnectOutputs:function(){_.each(this.output_terminals,function(a){a.disconnectAll()})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(b){if(b.type){this.type=b.type}this.name=b.name;this.form_html=b.form_html;this.tool_state=b.tool_state;this.tool_errors=b.tool_errors;this.tooltip=b.tooltip?b.tooltip:"";this.annotation=b.annotation;this.post_job_actions=b.post_job_actions?b.post_job_actions:{};this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var a=this;var c=new NodeView({el:this.element[0],node:a,});a.nodeView=c;$.each(b.data_inputs,function(f,d){c.addDataInput(d)});if((b.data_inputs.length>0)&&(b.data_outputs.length>0)){c.addRule()}$.each(b.data_outputs,function(f,d){c.addDataOutput(d)});c.render();workflow.node_changed(this)},update_field_data:function(d){var c=this;nodeView=c.nodeView;this.tool_state=d.tool_state;this.form_html=d.form_html;this.tool_errors=d.tool_errors;this.annotation=d.annotation;if("post_job_actions" in d){var f=$.parseJSON(d.post_job_actions);this.post_job_actions=f?f:{}}c.nodeView.renderToolErrors();var g=nodeView.$("div.inputs");var a=nodeView.newInputsDiv();var b={};_.each(d.data_inputs,function(h){var i=c.nodeView.addDataInput(h,a);b[h.name]=i});_.each(_.difference(_.values(nodeView.terminalViews),_.values(b)),function(h){h.el.terminal.destroy()});nodeView.terminalViews=b;if(d.data_outputs.length==1&&"collection_type" in d.data_outputs[0]){nodeView.updateDataOutput(d.data_outputs[0])}g.replaceWith(a);this.markChanged();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},markChanged:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node(f.type,f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if(!$.isArray(h)){h=[h]}$.each(h,function(k,j){var m=wf.nodes[j.id];var n=new Connector();n.connect(m.output_terminals[j.output_name],d.input_terminals[i]);n.redraw()})}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node({element:i});g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+galaxy_config.root+"static/images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<div>").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}function add_node(b,d,a){var c=prebuild_node(b,d,a);workflow.add_node(c);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview();workflow.activate_node(c);return c}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}var NodeView=Backbone.View.extend({initialize:function(a){this.node=a.node;this.output_width=Math.max(150,this.$el.width());this.tool_body=this.$el.find(".toolFormBody");this.tool_body.find("div").remove();this.newInputsDiv().appendTo(this.tool_body);this.terminalViews={};this.outputTerminlViews={}},render:function(){this.renderToolErrors();this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){if(this.node.tool_errors){this.$el.addClass("tool-node-error")}else{this.$el.removeClass("tool-node-error")}},newInputsDiv:function(){return $("<div class='inputs'></div>")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("<div class='rule'></div>"))},addDataInput:function(i,d){var j=true;if(!d){d=this.$(".inputs");j=false}var f=this.terminalViews[i.name];var h=(i.input_type=="dataset_collection")?InputCollectionTerminalView:InputTerminalView;if(f&&!(f instanceof h)){f.el.terminal.destroy();f=null}if(!f){f=new h({node:this.node,input:i})}else{var g=f.el.terminal;g.update(i);g.destroyInvalidConnections()}this.terminalViews[i.name]=f;var c=f.el;var b=new DataInputView({terminalElement:c,input:i,nodeView:this,skipResize:j});var a=b.$el;d.append(a.prepend(f.terminalElements()));return f},addDataOutput:function(a){var d=(a.collection_type)?OutputCollectionTerminalView:OutputTerminalView;var c=new d({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(c.terminalElements()))},updateDataOutput:function(b){var a=this.node.output_terminals[b.name];a.update(b)}});var DataInputView=Backbone.View.extend({className:"form-row dataRow input-data-row",initialize:function(a){this.input=a.input;this.nodeView=a.nodeView;this.terminalElement=a.terminalElement;this.$el.attr("name",this.input.name).html(this.input.label);if(!a.skipResize){this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth());this.$el.css({position:"",left:"",top:"",display:""});this.$el.remove()}},});var OutputCalloutView=Backbone.View.extend({tagName:"div",initialize:function(b){this.label=b.label;this.node=b.node;this.output=b.output;var a=this;this.$el.attr("class","callout "+this.label).css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(a.output.name,a.node.workflow_outputs)!=-1){a.node.workflow_outputs.splice($.inArray(a.output.name,a.node.workflow_outputs),1);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{a.node.workflow_outputs.push(a.output.name);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Mark dataset as a workflow output. All unmarked datasets will be hidden."});this.$el.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});this.$el.show();this.resetImage()},resetImage:function(){if($.inArray(this.output.name,this.node.workflow_outputs)===-1){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}},hoverImage:function(){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")}});var DataOutputView=Backbone.View.extend({className:"form-row dataRow",initialize:function(c){this.output=c.output;this.terminalElement=c.terminalElement;this.nodeView=c.nodeView;var a=this.output;var b=a.name;var f=this.nodeView.node;var d=a.extensions.indexOf("input")>=0||a.extensions.indexOf("input_collection")>=0;if(!d){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(f.type=="tool"){var g=new OutputCalloutView({label:b,output:a,node:f,});this.$el.append(g.el);this.$el.hover(function(){g.hoverImage()},function(){g.resetImage()})}this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth()+17);this.$el.css({position:"",left:"",top:"",display:""}).detach()}});var TerminalView=Backbone.View.extend({setupMappingView:function(b){var c=new this.terminalMappingClass({terminal:b});var a=new this.terminalMappingViewClass({model:c});a.render();b.terminalMappingView=a;this.terminalMappingView=a},terminalElements:function(){if(this.terminalMappingView){return[this.terminalMappingView.el,this.el]}else{return[this.el]}}});var BaseInputTerminalView=TerminalView.extend({className:"terminal input-terminal",initialize:function(c){var f=c.node;var a=c.input;var b=a.name;var d=this.terminalForInput(a);if(!d.multiple){this.setupMappingView(d)}this.el.terminal=d;d.node=f;d.name=b;f.input_terminals[b]=d},events:{dropinit:"onDropInit",dropstart:"onDropStart",dropend:"onDropEnd",drop:"onDrop",hover:"onHover",},onDropInit:function(b,c){var a=this.el.terminal;return $(c.drag).hasClass("output-terminal")&&a.canAccept(c.drag.terminal)},onDropStart:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#BBFFBB"}},onDropEnd:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#FFFFFF"}},onDrop:function(b,c){var a=this.el.terminal;new Connector(c.drag.terminal,a).redraw()},onHover:function(){var c=this.el;var b=c.terminal;if(b.connectors.length>0){var a=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='button'></div>").append($("<div/>").addClass("fa-icon-button fa fa-times").click(function(){$.each(b.connectors,function(f,d){if(d){d.destroy()}});a.remove()}))).bind("mouseleave",function(){$(this).remove()});a.css({top:$(c).offset().top-2,left:$(c).offset().left-a.width(),"padding-right":$(c).width()}).show()}},});var InputTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputTerminalMapping,terminalMappingViewClass:InputTerminalMappingView,terminalForInput:function(a){return new InputTerminal({element:this.el,input:a})},});var InputCollectionTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputCollectionTerminalMapping,terminalMappingViewClass:InputCollectionTerminalMappingView,terminalForInput:function(a){return new InputCollectionTerminal({element:this.el,input:a})},});var BaseOutputTerminalView=TerminalView.extend({className:"terminal output-terminal",initialize:function(c){var f=c.node;var a=c.output;var b=a.name;var d=this.terminalForOutput(a);this.setupMappingView(d);this.el.terminal=d;d.node=f;d.name=b;f.output_terminals[b]=d},events:{drag:"onDrag",dragstart:"onDragStart",dragend:"onDragEnd",},onDrag:function(b,c){var a=function(){var f=$(c.proxy).offsetParent().offset(),d=c.offsetX-f.left,g=c.offsetY-f.top;$(c.proxy).css({left:d,top:g});c.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};a();$("#canvas-container").get(0).scroll_panel.test(b,a)},onDragStart:function(b,f){$(f.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var a=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);a.terminal=new OutputTerminal({element:a});var g=new Connector();g.dragging=true;g.connect(this.el.terminal,a.terminal);return a},onDragEnd:function(a,b){b.proxy.terminal.connectors[0].destroy();$(b.proxy).remove();$(b.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()}});var OutputTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputTerminalMapping,terminalMappingViewClass:OutputTerminalMappingView,terminalForOutput:function(a){var c=a.extensions;var b=new OutputTerminal({element:this.el,datatypes:c});return b},});var OutputCollectionTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputCollectionTerminalMapping,terminalMappingViewClass:OutputCollectionTerminalMappingView,terminalForOutput:function(a){var c=a.collection_type;var b=new OutputCollectionTerminal({element:this.el,collection_type:c,datatypes:a.extensions});return b},});function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.tool_errors){i.fillStyle="#FFCCCC";i.strokeStyle="#AA6666"}else{if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
\ No newline at end of file
+function CollectionTypeDescription(a){this.collectionType=a;this.isCollection=true;this.rank=a.split(":").length}$.extend(CollectionTypeDescription.prototype,{append:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return this}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return otherCollectionType}return new CollectionTypeDescription(this.collectionType+":"+a.collectionType)},canMatch:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return true}return a.collectionType==this.collectionType},canMapOver:function(b){if(b===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(b===ANY_COLLECTION_TYPE_DESCRIPTION){return false}if(this.rank<=b.rank){return false}var a=b.collectionType;return this._endsWith(this.collectionType,a)},effectiveMapOver:function(a){var c=a.collectionType;var b=this.collectionType.substring(0,this.collectionType.length-c.length-1);return new CollectionTypeDescription(b)},equal:function(a){return a.collectionType==this.collectionType},toString:function(){return"CollectionType["+this.collectionType+"]"},_endsWith:function(b,a){return b.indexOf(a,b.length-a.length)!==-1}});NULL_COLLECTION_TYPE_DESCRIPTION={isCollection:false,canMatch:function(a){return false},canMapOver:function(a){return false},toString:function(){return"NullCollectionType[]"},append:function(a){return a},equal:function(a){return a===this}};ANY_COLLECTION_TYPE_DESCRIPTION={isCollection:true,canMatch:function(a){return NULL_COLLECTION_TYPE_DESCRIPTION!==a},canMapOver:function(a){return false},toString:function(){return"AnyCollectionType[]"},append:function(a){throw"Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"},equal:function(a){return a===this}};var TerminalMapping=Backbone.Model.extend({initialize:function(a){this.mapOver=a.mapOver||NULL_COLLECTION_TYPE_DESCRIPTION;this.terminal=a.terminal;this.terminal.terminalMapping=this},disableMapOver:function(){this.setMapOver(NULL_COLLECTION_TYPE_DESCRIPTION)},setMapOver:function(a){this.mapOver=a;this.trigger("change")}});var TerminalMappingView=Backbone.View.extend({tagName:"div",className:"fa-icon-button fa fa-folder-o",initialize:function(b){var a="Run tool in parallel over collection";this.$el.tooltip({delay:500,title:a});this.model.bind("change",_.bind(this.render,this))},render:function(){if(this.model.mapOver.isCollection){this.$el.show()}else{this.$el.hide()}},});var InputTerminalMappingView=TerminalMappingView.extend({events:{click:"onClick",mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",},onMouseEnter:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){this.$el.css("color","red")}},onMouseLeave:function(a){this.$el.css("color","black")},onClick:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){a.terminal.resetMapping()}},});var InputTerminalMapping=TerminalMapping;var InputCollectionTerminalMapping=TerminalMapping;var OutputTerminalMapping=TerminalMapping;var OutputTerminalMappingView=TerminalMappingView;var InputCollectionTerminalMappingView=InputTerminalMappingView;var OutputCollectionTerminalMapping=TerminalMapping;var OutputCollectionTerminalMappingView=TerminalMappingView;var Terminal=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.connectors=[]},connect:function(a){this.connectors.push(a);if(this.node){this.node.markChanged()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.markChanged();this.resetMappingIfNeeded()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})},destroyInvalidConnections:function(){_.each(this.connectors,function(a){a.destroyIfInvalid()})},setMapOver:function(a){if(this.multiple){return}if(!this.mapOver().equal(a)){this.terminalMapping.setMapOver(a);_.each(this.node.output_terminals,function(b){b.setMapOver(a)})}},mapOver:function(){if(!this.terminalMapping){return NULL_COLLECTION_TYPE_DESCRIPTION}else{return this.terminalMapping.mapOver}},isMappedOver:function(){return this.terminalMapping&&this.terminalMapping.mapOver.isCollection},resetMapping:function(){this.terminalMapping.disableMapOver()},resetMappingIfNeeded:function(){},});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes},resetMappingIfNeeded:function(){if(!this.node.hasConnectedOutputTerminals()&&!this.node.hasConnectedMappedInputTerminals()){_.each(this.node.mappedInputTerminals(),function(b){b.resetMappingIfNeeded()})}var a=!this.node.hasMappedOverInputTerminals();if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();_.each(this.connectors,function(a){var b=a.handle2;if(b){b.resetMappingIfNeeded();a.destroyIfInvalid()}})}});var BaseInputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.update(a.input)},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},resetMappingIfNeeded:function(){var b=this.mapOver();if(!b.isCollection){return}var a=this.node.hasConnectedMappedInputTerminals()||(!this.node.hasConnectedOutputTerminals());if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();if(!this.node.hasMappedOverInputTerminals()){_.each(this.node.output_terminals,function(a){a.resetMapping()})}},connected:function(){return this.connectors.length!==0},_inputFilled:function(){var a;if(!this.connected()){a=false}else{if(this.multiple){if(!this.connected()){a=false}else{var b=this.connectors[0].handle1;if(b===null){a=false}else{if(b.isDataCollectionInput||b.isMappedOver()||b.datatypes.indexOf("input_collection")>0){a=true}else{a=false}}}}else{a=true}}return a},_mappingConstraints:function(){if(!this.node){return[]}var b=this.mapOver();if(b.isCollection){return[b]}var a=[];if(!this.node.hasConnectedOutputTerminals()){_.each(this.node.connectedMappedInputTerminals(),function(c){a.push(c.mapOver())})}else{a.push(_.first(_.values(this.node.output_terminals)).mapOver())}return a},_producesAcceptableDatatype:function(a){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){var h=f[b];if(h=="input"||h=="input_collection"||issubtype(f[b],this.datatypes[c])){return true}}}return false},_otherCollectionType:function(a){var c=NULL_COLLECTION_TYPE_DESCRIPTION;if(a.isDataCollectionInput){c=a.collectionType}else{var b=a.mapOver();if(b.isCollection){c=b}}return c},});var InputTerminal=BaseInputTerminal.extend({update:function(a){this.datatypes=a.extensions;this.multiple=a.multiple;this.collection=false},connect:function(a){BaseInputTerminal.prototype.connect.call(this,a);var b=a.handle1;if(!b){return}var c=this._otherCollectionType(b);if(c.isCollection){this.setMapOver(c)}},attachable:function(b){var d=this._otherCollectionType(b);var a=this.mapOver();if(d.isCollection){if(this.multiple){if(this.connected()){return false}if(d.rank==1){return this._producesAcceptableDatatype(b)}else{return false}}if(a.isCollection&&a.canMatch(d)){return this._producesAcceptableDatatype(b)}else{var c=this._mappingConstraints();if(c.every(_.bind(d.canMatch,d))){return this._producesAcceptableDatatype(b)}else{return false}}}else{if(a.isCollection){return false}}return this._producesAcceptableDatatype(b)}});var InputCollectionTerminal=BaseInputTerminal.extend({update:function(a){this.multiple=false;this.collection=true;this.datatypes=a.extensions;if(a.collection_type){this.collectionType=new CollectionTypeDescription(a.collection_type)}else{this.collectionType=ANY_COLLECTION_TYPE_DESCRIPTION}},connect:function(b){BaseInputTerminal.prototype.connect.call(this,b);var a=b.handle1;if(!a){return}var c=this._effectiveMapOver(a);this.setMapOver(c)},_effectiveMapOver:function(a){var b=this.collectionType;var c=this._otherCollectionType(a);if(!b.canMatch(c)){return c.effectiveMapOver(b)}else{return NULL_COLLECTION_TYPE_DESCRIPTION}},_effectiveCollectionType:function(){var b=this.collectionType;var a=this.mapOver();return a.append(b)},attachable:function(b){var g=this._otherCollectionType(b);if(g.isCollection){var f=this._effectiveCollectionType();var a=this.mapOver();if(f.canMatch(g)){return this._producesAcceptableDatatype(b)}else{if(a.isCollection){return false}else{if(g.canMapOver(this.collectionType)){var d=this._effectiveMapOver(b);if(!d.isCollection){return false}var c=this._mappingConstraints();if(c.every(d.canMatch)){return this._producesAcceptableDatatype(b)}}}}}return false}});var OutputCollectionTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes;this.collectionType=new CollectionTypeDescription(a.collection_type);this.isDataCollectionInput=true},update:function(a){var b=new CollectionTypeDescription(a.collection_type);if(b.collectionType!=this.collectionType.collectionType){_.each(this.connectors,function(c){c.destroy()})}this.collectionType=b}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},destroyIfInvalid:function(){if(this.handle1&&this.handle2&&!this.handle2.attachable(this.handle1)){this.destroy()}},redraw:function(){var f=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}f.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var v=function(c){return $(c).offset().left-f.offset().left};var p=function(c){return $(c).offset().top-f.offset().top};if(!this.handle1||!this.handle2){return}var o=v(this.handle1.element)+5;var n=p(this.handle1.element)+5;var x=v(this.handle2.element)+5;var u=p(this.handle2.element)+5;var k=100;var r=Math.min(o,x);var a=Math.max(o,x);var q=Math.min(n,u);var B=Math.max(n,u);var d=Math.min(Math.max(Math.abs(B-q)/2,100),300);var w=r-k;var A=q-k;var y=a-r+2*k;var s=B-q+2*k;this.canvas.style.left=w+"px";this.canvas.style.top=A+"px";this.canvas.setAttribute("width",y);this.canvas.setAttribute("height",s);o-=w;n-=A;x-=w;u-=A;var z=this.canvas.getContext("2d"),h=null,l=null;var g=1;if(this.handle1&&this.handle1.isMappedOver()){var h=[-6,-3,0,3,6];g=5}else{var h=[0]}if(this.handle2&&this.handle2.isMappedOver()){var l=[-6,-3,0,3,6];g=5}else{var l=[0]}var b=this;for(var t=0;t<g;t++){var m=5,j=7;if(h.length>1||l.length>1){m=1;j=3}b.draw_outlined_curve(o,n,x,u,d,m,j,h[t%h.length],l[t%l.length])}},draw_outlined_curve:function(j,i,l,k,a,b,f,g,d){var g=g||0;var d=d||0;var h=this.canvas.getContext("2d");h.lineCap="round";h.strokeStyle=this.outer_color;h.lineWidth=f;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke();h.strokeStyle=this.inner_color;h.lineWidth=b;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},connectedOutputTerminals:function(){return this._connectedTerminals(this.output_terminals)},_connectedTerminals:function(b){var a=[];$.each(b,function(c,d){if(d.connectors.length>0){a.push(d)}});return a},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a){if(a[b].connectors.length>0){return true}}return false},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var c=this.input_terminals;for(var b in c){var a=c[b];if(a.connectors.length>0&&a.isMappedOver()){return true}}return false},_connectedMappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){if(d.connectors.length>0){a.push(d)}}});return a},mappedInputTerminals:function(){return this._mappedTerminals(this.input_terminals)},_mappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){a.push(d)}});return a},hasMappedOverInputTerminals:function(){var a=false;_.each(this.input_terminals,function(b){var c=b.mapOver();if(c.isCollection){a=true}});return a},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(b){if(b.type){this.type=b.type}this.name=b.name;this.form_html=b.form_html;this.tool_state=b.tool_state;this.tool_errors=b.tool_errors;this.tooltip=b.tooltip?b.tooltip:"";this.annotation=b.annotation;this.post_job_actions=b.post_job_actions?b.post_job_actions:{};this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var a=this;var c=new NodeView({el:this.element[0],node:a,});a.nodeView=c;$.each(b.data_inputs,function(f,d){c.addDataInput(d)});if((b.data_inputs.length>0)&&(b.data_outputs.length>0)){c.addRule()}$.each(b.data_outputs,function(f,d){c.addDataOutput(d)});c.render();workflow.node_changed(this)},update_field_data:function(d){var c=this;nodeView=c.nodeView;this.tool_state=d.tool_state;this.form_html=d.form_html;this.tool_errors=d.tool_errors;this.annotation=d.annotation;if("post_job_actions" in d){var f=$.parseJSON(d.post_job_actions);this.post_job_actions=f?f:{}}c.nodeView.renderToolErrors();var g=nodeView.$("div.inputs");var a=nodeView.newInputsDiv();var b={};_.each(d.data_inputs,function(h){var i=c.nodeView.addDataInput(h,a);b[h.name]=i});_.each(_.difference(_.values(nodeView.terminalViews),_.values(b)),function(h){h.el.terminal.destroy()});nodeView.terminalViews=b;if(d.data_outputs.length==1&&"collection_type" in d.data_outputs[0]){nodeView.updateDataOutput(d.data_outputs[0])}g.replaceWith(a);this.markChanged();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},markChanged:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node(f.type,f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if(!$.isArray(h)){h=[h]}$.each(h,function(k,j){var m=wf.nodes[j.id];var n=new Connector();n.connect(m.output_terminals[j.output_name],d.input_terminals[i]);n.redraw()})}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node({element:i});g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+galaxy_config.root+"static/images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<div>").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}function add_node(b,d,a){var c=prebuild_node(b,d,a);workflow.add_node(c);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview();workflow.activate_node(c);return c}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}var NodeView=Backbone.View.extend({initialize:function(a){this.node=a.node;this.output_width=Math.max(150,this.$el.width());this.tool_body=this.$el.find(".toolFormBody");this.tool_body.find("div").remove();this.newInputsDiv().appendTo(this.tool_body);this.terminalViews={};this.outputTerminlViews={}},render:function(){this.renderToolErrors();this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){if(this.node.tool_errors){this.$el.addClass("tool-node-error")}else{this.$el.removeClass("tool-node-error")}},newInputsDiv:function(){return $("<div class='inputs'></div>")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("<div class='rule'></div>"))},addDataInput:function(i,d){var j=true;if(!d){d=this.$(".inputs");j=false}var f=this.terminalViews[i.name];var h=(i.input_type=="dataset_collection")?InputCollectionTerminalView:InputTerminalView;if(f&&!(f instanceof h)){f.el.terminal.destroy();f=null}if(!f){f=new h({node:this.node,input:i})}else{var g=f.el.terminal;g.update(i);g.destroyInvalidConnections()}this.terminalViews[i.name]=f;var c=f.el;var b=new DataInputView({terminalElement:c,input:i,nodeView:this,skipResize:j});var a=b.$el;d.append(a.prepend(f.terminalElements()));return f},addDataOutput:function(a){var d=(a.collection_type)?OutputCollectionTerminalView:OutputTerminalView;var c=new d({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(c.terminalElements()))},updateDataOutput:function(b){var a=this.node.output_terminals[b.name];a.update(b)}});var DataInputView=Backbone.View.extend({className:"form-row dataRow input-data-row",initialize:function(a){this.input=a.input;this.nodeView=a.nodeView;this.terminalElement=a.terminalElement;this.$el.attr("name",this.input.name).html(this.input.label);if(!a.skipResize){this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth());this.$el.css({position:"",left:"",top:"",display:""});this.$el.remove()}},});var OutputCalloutView=Backbone.View.extend({tagName:"div",initialize:function(b){this.label=b.label;this.node=b.node;this.output=b.output;var a=this;this.$el.attr("class","callout "+this.label).css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(a.output.name,a.node.workflow_outputs)!=-1){a.node.workflow_outputs.splice($.inArray(a.output.name,a.node.workflow_outputs),1);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{a.node.workflow_outputs.push(a.output.name);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Mark dataset as a workflow output. All unmarked datasets will be hidden."});this.$el.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});this.$el.show();this.resetImage()},resetImage:function(){if($.inArray(this.output.name,this.node.workflow_outputs)===-1){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}},hoverImage:function(){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")}});var DataOutputView=Backbone.View.extend({className:"form-row dataRow",initialize:function(c){this.output=c.output;this.terminalElement=c.terminalElement;this.nodeView=c.nodeView;var a=this.output;var b=a.name;var f=this.nodeView.node;var d=a.extensions.indexOf("input")>=0||a.extensions.indexOf("input_collection")>=0;if(!d){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(f.type=="tool"){var g=new OutputCalloutView({label:b,output:a,node:f,});this.$el.append(g.el);this.$el.hover(function(){g.hoverImage()},function(){g.resetImage()})}this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth()+17);this.$el.css({position:"",left:"",top:"",display:""}).detach()}});var TerminalView=Backbone.View.extend({setupMappingView:function(b){var c=new this.terminalMappingClass({terminal:b});var a=new this.terminalMappingViewClass({model:c});a.render();b.terminalMappingView=a;this.terminalMappingView=a},terminalElements:function(){if(this.terminalMappingView){return[this.terminalMappingView.el,this.el]}else{return[this.el]}}});var BaseInputTerminalView=TerminalView.extend({className:"terminal input-terminal",initialize:function(c){var f=c.node;var a=c.input;var b=a.name;var d=this.terminalForInput(a);if(!d.multiple){this.setupMappingView(d)}this.el.terminal=d;d.node=f;d.name=b;f.input_terminals[b]=d},events:{dropinit:"onDropInit",dropstart:"onDropStart",dropend:"onDropEnd",drop:"onDrop",hover:"onHover",},onDropInit:function(b,c){var a=this.el.terminal;return $(c.drag).hasClass("output-terminal")&&a.canAccept(c.drag.terminal)},onDropStart:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#BBFFBB"}},onDropEnd:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#FFFFFF"}},onDrop:function(b,c){var a=this.el.terminal;new Connector(c.drag.terminal,a).redraw()},onHover:function(){var c=this.el;var b=c.terminal;if(b.connectors.length>0){var a=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='button'></div>").append($("<div/>").addClass("fa-icon-button fa fa-times").click(function(){$.each(b.connectors,function(f,d){if(d){d.destroy()}});a.remove()}))).bind("mouseleave",function(){$(this).remove()});a.css({top:$(c).offset().top-2,left:$(c).offset().left-a.width(),"padding-right":$(c).width()}).show()}},});var InputTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputTerminalMapping,terminalMappingViewClass:InputTerminalMappingView,terminalForInput:function(a){return new InputTerminal({element:this.el,input:a})},});var InputCollectionTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputCollectionTerminalMapping,terminalMappingViewClass:InputCollectionTerminalMappingView,terminalForInput:function(a){return new InputCollectionTerminal({element:this.el,input:a})},});var BaseOutputTerminalView=TerminalView.extend({className:"terminal output-terminal",initialize:function(c){var f=c.node;var a=c.output;var b=a.name;var d=this.terminalForOutput(a);this.setupMappingView(d);this.el.terminal=d;d.node=f;d.name=b;f.output_terminals[b]=d},events:{drag:"onDrag",dragstart:"onDragStart",dragend:"onDragEnd",},onDrag:function(b,c){var a=function(){var f=$(c.proxy).offsetParent().offset(),d=c.offsetX-f.left,g=c.offsetY-f.top;$(c.proxy).css({left:d,top:g});c.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};a();$("#canvas-container").get(0).scroll_panel.test(b,a)},onDragStart:function(b,f){$(f.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var a=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);a.terminal=new OutputTerminal({element:a});var g=new Connector();g.dragging=true;g.connect(this.el.terminal,a.terminal);return a},onDragEnd:function(a,b){b.proxy.terminal.connectors[0].destroy();$(b.proxy).remove();$(b.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()}});var OutputTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputTerminalMapping,terminalMappingViewClass:OutputTerminalMappingView,terminalForOutput:function(a){var c=a.extensions;var b=new OutputTerminal({element:this.el,datatypes:c});return b},});var OutputCollectionTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputCollectionTerminalMapping,terminalMappingViewClass:OutputCollectionTerminalMappingView,terminalForOutput:function(a){var c=a.collection_type;var b=new OutputCollectionTerminal({element:this.el,collection_type:c,datatypes:a.extensions});return b},});function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.tool_errors){i.fillStyle="#FFCCCC";i.strokeStyle="#AA6666"}else{if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
\ No newline at end of file
diff -r 472fa62aaf72a1b7c8a2520665efdc833d2ab5fc -r fefcf6f47abe795b392348fbb901ffdf7b5c61ab test/functional/tools/collection_nested_test.xml
--- a/test/functional/tools/collection_nested_test.xml
+++ b/test/functional/tools/collection_nested_test.xml
@@ -16,8 +16,8 @@
<collection type="list:paired"><element name="i1"><collection type="paired">
- <element name="left" value="simple_line.txt" />
- <element name="right" value="simple_line_alternative.txt" />
+ <element name="forward" value="simple_line.txt" />
+ <element name="reverse" value="simple_line_alternative.txt" /></collection></element></collection>
@@ -37,8 +37,8 @@
<test><param name="f1"><collection type="paired">
- <element name="left" value="simple_line.txt" />
- <element name="right" value="simple_line_alternative.txt" />
+ <element name="forward" value="simple_line.txt" />
+ <element name="reverse" value="simple_line_alternative.txt" /></collection></param><output name="out1">
diff -r 472fa62aaf72a1b7c8a2520665efdc833d2ab5fc -r fefcf6f47abe795b392348fbb901ffdf7b5c61ab test/functional/tools/collection_paired_test.xml
--- a/test/functional/tools/collection_paired_test.xml
+++ b/test/functional/tools/collection_paired_test.xml
@@ -1,6 +1,6 @@
<tool id="collection_paired_test" name="paired_test" version="0.1.0"><command>
- cat $f1.forward $f1.reverse >> $out1;
+ cat $f1.forward $f1['reverse'] >> $out1;
</command><inputs><param name="f1" type="data_collection" collection_type="paired" />
diff -r 472fa62aaf72a1b7c8a2520665efdc833d2ab5fc -r fefcf6f47abe795b392348fbb901ffdf7b5c61ab test/qunit/tests/workflow_editor_tests.js
--- a/test/qunit/tests/workflow_editor_tests.js
+++ b/test/qunit/tests/workflow_editor_tests.js
@@ -477,11 +477,22 @@
var terminal = this.view.node.input_terminals[ "TestName" ];
var outputTerminal = new OutputTerminal( { name: "TestOuptut", datatypes: [ outputType ] } );
- outputTerminal.node = { markChanged: function() {}, post_job_actions: [], hasMappedOverInputTerminals: function() { return false; } };
+ outputTerminal.node = { markChanged: function() {}, post_job_actions: [], hasMappedOverInputTerminals: function() { return false; }, hasConnectedOutputTerminals: function() { return true; } };
outputTerminal.terminalMapping = { disableMapOver: function() {}, mapOver: NULL_COLLECTION_TYPE_DESCRIPTION };
var c = new Connector( outputTerminal, terminal );
return c;
+ },
+ connectAttachedMappedOutput: function( ) {
+ this.view.addDataInput( { name: "TestName", extensions: [ "txt" ], input_type: "dataset_collection" } );
+ var terminal = this.view.node.input_terminals[ "TestName" ];
+
+ var outputTerminal = new OutputTerminal( { name: "TestOuptut", datatypes: [ "txt" ] } );
+ outputTerminal.node = { markChanged: function() {}, post_job_actions: [], hasMappedOverInputTerminals: function() { return false; }, hasConnectedOutputTerminals: function() { return true; } };
+ outputTerminal.terminalMapping = { disableMapOver: function() {}, mapOver: new CollectionTypeDescription( "list" ) };
+ var c = new Connector( outputTerminal, terminal );
+
+ return c;
}
} );
@@ -519,6 +530,14 @@
ok( connector.handle2 === terminal );
} );
+ test( "replacing mapped terminal on data collection input update preserves connections", function() {
+ var connector = this.connectAttachedMappedOutput();
+ var newElement = $("<div class='inputs'></div>");
+ this.view.addDataInput( { name: "TestName", extensions: ["txt"], input_type: "dataset_collection" }, newElement );
+ var terminal = newElement.find(".input-terminal")[ 0 ].terminal;
+ ok( connector.handle2 === terminal );
+ } );
+
test( "replacing terminal on data input destroys invalid connections", function() {
var connector = this.connectAttachedTerminal( "txt", "txt" );
var newElement = $("<div class='inputs'></div>");
@@ -529,6 +548,26 @@
ok( connector_destroy_spy.called );
} );
+ test( "replacing terminal on data input with collection changes mapping view type", function() {
+ var connector = this.connectAttachedTerminal( "txt", "txt" );
+ var newElement = $("<div class='inputs'></div>");
+ var connector_destroy_spy = sinon.spy( connector, "destroy" );
+ this.view.addDataInput( { name: "TestName", extensions: ["txt"], input_type: "dataset_collection" }, newElement );
+ // Input type changed to dataset_collection - old connections are reset.
+ // Would be nice to preserve these connections and make them map over.
+ var terminal = newElement.find(".input-terminal")[ 0 ].terminal;
+ ok( connector_destroy_spy.called );
+ } );
+
+ test( "replacing terminal on data collection input with simple input changes mapping view type", function() {
+ var connector = this.connectAttachedMappedOutput();
+ var newElement = $("<div class='inputs'></div>");
+ var connector_destroy_spy = sinon.spy( connector, "destroy" );
+ this.view.addDataInput( { name: "TestName", extensions: ["txt"], input_type: "dataset" }, newElement );
+ var terminal = newElement.find(".input-terminal")[ 0 ].terminal;
+ ok( connector_destroy_spy.called );
+ } );
+
/* global InputTerminalView */
module( "Input terminal view", {
setup: function() {
@@ -878,6 +917,24 @@
this.verifyNotAttachable( inputTerminal1, "list:paired" );
} );
+ test( "unconnected multiple inputs can be connected to rank 1 collections", function() {
+ var inputTerminal1 = this.newInputTerminal( null, { multiple: true } );
+ this.verifyAttachable( inputTerminal1, "list" );
+ } );
+
+ test( "connected multiple input cannot be connected to collections", function() {
+ var inputTerminal1 = this.newInputTerminal( null, { multiple: true } );
+ var connectedInput1 = this.addConnectedInput( inputTerminal1 );
+ this.addConnectedOutput( connectedInput1 );
+ // Normally could do this reduction, but cannot because input already connected.
+ this.verifyNotAttachable( connectedInput1, "list" );
+ } );
+
+ test( "unconnected multiple inputs cannot be connected to rank > 1 collections (yet...)", function() {
+ var inputTerminal1 = this.newInputTerminal( null, { multiple: true } );
+ this.verifyNotAttachable( inputTerminal1, "list:paired" );
+ } );
+
test( "resetMappingIfNeeded does nothing if not mapped", function() {
var inputTerminal1 = this.newInputTerminal();
inputTerminal1.resetMappingIfNeeded();
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/bdff5a8cc4b9/
Changeset: bdff5a8cc4b9
Branch: next-stable
User: greg
Date: 2014-05-27 19:32:49
Summary: Improve exception handling for generated Tool Shed URLs.
Affected #: 4 files
diff -r 2cab30fd7436a0e38bb3d60eea5b5fa929f62244 -r bdff5a8cc4b91ec6deff722884bde7357e67e6ef lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -383,6 +383,11 @@
"""
repository = suc.get_installed_tool_shed_repository( trans, repository_id )
tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, str( repository.tool_shed ) )
+ if tool_shed_url is None or repository_name is None or repository_owner is None or changeset_revision is None:
+ message = "Unable to retrieve tool dependencies from the Tool Shed because one or more of the following required "
+ message += "parameters is None: tool_shed_url: %s, repository_name: %s, repository_owner: %s, changeset_revision: %s " % \
+ ( str( tool_shed_url ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
+ raise Exception( message )
params = '?name=%s&owner=%s&changeset_revision=%s' % ( repository_name, repository_owner, changeset_revision )
url = common_util.url_join( tool_shed_url,
'repository/get_tool_dependencies%s' % params )
@@ -403,6 +408,11 @@
"""
repository = suc.get_installed_tool_shed_repository( trans, repository_id )
tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, str( repository.tool_shed ) )
+ if tool_shed_url is None or repository_name is None or repository_owner is None or changeset_revision is None:
+ message = "Unable to retrieve updated repository information from the Tool Shed because one or more of the following "
+ message += "required parameters is None: tool_shed_url: %s, repository_name: %s, repository_owner: %s, changeset_revision: %s " % \
+ ( str( tool_shed_url ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
+ raise Exception( message )
params = '?name=%s&owner=%s&changeset_revision=%s' % ( str( repository_name ),
str( repository_owner ),
changeset_revision )
diff -r 2cab30fd7436a0e38bb3d60eea5b5fa929f62244 -r bdff5a8cc4b91ec6deff722884bde7357e67e6ef lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
@@ -161,6 +161,11 @@
def create_temporary_tool_dependencies_config( self, app, tool_shed_url, name, owner, changeset_revision ):
"""Make a call to the tool shed to get the required repository's tool_dependencies.xml file."""
tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( app, tool_shed_url )
+ if tool_shed_url is None or name is None or owner is None or changeset_revision is None:
+ message = "Unable to retrieve required tool_dependencies.xml file from the Tool Shed because one or more of the "
+ message += "following required parameters is None: tool_shed_url: %s, name: %s, owner: %s, changeset_revision: %s " % \
+ ( str( tool_shed_url ), str( name ), str( owner ), str( changeset_revision ) )
+ raise Exception( message )
params = '?name=%s&owner=%s&changeset_revision=%s' % ( name, owner, changeset_revision )
url = common_util.url_join( tool_shed_url,
'repository/get_tool_dependencies_config_contents%s' % params )
@@ -175,7 +180,7 @@
fh.close()
return tmp_filename
else:
- message = "Unable to retrieve required tool_dependencies.xml file from the tool shed for revision "
+ message = "Unable to retrieve required tool_dependencies.xml file from the Tool Shed for revision "
message += "%s of installed repository %s owned by %s." % ( str( changeset_revision ), str( name ), str( owner ) )
raise Exception( message )
return None
diff -r 2cab30fd7436a0e38bb3d60eea5b5fa929f62244 -r bdff5a8cc4b91ec6deff722884bde7357e67e6ef lib/tool_shed/util/common_util.py
--- a/lib/tool_shed/util/common_util.py
+++ b/lib/tool_shed/util/common_util.py
@@ -107,7 +107,7 @@
def generate_clone_url_for_installed_repository( app, repository ):
"""Generate the URL for cloning a repository that has been installed into a Galaxy instance."""
tool_shed_url = get_tool_shed_url_from_tool_shed_registry( app, str( repository.tool_shed ) )
- return url_join( tool_shed_url, 'repos', repository.owner, repository.name )
+ return url_join( tool_shed_url, 'repos', str( repository.owner ), str( repository.name ) )
def generate_clone_url_for_repository_in_tool_shed( trans, repository ):
"""Generate the URL for cloning a repository that is in the tool shed."""
@@ -316,5 +316,6 @@
"""Return a valid URL produced by appending a base URL and a set of request parameters."""
parts = []
for arg in args:
- parts.append( arg.strip( '/' ) )
+ if arg is not None:
+ parts.append( arg.strip( '/' ) )
return '/'.join( parts )
diff -r 2cab30fd7436a0e38bb3d60eea5b5fa929f62244 -r bdff5a8cc4b91ec6deff722884bde7357e67e6ef lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -942,6 +942,11 @@
"""
# This method is used only in Galaxy, not the Tool Shed. We store the port (if one exists) in the database.
tool_shed = common_util.remove_protocol_from_tool_shed_url( tool_shed )
+ if tool_shed is None or name is None or owner is None or changeset_revision is None:
+ message = "Unable to retrieve the repository record from the database because one or more of the following "
+ message += "required parameters is None: tool_shed: %s, name: %s, owner: %s, changeset_revision: %s " % \
+ ( str( tool_shed ), str( name ), str( owner ), str( changeset_revision ) )
+ raise Exception( message )
repository = get_tool_shed_repository_by_shed_name_owner_installed_changeset_revision( app=app,
tool_shed=tool_shed,
name=name,
@@ -1356,6 +1361,11 @@
the received tool_shed_url / name / owner combination.
"""
tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( app, tool_shed_url )
+ if tool_shed_url is None or name is None or owner is None or changeset_revision is None:
+ message = "Unable to get updated changeset revisions from the Tool Shed because one or more of the following "
+ message += "required parameters is None: tool_shed_url: %s, name: %s, owner: %s, changeset_revision: %s " % \
+ ( str( tool_shed_url ), str( name ), str( owner ), str( changeset_revision ) )
+ raise Exception( message )
params = '?name=%s&owner=%s&changeset_revision=%s' % ( name, owner, changeset_revision )
url = common_util.url_join( tool_shed_url,
'repository/updated_changeset_revisions%s' % params )
https://bitbucket.org/galaxy/galaxy-central/commits/472fa62aaf72/
Changeset: 472fa62aaf72
User: greg
Date: 2014-05-27 19:33:16
Summary: Merged next-stable
Affected #: 4 files
diff -r 44df8d9c9aa11acf796f88203551a92ac03c559d -r 472fa62aaf72a1b7c8a2520665efdc833d2ab5fc lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -377,6 +377,11 @@
"""
repository = suc.get_installed_tool_shed_repository( trans, repository_id )
tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, str( repository.tool_shed ) )
+ if tool_shed_url is None or repository_name is None or repository_owner is None or changeset_revision is None:
+ message = "Unable to retrieve tool dependencies from the Tool Shed because one or more of the following required "
+ message += "parameters is None: tool_shed_url: %s, repository_name: %s, repository_owner: %s, changeset_revision: %s " % \
+ ( str( tool_shed_url ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
+ raise Exception( message )
params = '?name=%s&owner=%s&changeset_revision=%s' % ( repository_name, repository_owner, changeset_revision )
url = common_util.url_join( tool_shed_url,
'repository/get_tool_dependencies%s' % params )
@@ -397,6 +402,11 @@
"""
repository = suc.get_installed_tool_shed_repository( trans, repository_id )
tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, str( repository.tool_shed ) )
+ if tool_shed_url is None or repository_name is None or repository_owner is None or changeset_revision is None:
+ message = "Unable to retrieve updated repository information from the Tool Shed because one or more of the following "
+ message += "required parameters is None: tool_shed_url: %s, repository_name: %s, repository_owner: %s, changeset_revision: %s " % \
+ ( str( tool_shed_url ), str( repository_name ), str( repository_owner ), str( changeset_revision ) )
+ raise Exception( message )
params = '?name=%s&owner=%s&changeset_revision=%s' % ( str( repository_name ),
str( repository_owner ),
changeset_revision )
diff -r 44df8d9c9aa11acf796f88203551a92ac03c559d -r 472fa62aaf72a1b7c8a2520665efdc833d2ab5fc lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/recipe/tag_handler.py
@@ -161,6 +161,11 @@
def create_temporary_tool_dependencies_config( self, app, tool_shed_url, name, owner, changeset_revision ):
"""Make a call to the tool shed to get the required repository's tool_dependencies.xml file."""
tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( app, tool_shed_url )
+ if tool_shed_url is None or name is None or owner is None or changeset_revision is None:
+ message = "Unable to retrieve required tool_dependencies.xml file from the Tool Shed because one or more of the "
+ message += "following required parameters is None: tool_shed_url: %s, name: %s, owner: %s, changeset_revision: %s " % \
+ ( str( tool_shed_url ), str( name ), str( owner ), str( changeset_revision ) )
+ raise Exception( message )
params = '?name=%s&owner=%s&changeset_revision=%s' % ( name, owner, changeset_revision )
url = common_util.url_join( tool_shed_url,
'repository/get_tool_dependencies_config_contents%s' % params )
@@ -175,7 +180,7 @@
fh.close()
return tmp_filename
else:
- message = "Unable to retrieve required tool_dependencies.xml file from the tool shed for revision "
+ message = "Unable to retrieve required tool_dependencies.xml file from the Tool Shed for revision "
message += "%s of installed repository %s owned by %s." % ( str( changeset_revision ), str( name ), str( owner ) )
raise Exception( message )
return None
diff -r 44df8d9c9aa11acf796f88203551a92ac03c559d -r 472fa62aaf72a1b7c8a2520665efdc833d2ab5fc lib/tool_shed/util/common_util.py
--- a/lib/tool_shed/util/common_util.py
+++ b/lib/tool_shed/util/common_util.py
@@ -107,7 +107,7 @@
def generate_clone_url_for_installed_repository( app, repository ):
"""Generate the URL for cloning a repository that has been installed into a Galaxy instance."""
tool_shed_url = get_tool_shed_url_from_tool_shed_registry( app, str( repository.tool_shed ) )
- return url_join( tool_shed_url, 'repos', repository.owner, repository.name )
+ return url_join( tool_shed_url, 'repos', str( repository.owner ), str( repository.name ) )
def generate_clone_url_for_repository_in_tool_shed( trans, repository ):
"""Generate the URL for cloning a repository that is in the tool shed."""
@@ -316,5 +316,6 @@
"""Return a valid URL produced by appending a base URL and a set of request parameters."""
parts = []
for arg in args:
- parts.append( arg.strip( '/' ) )
+ if arg is not None:
+ parts.append( arg.strip( '/' ) )
return '/'.join( parts )
diff -r 44df8d9c9aa11acf796f88203551a92ac03c559d -r 472fa62aaf72a1b7c8a2520665efdc833d2ab5fc lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -929,6 +929,11 @@
"""
# This method is used only in Galaxy, not the Tool Shed. We store the port (if one exists) in the database.
tool_shed = common_util.remove_protocol_from_tool_shed_url( tool_shed )
+ if tool_shed is None or name is None or owner is None or changeset_revision is None:
+ message = "Unable to retrieve the repository record from the database because one or more of the following "
+ message += "required parameters is None: tool_shed: %s, name: %s, owner: %s, changeset_revision: %s " % \
+ ( str( tool_shed ), str( name ), str( owner ), str( changeset_revision ) )
+ raise Exception( message )
repository = get_tool_shed_repository_by_shed_name_owner_installed_changeset_revision( app=app,
tool_shed=tool_shed,
name=name,
@@ -1343,6 +1348,11 @@
the received tool_shed_url / name / owner combination.
"""
tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( app, tool_shed_url )
+ if tool_shed_url is None or name is None or owner is None or changeset_revision is None:
+ message = "Unable to get updated changeset revisions from the Tool Shed because one or more of the following "
+ message += "required parameters is None: tool_shed_url: %s, name: %s, owner: %s, changeset_revision: %s " % \
+ ( str( tool_shed_url ), str( name ), str( owner ), str( changeset_revision ) )
+ raise Exception( message )
params = '?name=%s&owner=%s&changeset_revision=%s' % ( name, owner, changeset_revision )
url = common_util.url_join( tool_shed_url,
'repository/updated_changeset_revisions%s' % params )
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: Style fix: remove manual height setting on empty annotation fields
by commits-noreply@bitbucket.org 27 May '14
by commits-noreply@bitbucket.org 27 May '14
27 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/44df8d9c9aa1/
Changeset: 44df8d9c9aa1
User: carlfeberhard
Date: 2014-05-27 18:14:50
Summary: Style fix: remove manual height setting on empty annotation fields
Affected #: 2 files
diff -r 6934f4ceed4572bffde39303c2ca9a993f1ffc55 -r 44df8d9c9aa11acf796f88203551a92ac03c559d static/style/blue/base.css
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -1663,7 +1663,6 @@
.hda.dataset .dataset-body .dataset-actions .right .icon-btn{margin-left:2px}
.hda.dataset .dataset-body .tags-display{display:none;margin-bottom:8px}.hda.dataset .dataset-body .tags-display .select2-container{min-width:0px}.hda.dataset .dataset-body .tags-display .select2-container .select2-choices{border-radius:3px}
.hda.dataset .dataset-body .annotation-display{display:none;margin-bottom:8px}.hda.dataset .dataset-body .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.30000000000000004);padding:4px;white-space:pre-line;overflow:auto}
-.hda.dataset .dataset-body .annotation-display .annotation:empty{height:22px}
.hda.dataset .dataset-body .annotation-display .annotation:empty:after{position:relative;top:-4px;font-size:10px;font-style:italic;color:grey;content:'Click here to edit annotation'}
.hda.dataset .dataset-body .annotation-display textarea{margin:0px 0px 2px 0px;display:block;border-radius:3px;width:100%}
.hda.dataset .dataset-body .dataset-display-applications .display-application:last-child{margin-bottom:8px}
@@ -1693,7 +1692,6 @@
.history-panel .history-controls .tags-display .select2-container{min-width:0px}.history-panel .history-controls .tags-display .select2-container .select2-choices{border-radius:3px}
.history-panel .history-controls .tags-display input{border-radius:3px}
.history-panel .history-controls .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.5);padding:4px;white-space:pre-line;overflow:auto}
-.history-panel .history-controls .annotation-display .annotation:empty{height:22px}
.history-panel .history-controls .annotation-display .annotation:empty:after{position:relative;top:-4px;font-size:10px;font-style:italic;color:grey;content:'Click here to edit annotation'}
.history-panel .history-controls .annotation-display textarea{margin:0px 0px 2px 0px;display:block;border-radius:3px;width:100%}
.history-panel .history-controls .history-dataset-actions{display:none}.history-panel .history-controls .history-dataset-actions:before,.history-panel .history-controls .history-dataset-actions:after{content:" ";display:table;}
diff -r 6934f4ceed4572bffde39303c2ca9a993f1ffc55 -r 44df8d9c9aa11acf796f88203551a92ac03c559d static/style/src/less/history.less
--- a/static/style/src/less/history.less
+++ b/static/style/src/less/history.less
@@ -311,9 +311,6 @@
white-space: pre-line;
overflow: auto;
}
- .annotation:empty {
- height: @icon-btn-size;
- }
// fake placeholder for editable text annotation
.annotation:empty:after {
position: relative;
@@ -490,9 +487,6 @@
white-space: pre-line;
overflow: auto;
}
- .annotation:empty {
- height: @icon-btn-size;
- }
// fake placeholder for editable text annotation
.annotation:empty:after {
position: relative;
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: Style fix: use scrolling overflow for long history annotation lines
by commits-noreply@bitbucket.org 27 May '14
by commits-noreply@bitbucket.org 27 May '14
27 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/6934f4ceed45/
Changeset: 6934f4ceed45
User: carlfeberhard
Date: 2014-05-27 17:54:12
Summary: Style fix: use scrolling overflow for long history annotation lines
Affected #: 2 files
diff -r 14c8fee2aedba44f1a21f233bc87412edcbc00aa -r 6934f4ceed4572bffde39303c2ca9a993f1ffc55 static/style/blue/base.css
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -1692,7 +1692,7 @@
.history-panel .history-controls .tags-display label:after,.history-panel .history-controls .annotation-display label:after{content:':'}
.history-panel .history-controls .tags-display .select2-container{min-width:0px}.history-panel .history-controls .tags-display .select2-container .select2-choices{border-radius:3px}
.history-panel .history-controls .tags-display input{border-radius:3px}
-.history-panel .history-controls .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.5);padding:4px;white-space:pre-line}
+.history-panel .history-controls .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.5);padding:4px;white-space:pre-line;overflow:auto}
.history-panel .history-controls .annotation-display .annotation:empty{height:22px}
.history-panel .history-controls .annotation-display .annotation:empty:after{position:relative;top:-4px;font-size:10px;font-style:italic;color:grey;content:'Click here to edit annotation'}
.history-panel .history-controls .annotation-display textarea{margin:0px 0px 2px 0px;display:block;border-radius:3px;width:100%}
diff -r 14c8fee2aedba44f1a21f233bc87412edcbc00aa -r 6934f4ceed4572bffde39303c2ca9a993f1ffc55 static/style/src/less/history.less
--- a/static/style/src/less/history.less
+++ b/static/style/src/less/history.less
@@ -488,6 +488,7 @@
border: 1px solid fadeout( @layout-border-color, 50% );
padding: 4px;
white-space: pre-line;
+ overflow: auto;
}
.annotation:empty {
height: @icon-btn-size;
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: Style fix: use scrolling overflow for long HDA annotation lines
by commits-noreply@bitbucket.org 27 May '14
by commits-noreply@bitbucket.org 27 May '14
27 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/14c8fee2aedb/
Changeset: 14c8fee2aedb
User: carlfeberhard
Date: 2014-05-27 17:49:47
Summary: Style fix: use scrolling overflow for long HDA annotation lines
Affected #: 2 files
diff -r 33b1ff4b998541ae1ca1c302919077f4f29cc57a -r 14c8fee2aedba44f1a21f233bc87412edcbc00aa static/style/blue/base.css
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -1662,7 +1662,7 @@
.hda.dataset .dataset-body .dataset-actions .right .icon-btn:only-child{margin:0px;border-radius:3px}
.hda.dataset .dataset-body .dataset-actions .right .icon-btn{margin-left:2px}
.hda.dataset .dataset-body .tags-display{display:none;margin-bottom:8px}.hda.dataset .dataset-body .tags-display .select2-container{min-width:0px}.hda.dataset .dataset-body .tags-display .select2-container .select2-choices{border-radius:3px}
-.hda.dataset .dataset-body .annotation-display{display:none;margin-bottom:8px}.hda.dataset .dataset-body .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.30000000000000004);padding:4px;white-space:pre-line}
+.hda.dataset .dataset-body .annotation-display{display:none;margin-bottom:8px}.hda.dataset .dataset-body .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.30000000000000004);padding:4px;white-space:pre-line;overflow:auto}
.hda.dataset .dataset-body .annotation-display .annotation:empty{height:22px}
.hda.dataset .dataset-body .annotation-display .annotation:empty:after{position:relative;top:-4px;font-size:10px;font-style:italic;color:grey;content:'Click here to edit annotation'}
.hda.dataset .dataset-body .annotation-display textarea{margin:0px 0px 2px 0px;display:block;border-radius:3px;width:100%}
diff -r 33b1ff4b998541ae1ca1c302919077f4f29cc57a -r 14c8fee2aedba44f1a21f233bc87412edcbc00aa static/style/src/less/history.less
--- a/static/style/src/less/history.less
+++ b/static/style/src/less/history.less
@@ -309,6 +309,7 @@
.annotation {
.info-section;
white-space: pre-line;
+ overflow: auto;
}
.annotation:empty {
height: @icon-btn-size;
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Clarify problematic members of tar archives being uploaded to Tool Shed repositories.
by commits-noreply@bitbucket.org 27 May '14
by commits-noreply@bitbucket.org 27 May '14
27 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/33b1ff4b9985/
Changeset: 33b1ff4b9985
User: greg
Date: 2014-05-27 17:13:05
Summary: Clarify problematic members of tar archives being uploaded to Tool Shed repositories.
Affected #: 1 file
diff -r 3f5ed3658cbecfcdcfa6f130a0d3a361f98d5508 -r 33b1ff4b998541ae1ca1c302919077f4f29cc57a lib/tool_shed/util/commit_util.py
--- a/lib/tool_shed/util/commit_util.py
+++ b/lib/tool_shed/util/commit_util.py
@@ -39,20 +39,25 @@
for member in archive.getmembers():
# Allow regular files and directories only
if not ( member.isdir() or member.isfile() or member.islnk() ):
- message = "Uploaded archives can only include regular directories and files (no symbolic links, devices, etc). Offender: %s" % str( member )
+ message = "Uploaded archives can only include regular directories and files (no symbolic links, devices, etc). "
+ message += "The problematic member in this archive is %s," % str( member.name )
return False, message
for item in [ '.hg', '..', '/' ]:
if member.name.startswith( item ):
- message = "Uploaded archives cannot contain .hg directories, absolute filenames starting with '/', or filenames with two dots '..'."
+ message = "Uploaded archives cannot contain .hg directories, absolute filenames starting with '/', or filenames with two dots '..'. "
+ message += "The problematic member in this archive is %s." % str( member.name )
return False, message
if member.name in [ 'hgrc' ]:
- message = "Uploaded archives cannot contain hgrc files."
+ message = "Uploaded archives cannot contain hgrc files. "
+ message += "The problematic member in this archive is %s." % str( member.name )
return False, message
if repository.type == rt_util.REPOSITORY_SUITE_DEFINITION and member.name != rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME:
- message = 'Repositories of type <b>Repsoitory suite definition</b> can contain only a single file named <b>repository_dependencies.xml</b>.'
+ message = 'Repositories of type <b>Repository suite definition</b> can contain only a single file named <b>repository_dependencies.xml</b>.'
+ message += 'This archive contains a member named %s.' % str( member.name )
return False, message
if repository.type == rt_util.TOOL_DEPENDENCY_DEFINITION and member.name != rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME:
message = 'Repositories of type <b>Tool dependency definition</b> can contain only a single file named <b>tool_dependencies.xml</b>.'
+ message += 'This archive contains a member named %s.' % str( member.name )
return False, message
return True, ''
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/636f0270fc30/
Changeset: 636f0270fc30
Branch: next-stable
User: jmchilton
Date: 2014-05-27 16:06:44
Summary: Bugfix: Don't show deleted HDCAs when displaying tool selection options.
Affected #: 2 files
diff -r a5f097248869df64d6f16a2e7dc14c3ceffbcce1 -r 636f0270fc3009a4da3737ffd1751d40821d3cdd lib/galaxy/dataset_collections/__init__.py
--- a/lib/galaxy/dataset_collections/__init__.py
+++ b/lib/galaxy/dataset_collections/__init__.py
@@ -170,7 +170,7 @@
return validated_payload
def history_dataset_collections(self, history, query):
- collections = history.dataset_collections
+ collections = history.active_dataset_collections
collections = filter( query.direct_match, collections )
return collections
diff -r a5f097248869df64d6f16a2e7dc14c3ceffbcce1 -r 636f0270fc3009a4da3737ffd1751d40821d3cdd lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -1738,7 +1738,7 @@
dataset_collection_matcher = DatasetCollectionMatcher( dataset_matcher )
- for history_dataset_collection in history.dataset_collections:
+ for history_dataset_collection in history.active_dataset_collections:
if dataset_collection_matcher.hdca_match( history_dataset_collection, reduction=reduction ):
name = history_dataset_collection.name
hid = str( history_dataset_collection.hid )
@@ -2042,7 +2042,7 @@
dataset_matcher = DatasetMatcher( trans, self, value, other_values )
dataset_collection_matcher = DatasetCollectionMatcher( dataset_matcher )
- for history_dataset_collection in history.dataset_collections:
+ for history_dataset_collection in history.active_dataset_collections:
if not self._history_query( trans ).can_map_over( history_dataset_collection ):
continue
https://bitbucket.org/galaxy/galaxy-central/commits/2cab30fd7436/
Changeset: 2cab30fd7436
Branch: next-stable
User: jmchilton
Date: 2014-05-27 16:06:44
Summary: English fix: Spelling error in a5f0972.
Affected #: 1 file
diff -r 636f0270fc3009a4da3737ffd1751d40821d3cdd -r 2cab30fd7436a0e38bb3d60eea5b5fa929f62244 lib/galaxy/dataset_collections/util.py
--- a/lib/galaxy/dataset_collections/util.py
+++ b/lib/galaxy/dataset_collections/util.py
@@ -10,7 +10,7 @@
ERROR_MESSAGE_NO_NAME = "Cannot load invalid dataset identifier - missing name - %s"
ERROR_MESSAGE_NO_COLLECTION_TYPE = "No collection_type define for nested collection %s."
ERROR_MESSAGE_INVALID_PARAMETER_FOUND = "Found invalid parameter %s in element identifier description %s."
-ERROR_MESSAGE_DUPLICATED_IDENTIFIER_FOUND = "Found duplicated element idenfier name %s."
+ERROR_MESSAGE_DUPLICATED_IDENTIFIER_FOUND = "Found duplicated element identifier name %s."
def api_payload_to_create_params( payload ):
https://bitbucket.org/galaxy/galaxy-central/commits/3f5ed3658cbe/
Changeset: 3f5ed3658cbe
User: jmchilton
Date: 2014-05-27 16:07:03
Summary: Merge latest next-stable.
Affected #: 3 files
diff -r 8f1f131bdeb49f876a06d748d97cfb48ec329550 -r 3f5ed3658cbecfcdcfa6f130a0d3a361f98d5508 lib/galaxy/dataset_collections/__init__.py
--- a/lib/galaxy/dataset_collections/__init__.py
+++ b/lib/galaxy/dataset_collections/__init__.py
@@ -170,7 +170,7 @@
return validated_payload
def history_dataset_collections(self, history, query):
- collections = history.dataset_collections
+ collections = history.active_dataset_collections
collections = filter( query.direct_match, collections )
return collections
diff -r 8f1f131bdeb49f876a06d748d97cfb48ec329550 -r 3f5ed3658cbecfcdcfa6f130a0d3a361f98d5508 lib/galaxy/dataset_collections/util.py
--- a/lib/galaxy/dataset_collections/util.py
+++ b/lib/galaxy/dataset_collections/util.py
@@ -10,7 +10,7 @@
ERROR_MESSAGE_NO_NAME = "Cannot load invalid dataset identifier - missing name - %s"
ERROR_MESSAGE_NO_COLLECTION_TYPE = "No collection_type define for nested collection %s."
ERROR_MESSAGE_INVALID_PARAMETER_FOUND = "Found invalid parameter %s in element identifier description %s."
-ERROR_MESSAGE_DUPLICATED_IDENTIFIER_FOUND = "Found duplicated element idenfier name %s."
+ERROR_MESSAGE_DUPLICATED_IDENTIFIER_FOUND = "Found duplicated element identifier name %s."
def api_payload_to_create_params( payload ):
diff -r 8f1f131bdeb49f876a06d748d97cfb48ec329550 -r 3f5ed3658cbecfcdcfa6f130a0d3a361f98d5508 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -1738,7 +1738,7 @@
dataset_collection_matcher = DatasetCollectionMatcher( dataset_matcher )
- for history_dataset_collection in history.dataset_collections:
+ for history_dataset_collection in history.active_dataset_collections:
if dataset_collection_matcher.hdca_match( history_dataset_collection, reduction=reduction ):
name = history_dataset_collection.name
hid = str( history_dataset_collection.hid )
@@ -2042,7 +2042,7 @@
dataset_matcher = DatasetMatcher( trans, self, value, other_values )
dataset_collection_matcher = DatasetCollectionMatcher( dataset_matcher )
- for history_dataset_collection in history.dataset_collections:
+ for history_dataset_collection in history.active_dataset_collections:
if not self._history_query( trans ).can_map_over( history_dataset_collection ):
continue
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/d5125e51cefa/
Changeset: d5125e51cefa
Branch: next-stable
User: jmchilton
Date: 2014-05-24 00:20:55
Summary: Add some more colllection creation logging.
Bug report from takadonet of collection request with 280 items only creating 60 - want to know if request got chopped off by proxy or paste, there was some logic error in my code, or if it hit some sort of db/transaction limit. This logging should help dianose that (hopefully). If I find the bug, I will remove this extra logging.
Affected #: 2 files
diff -r 13e3b3e1c194b70dc8d17fc669d314264a000151 -r d5125e51cefab1b43bf004eaa1b57623149c7202 lib/galaxy/dataset_collections/__init__.py
--- a/lib/galaxy/dataset_collections/__init__.py
+++ b/lib/galaxy/dataset_collections/__init__.py
@@ -76,6 +76,7 @@
trans.sa_session.add( output_dataset )
dataset_collection_instance.implicit_output_name = implicit_collection_info[ "implicit_output_name" ]
+ log.debug("Created collection with %d elements" % ( len( dataset_collection_instance.collection.elements ) ) )
# Handle setting hid
parent.add_dataset_collection( dataset_collection_instance )
elif isinstance( parent, model.LibraryFolder ):
diff -r 13e3b3e1c194b70dc8d17fc669d314264a000151 -r d5125e51cefab1b43bf004eaa1b57623149c7202 lib/galaxy/dataset_collections/util.py
--- a/lib/galaxy/dataset_collections/util.py
+++ b/lib/galaxy/dataset_collections/util.py
@@ -2,6 +2,9 @@
from galaxy import web
from galaxy import model
+import logging
+log = logging.getLogger( __name__ )
+
ERROR_MESSAGE_UNKNOWN_SRC = "Unknown dataset source (src) %s."
ERROR_MESSAGE_NO_NESTED_IDENTIFIERS = "Dataset source new_collection requires nested element_identifiers for new collection."
ERROR_MESSAGE_NO_NAME = "Cannot load invalid dataset identifier - missing name - %s"
@@ -32,6 +35,7 @@
""" Scan through the list of element identifiers supplied by the API consumer
and verify the structure is valid.
"""
+ log.debug( "Validating %d element identifiers for collection creation." % len( element_identifiers ) )
for element_identifier in element_identifiers:
if "__object__" in element_identifier:
message = ERROR_MESSAGE_INVALID_PARAMETER_FOUND % ( "__model_object__", element_identifier )
https://bitbucket.org/galaxy/galaxy-central/commits/a5f097248869/
Changeset: a5f097248869
Branch: next-stable
User: jmchilton
Date: 2014-05-27 15:20:39
Summary: Bugfix: Explicitly enforce implicit collection unique element identifier constraint.
Think this is responsible for bug when collections API does not create as many collections as requested.
Affected #: 2 files
diff -r d5125e51cefab1b43bf004eaa1b57623149c7202 -r a5f097248869df64d6f16a2e7dc14c3ceffbcce1 lib/galaxy/dataset_collections/util.py
--- a/lib/galaxy/dataset_collections/util.py
+++ b/lib/galaxy/dataset_collections/util.py
@@ -10,6 +10,7 @@
ERROR_MESSAGE_NO_NAME = "Cannot load invalid dataset identifier - missing name - %s"
ERROR_MESSAGE_NO_COLLECTION_TYPE = "No collection_type define for nested collection %s."
ERROR_MESSAGE_INVALID_PARAMETER_FOUND = "Found invalid parameter %s in element identifier description %s."
+ERROR_MESSAGE_DUPLICATED_IDENTIFIER_FOUND = "Found duplicated element idenfier name %s."
def api_payload_to_create_params( payload ):
@@ -36,6 +37,7 @@
and verify the structure is valid.
"""
log.debug( "Validating %d element identifiers for collection creation." % len( element_identifiers ) )
+ identifier_names = set()
for element_identifier in element_identifiers:
if "__object__" in element_identifier:
message = ERROR_MESSAGE_INVALID_PARAMETER_FOUND % ( "__model_object__", element_identifier )
@@ -43,6 +45,12 @@
if "name" not in element_identifier:
message = ERROR_MESSAGE_NO_NAME % element_identifier
raise exceptions.RequestParameterInvalidException( message )
+ name = element_identifier[ "name" ]
+ if name in identifier_names:
+ message = ERROR_MESSAGE_DUPLICATED_IDENTIFIER_FOUND % name
+ raise exceptions.RequestParameterInvalidException( message )
+ else:
+ identifier_names.add( name )
src = element_identifier.get( "src", "hda" )
if src not in [ "hda", "hdca", "ldda", "new_collection" ]:
message = ERROR_MESSAGE_UNKNOWN_SRC % src
diff -r d5125e51cefab1b43bf004eaa1b57623149c7202 -r a5f097248869df64d6f16a2e7dc14c3ceffbcce1 test/api/test_dataset_collections.py
--- a/test/api/test_dataset_collections.py
+++ b/test/api/test_dataset_collections.py
@@ -106,6 +106,19 @@
create_response = self._post( "dataset_collections", payload )
self._assert_status_code_is( create_response, 403 )
+ def test_enforces_unique_names( self ):
+ element_identifiers = self.dataset_collection_populator.list_identifiers( self.history_id )
+ element_identifiers[ 2 ][ "name" ] = element_identifiers[ 0 ][ "name" ]
+ payload = dict(
+ instance_type="history",
+ history_id=self.history_id,
+ element_identifiers=json.dumps(element_identifiers),
+ collection_type="list",
+ )
+
+ create_response = self._post( "dataset_collections", payload )
+ self._assert_status_code_is( create_response, 400 )
+
def _check_create_response( self, create_response ):
self._assert_status_code_is( create_response, 200 )
dataset_collection = create_response.json()
https://bitbucket.org/galaxy/galaxy-central/commits/8f1f131bdeb4/
Changeset: 8f1f131bdeb4
User: jmchilton
Date: 2014-05-27 15:21:18
Summary: Merge latest next-stable.
Affected #: 2 files
diff -r f94244370cfa4b6e685a5ea46a2b2d4ede42b749 -r 8f1f131bdeb49f876a06d748d97cfb48ec329550 lib/galaxy/dataset_collections/util.py
--- a/lib/galaxy/dataset_collections/util.py
+++ b/lib/galaxy/dataset_collections/util.py
@@ -10,6 +10,7 @@
ERROR_MESSAGE_NO_NAME = "Cannot load invalid dataset identifier - missing name - %s"
ERROR_MESSAGE_NO_COLLECTION_TYPE = "No collection_type define for nested collection %s."
ERROR_MESSAGE_INVALID_PARAMETER_FOUND = "Found invalid parameter %s in element identifier description %s."
+ERROR_MESSAGE_DUPLICATED_IDENTIFIER_FOUND = "Found duplicated element idenfier name %s."
def api_payload_to_create_params( payload ):
@@ -36,6 +37,7 @@
and verify the structure is valid.
"""
log.debug( "Validating %d element identifiers for collection creation." % len( element_identifiers ) )
+ identifier_names = set()
for element_identifier in element_identifiers:
if "__object__" in element_identifier:
message = ERROR_MESSAGE_INVALID_PARAMETER_FOUND % ( "__model_object__", element_identifier )
@@ -43,6 +45,12 @@
if "name" not in element_identifier:
message = ERROR_MESSAGE_NO_NAME % element_identifier
raise exceptions.RequestParameterInvalidException( message )
+ name = element_identifier[ "name" ]
+ if name in identifier_names:
+ message = ERROR_MESSAGE_DUPLICATED_IDENTIFIER_FOUND % name
+ raise exceptions.RequestParameterInvalidException( message )
+ else:
+ identifier_names.add( name )
src = element_identifier.get( "src", "hda" )
if src not in [ "hda", "hdca", "ldda", "new_collection" ]:
message = ERROR_MESSAGE_UNKNOWN_SRC % src
diff -r f94244370cfa4b6e685a5ea46a2b2d4ede42b749 -r 8f1f131bdeb49f876a06d748d97cfb48ec329550 test/api/test_dataset_collections.py
--- a/test/api/test_dataset_collections.py
+++ b/test/api/test_dataset_collections.py
@@ -106,6 +106,19 @@
create_response = self._post( "dataset_collections", payload )
self._assert_status_code_is( create_response, 403 )
+ def test_enforces_unique_names( self ):
+ element_identifiers = self.dataset_collection_populator.list_identifiers( self.history_id )
+ element_identifiers[ 2 ][ "name" ] = element_identifiers[ 0 ][ "name" ]
+ payload = dict(
+ instance_type="history",
+ history_id=self.history_id,
+ element_identifiers=json.dumps(element_identifiers),
+ collection_type="list",
+ )
+
+ create_response = self._post( "dataset_collections", payload )
+ self._assert_status_code_is( create_response, 400 )
+
def _check_create_response( self, create_response ):
self._assert_status_code_is( create_response, 200 )
dataset_collection = create_response.json()
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: guerler: Ui: Update ui-tabs style
by commits-noreply@bitbucket.org 27 May '14
by commits-noreply@bitbucket.org 27 May '14
27 May '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/f94244370cfa/
Changeset: f94244370cfa
User: guerler
Date: 2014-05-27 06:05:24
Summary: Ui: Update ui-tabs style
Affected #: 3 files
diff -r c42da63a174f7695eb48ee71107b18589e034418 -r f94244370cfa4b6e685a5ea46a2b2d4ede42b749 config/plugins/visualizations/charts/static/build-app.js
--- a/config/plugins/visualizations/charts/static/build-app.js
+++ b/config/plugins/visualizations/charts/static/build-app.js
@@ -3,4 +3,4 @@
// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore may be freely distributed under the MIT license.
-define("mvc/ui/ui-modal",[],function(){var e=Backbone.View.extend({elMain:"body",optionsDefault:{title:"ui-modal",body:"",backdrop:!0,height:null,width:null,closing_events:!1,closing_callback:null},buttonList:{},initialize:function(e){e&&this._create(e)},show:function(e){this.initialize(e),this.options.height?(this.$body.css("height",this.options.height),this.$body.css("overflow","hidden")):this.$body.css("max-height",$(window).height()/2),this.options.width&&this.$dialog.css("width",this.options.width),this.visible?this.$el.show():this.$el.fadeIn("fast"),this.visible=!0},hide:function(){this.visible=!1,this.$el.fadeOut("fast"),this.options.closing_callback&&this.options.closing_callback()},enableButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).prop("disabled",!1)},disableButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).prop("disabled",!0)},showButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).show()},hideButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).hide()},getButton:function(e){var t=this.buttonList[e];return this.$buttons.find("#"+t)},scrollTop:function(){return this.$body.scrollTop()},_create:function(e){var t=this;this.options=_.defaults(e,this.optionsDefault),this.options.body=="progress"&&(this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')),this.$el&&(this.$el.remove(),$(document).off("keyup.ui-modal")),this.setElement(this._template(this.options.title)),this.$dialog=this.$el.find(".modal-dialog"),this.$body=this.$el.find(".modal-body"),this.$footer=this.$el.find(".modal-footer"),this.$buttons=this.$el.find(".buttons"),this.$backdrop=this.$el.find(".modal-backdrop"),this.$body.html(this.options.body),this.options.backdrop||this.$backdrop.removeClass("in");if(this.options.buttons){this.buttonList={};var n=0;$.each(this.options.buttons,function(e,r){var i="button-"+n++;t.$buttons.append($('<button id="'+i+'"></button>').text(e).click(r)).append(" "),t.buttonList[e]=i})}else this.$footer.hide();$(this.elMain).append($(this.el)),this.options.closing_events&&($(document).on("keyup.ui-modal",function(e){e.keyCode==27&&t.hide()}),this.$el.find(".modal-backdrop").on("click",function(){t.hide()}))},_template:function(e){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+e+"</h4>"+"</div>"+'<div class="modal-body" style="position: static;"></div>'+'<div class="modal-footer">'+'<div class="buttons" style="float: right;"></div>'+"</div>"+"</div"+"</div>"+"</div>"}});return{View:e}}),function(){var e=this,t=e._,n={},r=Array.prototype,i=Object.prototype,s=Function.prototype,o=r.push,u=r.slice,a=r.concat,f=i.toString,l=i.hasOwnProperty,c=r.forEach,h=r.map,p=r.reduce,d=r.reduceRight,v=r.filter,m=r.every,g=r.some,y=r.indexOf,b=r.lastIndexOf,w=Array.isArray,E=Object.keys,S=s.bind,x=function(e){if(e instanceof x)return e;if(!(this instanceof x))return new x(e);this._wrapped=e};typeof exports!="undefined"?(typeof module!="undefined"&&module.exports&&(exports=module.exports=x),exports._=x):e._=x,x.VERSION="1.4.4";var T=x.each=x.forEach=function(e,t,r){if(e==null)return;if(c&&e.forEach===c)e.forEach(t,r);else if(e.length===+e.length){for(var i=0,s=e.length;i<s;i++)if(t.call(r,e[i],i,e)===n)return}else for(var o in e)if(x.has(e,o)&&t.call(r,e[o],o,e)===n)return};x.map=x.collect=function(e,t,n){var r=[];return e==null?r:h&&e.map===h?e.map(t,n):(T(e,function(e,i,s){r.push(t.call(n,e,i,s))}),r)};var N="Reduce of empty array with no initial value";x.reduce=x.foldl=x.inject=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(p&&e.reduce===p)return r&&(t=x.bind(t,r)),i?e.reduce(t,n):e.reduce(t);T(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError(N);return n},x.reduceRight=x.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(d&&e.reduceRight===d)return r&&(t=x.bind(t,r)),i?e.reduceRight(t,n):e.reduceRight(t);var s=e.length;if(s!==+s){var o=x.keys(e);s=o.length}T(e,function(u,a,f){a=o?o[--s]:--s,i?n=t.call(r,n,e[a],a,f):(n=e[a],i=!0)});if(!i)throw new TypeError(N);return n},x.find=x.detect=function(e,t,n){var r;return C(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},x.filter=x.select=function(e,t,n){var r=[];return e==null?r:v&&e.filter===v?e.filter(t,n):(T(e,function(e,i,s){t.call(n,e,i,s)&&r.push(e)}),r)},x.reject=function(e,t,n){return x.filter(e,function(e,r,i){return!t.call(n,e,r,i)},n)},x.every=x.all=function(e,t,r){t||(t=x.identity);var i=!0;return e==null?i:m&&e.every===m?e.every(t,r):(T(e,function(e,s,o){if(!(i=i&&t.call(r,e,s,o)))return n}),!!i)};var C=x.some=x.any=function(e,t,r){t||(t=x.identity);var i=!1;return e==null?i:g&&e.some===g?e.some(t,r):(T(e,function(e,s,o){if(i||(i=t.call(r,e,s,o)))return n}),!!i)};x.contains=x.include=function(e,t){return e==null?!1:y&&e.indexOf===y?e.indexOf(t)!=-1:C(e,function(e){return e===t})},x.invoke=function(e,t){var n=u.call(arguments,2),r=x.isFunction(t);return x.map(e,function(e){return(r?t:e[t]).apply(e,n)})},x.pluck=function(e,t){return x.map(e,function(e){return e[t]})},x.where=function(e,t,n){return x.isEmpty(t)?n?void 0:[]:x[n?"find":"filter"](e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},x.findWhere=function(e,t){return x.where(e,t,!0)},x.max=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.max.apply(Math,e);if(!t&&x.isEmpty(e))return-Infinity;var r={computed:-Infinity,value:-Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},x.min=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.min.apply(Math,e);if(!t&&x.isEmpty(e))return Infinity;var r={computed:Infinity,value:Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o<r.computed&&(r={value:e,computed:o})}),r.value},x.shuffle=function(e){var t,n=0,r=[];return T(e,function(e){t=x.random(n++),r[n-1]=r[t],r[t]=e}),r};var k=function(e){return x.isFunction(e)?e:function(t){return t[e]}};x.sortBy=function(e,t,n){var r=k(t);return x.pluck(x.map(e,function(e,t,i){return{value:e,index:t,criteria:r.call(n,e,t,i)}}).sort(function(e,t){var n=e.criteria,r=t.criteria;if(n!==r){if(n>r||n===void 0)return 1;if(n<r||r===void 0)return-1}return e.index<t.index?-1:1}),"value")};var L=function(e,t,n,r){var i={},s=k(t==null?x.identity:t);return T(e,function(t,o){var u=s.call(n,t,o,e);r(i,u,t)}),i};x.groupBy=function(e,t,n){return L(e,t,n,function(e,t,n){(x.has(e,t)?e[t]:e[t]=[]).push(n)})},x.countBy=function(e,t,n){return L(e,t,n,function(e,t){x.has(e,t)||(e[t]=0),e[t]++})},x.sortedIndex=function(e,t,n,r){n=n==null?x.identity:k(n);var i=n.call(r,t),s=0,o=e.length;while(s<o){var u=s+o>>>1;n.call(r,e[u])<i?s=u+1:o=u}return s},x.toArray=function(e){return e?x.isArray(e)?u.call(e):e.length===+e.length?x.map(e,x.identity):x.values(e):[]},x.size=function(e){return e==null?0:e.length===+e.length?e.length:x.keys(e).length},x.first=x.head=x.take=function(e,t,n){return e==null?void 0:t!=null&&!n?u.call(e,0,t):e[0]},x.initial=function(e,t,n){return u.call(e,0,e.length-(t==null||n?1:t))},x.last=function(e,t,n){return e==null?void 0:t!=null&&!n?u.call(e,Math.max(e.length-t,0)):e[e.length-1]},x.rest=x.tail=x.drop=function(e,t,n){return u.call(e,t==null||n?1:t)},x.compact=function(e){return x.filter(e,x.identity)};var A=function(e,t,n){return T(e,function(e){x.isArray(e)?t?o.apply(n,e):A(e,t,n):n.push(e)}),n};x.flatten=function(e,t){return A(e,t,[])},x.without=function(e){return x.difference(e,u.call(arguments,1))},x.uniq=x.unique=function(e,t,n,r){x.isFunction(t)&&(r=n,n=t,t=!1);var i=n?x.map(e,n,r):e,s=[],o=[];return T(i,function(n,r){if(t?!r||o[o.length-1]!==n:!x.contains(o,n))o.push(n),s.push(e[r])}),s},x.union=function(){return x.uniq(a.apply(r,arguments))},x.intersection=function(e){var t=u.call(arguments,1);return x.filter(x.uniq(e),function(e){return x.every(t,function(t){return x.indexOf(t,e)>=0})})},x.difference=function(e){var t=a.apply(r,u.call(arguments,1));return x.filter(e,function(e){return!x.contains(t,e)})},x.zip=function(){var e=u.call(arguments),t=x.max(x.pluck(e,"length")),n=new Array(t);for(var r=0;r<t;r++)n[r]=x.pluck(e,""+r);return n},x.unzip=function(e){var t=[];return x.each(e,function(e,n){x.each(e,function(e,r){t.length<=r&&(t[r]=[]),t[r][n]=e})}),t},x.object=function(e,t){if(e==null)return{};var n={};for(var r=0,i=e.length;r<i;r++)t?n[e[r]]=t[r]:n[e[r][0]]=e[r][1];return n},x.indexOf=function(e,t,n){if(e==null)return-1;var r=0,i=e.length;if(n){if(typeof n!="number")return r=x.sortedIndex(e,t),e[r]===t?r:-1;r=n<0?Math.max(0,i+n):n}if(y&&e.indexOf===y)return e.indexOf(t,n);for(;r<i;r++)if(e[r]===t)return r;return-1},x.lastIndexOf=function(e,t,n){if(e==null)return-1;var r=n!=null;if(b&&e.lastIndexOf===b)return r?e.lastIndexOf(t,n):e.lastIndexOf(t);var i=r?n:e.length;while(i--)if(e[i]===t)return i;return-1},x.range=function(e,t,n){arguments.length<=1&&(t=e||0,e=0),n=arguments[2]||1;var r=Math.max(Math.ceil((t-e)/n),0),i=0,s=new Array(r);while(i<r)s[i++]=e,e+=n;return s};var O=function(){};x.bind=function(e,t){var n,r;if(e.bind===S&&S)return S.apply(e,u.call(arguments,1));if(!x.isFunction(e))throw new TypeError;return n=u.call(arguments,2),r=function(){if(this instanceof r){O.prototype=e.prototype;var i=new O;O.prototype=null;var s=e.apply(i,n.concat(u.call(arguments)));return Object(s)===s?s:i}return e.apply(t,n.concat(u.call(arguments)))}},x.partial=function(e){var t=u.call(arguments,1);return function(){return e.apply(this,t.concat(u.call(arguments)))}},x.bindAll=function(e){var t=u.call(arguments,1);if(t.length===0)throw new Error("bindAll must be passed function names");return T(t,function(t){e[t]=x.bind(e[t],e)}),e},x.memoize=function(e,t){var n={};return t||(t=x.identity),function(){var r=t.apply(this,arguments);return x.has(n,r)?n[r]:n[r]=e.apply(this,arguments)}},x.delay=function(e,t){var n=u.call(arguments,2);return setTimeout(function(){return e.apply(null,n)},t)},x.defer=function(e){return x.delay.apply(x,[e,1].concat(u.call(arguments,1)))},x.throttle=function(e,t,n){var r,i,s,o,u=0,a=function(){u=new Date,s=null,o=e.apply(r,i)};return function(){var f=new Date;!u&&n===!1&&(u=f);var l=t-(f-u);return r=this,i=arguments,l<=0?(clearTimeout(s),s=null,u=f,o=e.apply(r,i)):s||(s=setTimeout(a,l)),o}},x.debounce=function(e,t,n){var r,i;return function(){var s=this,o=arguments,u=function(){r=null,n||(i=e.apply(s,o))},a=n&&!r;return clearTimeout(r),r=setTimeout(u,t),a&&(i=e.apply(s,o)),i}},x.once=function(e){var t=!1,n;return function(){return t?n:(t=!0,n=e.apply(this,arguments),e=null,n)}},x.wrap=function(e,t){return function(){var n=[e];return o.apply(n,arguments),t.apply(this,n)}},x.compose=function(){var e=arguments;return function(){var t=arguments;for(var n=e.length-1;n>=0;n--)t=[e[n].apply(this,t)];return t[0]}},x.after=function(e,t){return e<=0?t():function(){if(--e<1)return t.apply(this,arguments)}},x.keys=E||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)x.has(e,n)&&t.push(n);return t},x.values=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push(e[n]);return t},x.pairs=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push([n,e[n]]);return t},x.invert=function(e){var t={};for(var n in e)x.has(e,n)&&(t[e[n]]=n);return t},x.functions=x.methods=function(e){var t=[];for(var n in e)x.isFunction(e[n])&&t.push(n);return t.sort()},x.extend=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]=t[n]}),e},x.pick=function(e){var t={},n=a.apply(r,u.call(arguments,1));return T(n,function(n){n in e&&(t[n]=e[n])}),t},x.omit=function(e){var t={},n=a.apply(r,u.call(arguments,1));for(var i in e)x.contains(n,i)||(t[i]=e[i]);return t},x.defaults=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]===void 0&&(e[n]=t[n])}),e},x.clone=function(e){return x.isObject(e)?x.isArray(e)?e.slice():x.extend({},e):e},x.tap=function(e,t){return t(e),e};var M=function(e,t,n,r){if(e===t)return e!==0||1/e==1/t;if(e==null||t==null)return e===t;e instanceof x&&(e=e._wrapped),t instanceof x&&(t=t._wrapped);var i=f.call(e);if(i!=f.call(t))return!1;switch(i){case"[object String]":return e==String(t);case"[object Number]":return e!=+e?t!=+t:e==0?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if(typeof e!="object"||typeof t!="object")return!1;var s=n.length;while(s--)if(n[s]==e)return r[s]==t;n.push(e),r.push(t);var o=0,u=!0;if(i=="[object Array]"){o=e.length,u=o==t.length;if(u)while(o--)if(!(u=M(e[o],t[o],n,r)))break}else{var a=e.constructor,l=t.constructor;if(a!==l&&!(x.isFunction(a)&&a instanceof a&&x.isFunction(l)&&l instanceof l))return!1;for(var c in e)if(x.has(e,c)){o++;if(!(u=x.has(t,c)&&M(e[c],t[c],n,r)))break}if(u){for(c in t)if(x.has(t,c)&&!(o--))break;u=!o}}return n.pop(),r.pop(),u};x.isEqual=function(e,t){return M(e,t,[],[])},x.isEmpty=function(e){if(e==null)return!0;if(x.isArray(e)||x.isString(e))return e.length===0;for(var t in e)if(x.has(e,t))return!1;return!0},x.isElement=function(e){return!!e&&e.nodeType===1},x.isArray=w||function(e){return f.call(e)=="[object Array]"},x.isObject=function(e){return e===Object(e)},T(["Arguments","Function","String","Number","Date","RegExp"],function(e){x["is"+e]=function(t){return f.call(t)=="[object "+e+"]"}}),x.isArguments(arguments)||(x.isArguments=function(e){return!!e&&!!x.has(e,"callee")}),typeof /./!="function"&&(x.isFunction=function(e){return typeof e=="function"}),x.isFinite=function(e){return isFinite(e)&&!isNaN(parseFloat(e))},x.isNaN=function(e){return x.isNumber(e)&&e!=+e},x.isBoolean=function(e){return e===!0||e===!1||f.call(e)=="[object Boolean]"},x.isNull=function(e){return e===null},x.isUndefined=function(e){return e===void 0},x.has=function(e,t){return l.call(e,t)},x.noConflict=function(){return e._=t,this},x.identity=function(e){return e},x.times=function(e,t,n){var r=Array(e);for(var i=0;i<e;i++)r[i]=t.call(n,i);return r},x.random=function(e,t){return t==null&&(t=e,e=0),e+Math.floor(Math.random()*(t-e+1))};var _={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};_.unescape=x.invert(_.escape);var D={escape:new RegExp("["+x.keys(_.escape).join("")+"]","g"),unescape:new RegExp("("+x.keys(_.unescape).join("|")+")","g")};x.each(["escape","unescape"],function(e){x[e]=function(t){return t==null?"":(""+t).replace(D[e],function(t){return _[e][t]})}}),x.result=function(e,t){if(e==null)return void 0;var n=e[t];return x.isFunction(n)?n.call(e):n},x.mixin=function(e){T(x.functions(e),function(t){var n=x[t]=e[t];x.prototype[t]=function(){var e=[this._wrapped];return o.apply(e,arguments),F.call(this,n.apply(x,e))}})};var P=0;x.uniqueId=function(e){var t=++P+"";return e?e+t:t},x.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var H=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},j=/\\|'|\r|\n|\t|\u2028|\u2029/g;x.template=function(e,t,n){var r;n=x.defaults({},n,x.templateSettings);var i=new RegExp([(n.escape||H).source,(n.interpolate||H).source,(n.evaluate||H).source].join("|")+"|$","g"),s=0,o="__p+='";e.replace(i,function(t,n,r,i,u){return o+=e.slice(s,u).replace(j,function(e){return"\\"+B[e]}),n&&(o+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),r&&(o+="'+\n((__t=("+r+"))==null?'':__t)+\n'"),i&&(o+="';\n"+i+"\n__p+='"),s=u+t.length,t}),o+="';\n",n.variable||(o="with(obj||{}){\n"+o+"}\n"),o="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{r=new Function(n.variable||"obj","_",o)}catch(u){throw u.source=o,u}if(t)return r(t,x);var a=function(e){return r.call(this,e,x)};return a.source="function("+(n.variable||"obj")+"){\n"+o+"}",a},x.chain=function(e){return x(e).chain()};var F=function(e){return this._chain?x(e).chain():e};x.mixin(x),T(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];x.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),(e=="shift"||e=="splice")&&n.length===0&&delete n[0],F.call(this,n)}}),T(["concat","join","slice"],function(e){var t=r[e];x.prototype[e]=function(){return F.call(this,t.apply(this._wrapped,arguments))}}),x.extend(x.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}.call(this),define("libs/underscore",function(e){return function(){var t,n;return t||e._}}(this)),define("utils/utils",["libs/underscore"],function(e){function t(e,t,r){n("GET",e,{},t,r)}function n(e,t,n,r,i){if(e=="GET"||e=="DELETE")t.indexOf("?")==-1?t+="?":t+="&",t+=$.param(n);var s=new XMLHttpRequest;s.open(e,t,!0),s.setRequestHeader("Accept","application/json"),s.setRequestHeader("Cache-Control","no-cache"),s.setRequestHeader("X-Requested-With","XMLHttpRequest"),s.setRequestHeader("Content-Type","application/json"),s.onloadend=function(){var e=s.status;try{response=jQuery.parseJSON(s.responseText)}catch(t){response=s.responseText}e==200?r&&r(response):i&&i(response)},e=="GET"||e=="DELETE"?s.send():s.send(JSON.stringify(n))}function r(e,t){var n=$('<div class="'+e+'"></div>');n.appendTo(":eq(0)");var r=n.css(t);return n.remove(),r}function i(e){$('link[href^="'+e+'"]').length||$('<link href="'+galaxy_config.root+e+'" rel="stylesheet">').appendTo("head")}function s(t,n){return t?e.defaults(t,n):n}function o(e,t){var n="";if(e>=1e11)e/=1e11,n="TB";else if(e>=1e8)e/=1e8,n="GB";else if(e>=1e5)e/=1e5,n="MB";else if(e>=100)e/=100,n="KB";else{if(!(e>0))return"<strong>-</strong>";e*=10,n="b"}var r=Math.round(e)/10;return t?r+" "+n:"<strong>"+r+"</strong> "+n}function u(){return(new Date).getTime().toString(36)}function a(e){var t=$("<p></p>");return t.append(e),t}function f(){var e=new Date,t=(e.getHours()<10?"0":"")+e.getHours(),n=(e.getMinutes()<10?"0":"")+e.getMinutes(),r=e.getDate()+"/"+(e.getMonth()+1)+"/"+e.getFullYear()+", "+t+":"+n;return r}return{cssLoadFile:i,cssGetAttribute:r,get:t,merge:s,bytesToString:o,uuid:u,time:f,wrap:a,request:n}}),define("mvc/ui/ui-portlet",["utils/utils"],function(e){var t=Backbone.View.extend({visible:!1,optionsDefault:{title:"",icon:"",buttons:null,body:null,height:null,operations:null,placement:"bottom",overflow:"auto"},$title:null,$content:null,$buttons:null,$operations:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.$content=this.$el.find("#content"),this.$title=this.$el.find("#title-text"),this.options.height&&(this.$el.find("#body").css("height",this.options.height),this.$el.find("#content").css("overflow",this.options.overflow)),this.$buttons=$(this.el).find("#buttons");if(this.options.buttons){var n=this;$.each(this.options.buttons,function(e,t){t.$el.prop("id",e),n.$buttons.append(t.$el)})}else this.$buttons.remove();this.$operations=$(this.el).find("#operations");if(this.options.operations){var n=this;$.each(this.options.operations,function(e,t){t.$el.prop("id",e),n.$operations.append(t.$el)})}this.options.body&&this.append(this.options.body)},append:function(t){this.$content.append(e.wrap(t))},content:function(){return this.$content},show:function(){this.$el.fadeIn("fast"),this.visible=!0},hide:function(){this.$el.fadeOut("fast"),this.visible=!1},enableButton:function(e){this.$buttons.find("#"+e).prop("disabled",!1)},disableButton:function(e){this.$buttons.find("#"+e).prop("disabled",!0)},hideOperation:function(e){this.$operations.find("#"+e).hide()},showOperation:function(e){this.$operations.find("#"+e).show()},setOperation:function(e,t){var n=this.$operations.find("#"+e);n.off("click"),n.on("click",t)},title:function(e){var t=this.$title;return e&&t.html(e),t.html()},_template:function(e){var t='<div class="toolForm portlet-view no-highlight">';if(e.title||e.icon)t+='<div id="title" class="toolFormTitle portlet-title"><div id="operations" class="portlet-operations"/><div style="overflow: hidden;">',e.icon&&(t+='<div class="portlet-title-icon fa '+e.icon+'"> </div>'),t+='<div id="title-text" class="portlet-title-text">'+e.title+"</div>",t+="</div></div>";return t+='<div id="body" class="toolFormBody portlet-body">',e.placement=="top"&&(t+='<div id="buttons" class="portlet-buttons"/>'),t+='<div id="content" class="portlet-content"/>',e.placement=="bottom"&&(t+='<div id="buttons" class="portlet-buttons"/>'),t+="</div></div>",t}});return{View:t}}),define("plugin/library/ui-select",["utils/utils"],function(e){var t=Backbone.View.extend({optionsDefault:{id:"",cls:"",empty:"No data available",visible:!0,wait:!1},selected:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.selected=this.options.value,this.setElement(this._template(this.options)),this.$select=this.$el.find("#select"),this.$icon=this.$el.find("#icon");var n=this;this.$select.on("change",function(){n.value(n.$select.val())}),this.on("change",function(){n.options.onchange&&n.options.onchange(n.value())}),this._refresh(),this.options.visible||this.hide(),this.options.wait?this.wait():this.show()},value:function(e){var t=this.selected;e!==undefined&&(this.selected=e,this.$select.val(e));var n=this.selected;return n&&n!=t&&this.options.onchange&&this.options.onchange(n),n},first:function(){var e=this.$select.find("option");return e.length>0?e.val():undefined},text:function(){return this.$select.find("option:selected").text()},show:function(){this.$icon.removeClass(),this.$icon.addClass("fa fa-caret-down"),this.$select.show(),this.$el.show()},hide:function(){this.$el.hide()},wait:function(){this.$icon.removeClass(),this.$icon.addClass("fa fa-spinner fa-spin"),this.$select.hide()},disabled:function(){return this.$select.is(":disabled")},enable:function(){this.$select.prop("disabled",!1)},disable:function(){this.$select.prop("disabled",!0)},add:function(e){this.$select.append(this._templateOption(e)),this._refresh()},del:function(e){this.$select.find("option[value="+e+"]").remove(),this.$select.trigger("change"),this._refresh()},update:function(e){this.$select.find("option").remove();for(var t in e)this.$select.append(this._templateOption(e[t]));this._refresh()},setOnChange:function(e){this.options.onchange=e},exists:function(e){return this.$select.find("option[value="+e+"]").length>0},_refresh:function(){this.$select.find("option[value=null]").remove();var e=this.$select.find("option").length;e==0?(this.disable(),this.$select.append(this._templateOption({value:"null",label:this.options.empty}))):this.enable(),this.selected&&this.$select.val(this.selected)},_templateOption:function(e){return'<option value="'+e.value+'">'+e.label+"</option>"},_template:function(e){var t='<div id="'+e.id+'" class="styled-select">'+'<div class="button">'+'<i id="icon"/>'+"</div>"+'<select id="select" class="select '+e.cls+" "+e.id+'">';for(key in e.data){var n=e.data[key],r="";if(n.value==e.value||n.value=="")r="selected";t+='<option value="'+n.value+'" '+r+">"+n.label+"</option>"}return t+="</select></div>",t}});return{View:t}}),define("plugin/library/ui",["utils/utils","plugin/library/ui-select"],function(e,t){var n=Backbone.View.extend({optionsDefault:{url:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options))},_template:function(e){return'<img src="'+e.url+'"/>'}}),r=Backbone.View.extend({optionsDefault:{title:"",cls:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options))},title:function(e){this.$el.find("b").html(e)},_template:function(e){return'<label class="'+e.cls+'"><b>'+e.title+"</b></label>"},value:function(){return options.title}}),i=Backbone.View.extend({optionsDefault:{title:"",cls:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options))},title:function(e){this.$el.html(e)},_template:function(e){return'<div class="'+e.cls+'">'+e.title+"</div>"},value:function(){return options.title}}),s=Backbone.View.extend({optionsDefault:{id:null,title:"","float":"right",cls:"btn-default",icon:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){var t='<button id="'+e.id+'" type="submit" style="margin-right: 5px; float: '+e.float+';" type="button" class="btn '+e.cls+'">';return e.icon&&(t+='<i class="icon fa '+e.icon+'"></i> '),t+=e.title+"</button>",t}}),o=Backbone.View.extend({optionsDefault:{title:"",id:null,"float":"right",cls:"icon-btn",icon:"",tooltip:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){var t="";e.title&&(t="width: auto;");var n='<div id="'+e.id+'" style="margin-right: 5px; float: '+e.float+"; "+t+'" class="'+e.cls+'">';return e.title?n+='<div style="margin-right: 5px; margin-left: 5px;"><i class="icon fa '+e.icon+'"/> '+'<span style="position: relative; font-size: 0.8em; font-weight: normal; top: -1px;">'+e.title+"</span>"+"</div>":n+='<i class="icon fa '+e.icon+'"/>',n+="</div>",n}}),u=Backbone.View.extend({optionsDefault:{"float":"right",icon:"",tooltip:"",placement:"bottom",title:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){return'<div><span class="fa '+e.icon+'" style="font-size: 1.2em;"/> '+e.title+"</div>"}}),a=Backbone.View.extend({optionsDefault:{title:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick)},_template:function(e){return'<div><a href="javascript:void(0)">'+e.title+"</a></div>"}}),f=Backbone.View.extend({optionsDefault:{message:"",status:"info",persistent:!1},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement("<div></div>")},update:function(t){this.options=e.merge(t,this.optionsDefault);if(t.message!=""){this.$el.html(this._template(this.options)),this.$el.fadeIn();if(!t.persistent){var n=this;window.setTimeout(function(){n.$el.is(":visible")?n.$el.fadeOut():n.$el.hide()},3e3)}}else this.$el.fadeOut()},_template:function(e){return'<div class="alert alert-'+e.status+'" style="padding: 2px 2px 2px 10px;">'+e.message+"</div>"}}),l=Backbone.View.extend({optionsDefault:{onclick:null,searchword:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options));var n=this;this.options.onclick&&this.$el.on("submit",function(e){var t=n.$el.find("#search");n.options.onclick(t.val())})},_template:function(e){return'<div class="search"><form onsubmit="return false;"><input id="search" class="form-control input-sm" type="text" name="search" placeholder="Search..." value="'+e.searchword+'">'+'<button type="submit" class="btn search-btn">'+'<i class="fa fa-search"></i>'+"</button>"+"</form>"+"</div>"}}),c=Backbone.View.extend({optionsDefault:{title:"Unlabeled",body:null},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.options.body&&this.$el.find(".body").append(this.options.body)},_template:function(e){return'<div id="title" class="title">'+e.title+":"+"</div>"}}),h=Backbone.View.extend({optionsDefault:{id:"",title:"",target:"",href:"",onunload:null,onclick:null,visible:!0,icon:null,tag:""},$menu:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement($(this._template(this.options)));var n=$(this.el).find(".root"),r=this;n.on("click",function(e){e.preventDefault(),r.options.onclick&&r.options.onclick()}),this.options.visible||this.hide()},show:function(){$(this.el).show()},hide:function(){$(this.el).hide()},addMenu:function(t){var n={title:"",target:"",href:"",onclick:null,divider:!1,icon:null};n=e.merge(t,n),this.$menu||($(this.el).append(this._templateMenu()),this.$menu=$(this.el).find(".menu"));var r=$(this._templateMenuItem(n));r.on("click",function(e){e.preventDefault(),n.onclick&&n.onclick()}),this.$menu.append(r),n.divider&&this.$menu.append($(this._templateDivider()))},_templateMenuItem:function(e){var t='<li><a href="'+e.href+'" target="'+e.target+'">';return e.icon&&(t+='<i class="fa '+e.icon+'"></i>'),t+=" "+e.title+"</a>"+"</li>",t},_templateMenu:function(){return'<ul class="menu dropdown-menu pull-right" role="menu"></ul>'},_templateDivider:function(){return'<li class="divider"></li>'},_template:function(e){var t='<div id="'+e.id+'" class="button-menu btn-group">'+'<button type="button" class="root btn btn-default dropdown-toggle" data-toggle="dropdown">';return e.icon&&(t+='<i class="fa '+e.icon+'"></i>'),t+="</button></div>",t}}),p=Backbone.View.extend({optionsDefault:{value:"",type:"text",placeholder:"",disabled:!1,visible:!0},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.options.disabled&&this.$el.prop("disabled",!0),this.options.visible||this.$el.hide();var n=this;this.$el.on("input",function(){n.options.onchange&&n.options.onchange(n.$el.val())})},value:function(e){return e!==undefined&&this.$el.val(e),this.$el.val()},_template:function(e){return'<input id="'+e.id+'" type="'+e.type+'" value="'+e.value+'" placeholder="'+e.placeholder+'" class="form-control">'}});return{Label:r,Image:n,Button:s,Icon:u,ButtonIcon:o,Input:p,Anchor:a,Message:f,Searchbox:l,Title:c,Text:i,Select:t,ButtonMenu:h}}),define("plugin/library/jobs",["utils/utils"],function(e){return Backbone.Model.extend({initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault)},request:function(e,t,n,r,i){var s=this;e.state("wait","Requesting job results...");var o=e.get("dataset_id_job");o!=""?s._wait(e,r,i):s._submit(e,t,n,r,i)},cleanup:function(t){var n=this,r=t.get("dataset_id_job");r!=""&&(e.request("PUT",config.root+"api/histories/none/contents/"+r,{deleted:!0},function(){n._refreshHdas()}),t.set("dataset_id_job",""))},_submit:function(t,n,r,i,s){var o=this,u=t.id,a=t.get("type"),f=this.app.types.get(a);data={tool_id:"charts",inputs:{input:{id:t.get("dataset_id"),src:"hda"},module:f.execute,columns:r,settings:n}},t.state("wait","Sending job request..."),e.request("POST",config.root+"api/tools",data,function(e){if(!e.outputs||e.outputs.length==0)t.state("failed","Job submission failed. No response."),s&&s();else{o._refreshHdas();var n=e.outputs[0];t.state("wait","Your job has been queued. You may close the browser window. The job will run in the background."),t.set("dataset_id_job",n.id),this.app.storage.save(),o._wait(t,i,s)}},function(e){var n="";e&&e.message&&e.message.data&&e.message.data.input&&(n=e.message.data.input+"."),t.state("failed","This visualization requires the 'charts' tool. Please make sure it is installed. "+n),s&&s()})},_wait:function(t,n,r){var i=this;e.request("GET",config.root+"api/datasets/"+t.get("dataset_id_job"),{},function(e){var s=!1;switch(e.state){case"ok":t.state("wait","Job completed successfully..."),n&&n(e),s=!0;break;case"error":t.state("failed","Job has failed. Please check the history for details."),r&&r(e),s=!0;break;case"running":t.state("wait","Your job is running. You may close the browser window. The job will continue in the background.")}s||setTimeout(function(){i._wait(t,n,r)},i.app.config.get("query_timeout"))})},_refreshHdas:function(){Galaxy&&Galaxy.currHistoryPanel&&Galaxy.currHistoryPanel.refreshContents()}})}),define("plugin/library/datasets",["utils/utils"],function(e){return Backbone.Collection.extend({list:{},cache:{},initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault)},request:function(e,t,n){var r=this;e.groups?this._get_dataset(e.id,function(){r._get(e,t)}):this._get_dataset(e.id,t,n)},_get_dataset:function(t,n,r){var i=this.list[t];if(i){n(i);return}var s=this;e.request("GET",config.root+"api/datasets/"+t,{},function(e){switch(e.state){case"error":r&&r(e);break;default:s.list[t]=e,n(e)}})},_block_id:function(e,t){return e.id+"_"+e.start+"_"+e.end+"_"+t},_get:function(e,t){e.start||(e.start=0),e.end||(e.end=this.app.config.get("query_limit"));var n=[],r={},i=0;for(var s in e.groups){var o=e.groups[s];for(var u in o.columns){var a=o.columns[u].index,f=this._block_id(e,a);if(this.cache[f]||a==="auto")continue;!r[a]&&a!==undefined&&(r[a]=i,n.push(a),i++)}}if(n.length==0){this._fill_from_cache(e),t(e);return}var l={dataset_id:e.id,start:e.start,end:e.end,columns:n},c=this;this._fetch(l,function(r){for(var i in r){var s=n[i],o=c._block_id(e,s);c.cache[o]=r[i]}c._fill_from_cache(e),t(e)})},_fill_from_cache:function(e){console.debug("Datasets::_fill_from_cache() - Filling request from cache.");var t=e.start,n=0;for(var r in e.groups){var i=e.groups[r];for(var s in i.columns){var o=i.columns[s],u=this._block_id(e,o.index),a=this.cache[u];a&&(n=Math.max(n,a.length))}}n==0&&console.debug("FAILED - Datasets::_fill_from_cache() - Invalid range.");for(var r in e.groups){var i=e.groups[r];i.values=[];for(var f=0;f<n;f++)i.values[f]={x:parseInt(f)+t}}for(var r in e.groups){var i=e.groups[r];for(var s in i.columns){var o=i.columns[s];if(o.index=="auto")for(var f=0;f<n;f++){var l=i.values[f];l[s]=parseInt(f)+t}else{var u=this._block_id(e,o.index),a=this.cache[u];for(var f=0;f<n;f++){var l=i.values[f],c=a[f];isNaN(c)&&!o.is_label&&(c=0),l[s]=c}}}}},_fetch:function(t,n){var r=t.start?t.start:0,i=Math.abs(t.end-t.start),s=this.app.config.get("query_limit");if(!i||i>s)i=s;var o=0;t.columns&&(o=t.columns.length,console.debug("Datasets::_fetch() - Fetching "+o+" column(s)")),o==0&&console.debug("Datasets::_fetch() - No columns requested");var u="";for(var a in t.columns)u+=t.columns[a]+",";u=u.substring(0,u.length-1);var f=this;e.request("GET",config.root+"api/datasets/"+t.dataset_id,{data_type:"raw_data",provider:"dataset-column",limit:i,offset:r,indeces:u},function(e){var t=new Array(o);for(var r=0;r<o;r++)t[r]=[];for(var r in e.data){var i=e.data[r];for(var s in i){var u=i[s];u!==undefined&&u!=2147483647&&t[s].push(u)}}console.debug("Datasets::_fetch() - Fetching complete."),n(t)})}})}),define("plugin/models/group",[],function(){return Backbone.Model.extend({defaults:{key:"Data label",date:""},reset:function(){this.clear({silent:!0}).set(this.defaults),this.trigger("reset",this)}})}),define("plugin/models/groups",["plugin/models/group"],function(e){return Backbone.Collection.extend({model:e})}),define("plugin/library/deferred",["utils/utils"],function(e){return Backbone.Model.extend({queue:[],process:{},counter:0,initialize:function(){this.on("refresh",function(){if(this.counter==0)for(var e in this.queue){var t=this.queue[e];this.queue.splice(e,1),t()}})},execute:function(e){this.queue.push(e),this.trigger("refresh")},register:function(){var t=e.uuid();return this.process[t]=!0,this.counter++,console.debug("Deferred:register() - Registering "+t),t},done:function(e){this.process[e]&&(delete this.process[e],this.counter--,console.debug("Deferred:done() - Unregistering "+e),this.trigger("refresh"))},ready:function(){return this.counter==0}})});var Visualization=Backbone.Model.extend({defaults:{config:{}},urlRoot:function(){var e="/api/visualizations";return window.galaxy_config&&galaxy_config.root?galaxy_config.root+e:e},initialize:function(e){_.isObject(e.config)&&_.isObject(this.defaults.config)&&_.defaults(e.config,this.defaults.config),this._setUpListeners()},_setUpListeners:function(){},set:function(e,t){if(e==="config"){var n=this.get("config");_.isObject(n)&&(t=_.extend(_.clone(n),t))}return Backbone.Model.prototype.set.call(this,e,t),this},toString:function(){var e=this.get("id")||"";return this.get("title")&&(e+=":"+this.get("title")),"Visualization("+e+")"}}),VisualizationCollection=Backbone.Collection.extend({model:Visualization,url:function(){return galaxy_config.root+"api/visualizations"},initialize:function(e,t){t=t||{}},set:function(e,t){var n=this;e=_.map(e,function(e){var t=n.get(e.id);if(!t)return e;var r=t.toJSON();return _.extend(r,e),r}),Backbone.Collection.prototype.set.call(this,e,t)},toString:function(){return["VisualizationCollection(",[this.historyId,this.length].join(),")"].join("")}});define("mvc/visualization/visualization-model",function(){}),define("plugin/models/chart",["plugin/models/groups","plugin/library/deferred","mvc/visualization/visualization-model"],function(e,t){return Backbone.Model.extend({defaults:{id:null,title:"",type:"",date:null,state:"",state_info:"",modified:!1,dataset_id:"",dataset_id_job:""},initialize:function(n){this.groups=new e,this.settings=new Backbone.Model,this.deferred=new t},reset:function(){this.clear({silent:!0}).set(this.defaults),this.groups.reset(),this.settings.clear(),this.trigger("reset",this)},copy:function(e){var t=this;t.clear({silent:!0}).set(this.defaults),t.set(e.attributes),t.settings=e.settings.clone(),t.groups.reset(),e.groups.each(function(e){t.groups.add(e.clone())}),t.trigger("change",t)},state:function(e,t){this.set("state",e),this.set("state_info",t),this.trigger("set:state"),console.debug("Chart:state() - "+t+" ("+e+")")}})}),define("plugin/library/storage",["utils/utils","plugin/models/chart","plugin/models/group"],function(e,t,n){return Backbone.Model.extend({vis:null,initialize:function(e){this.app=e,this.chart=this.app.chart,this.options=this.app.options,this.id=this.options.id,this.vis=new Visualization({type:"charts",config:{dataset_id:this.options.config.dataset_id,chart_dict:{}}}),this.id&&(this.vis.id=this.id);var t=this.options.config.chart_dict;t&&(this.vis.get("config").chart_dict=t)},save:function(){var e=this.app.chart;this.vis.get("config").chart_dict={};var t=e.get("title");t!=""&&this.vis.set("title",t);var n={attributes:e.attributes,settings:e.settings.attributes,groups:[]};e.groups.each(function(e){n.groups.push(e.attributes)}),this.vis.get("config").chart_dict=n;var r=this;this.vis.save().fail(function(e,t,n){console.error(e,t,n)}).then(function(e){e&&e.id&&(r.id=e.id)})},load:function(){var e=this.vis.get("config").chart_dict;if(!e.attributes)return!1;this.chart.set(e.attributes),this.chart.state("ok","Loading saved visualization..."),this.chart.settings.set(e.settings);for(var t in e.groups)this.chart.groups.add(new n(e.groups[t]));return this.chart.set("modified",!1),!0}})}),define("plugin/library/screenshot",["libs/underscore"],function(e){function t(e,t,i){var s=new XMLSerializer,o="",u=this,a=0,f=0;return e.find("svg").each(function(){var e=$(this);n(e),a=parseInt(e.css("height")),f=parseInt(e.css("width")),e.attr({version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:f,height:a}),o+=s.serializeToString(this)}),r(e,t,{filename:i||"chart",type:"application/pdf",height:a,width:f,scale:2,svg:o}),o}function n(e){for(var t in document.styleSheets){var n=document.styleSheets[t],r=n.cssRules;if(r)for(var i=0,s=r.length;i<s;i++)try{e.find(r[i].selectorText).each(function(e,t){t.style.cssText+=r[i].style.cssText})}catch(o){}}}function r(e,t,n){var r=e.find("#viewport-form");r.length===0&&(r=$("<form>",{id:"viewport-form",method:"post",action:t,display:"none"}),e.append(r)),r.empty();for(name in n){var i=$("<input/>",{type:"hidden",name:name,value:n[name]});r.append(i)}try{r.submit()}catch(s){console.log(s)}}return{create:t}}),define("plugin/views/viewport",["mvc/ui/ui-portlet","plugin/library/ui","plugin/library/screenshot","utils/utils"],function(e,t,n,r){return Backbone.View.extend({container_list:[],canvas_list:[],initialize:function(e,t){this.app=e,this.chart=this.app.chart,this.options=r.merge(t,this.optionsDefault),this.setElement($(this._template())),this._fullscreen(this.$el,80),this._createContainer("div");var n=this;this.chart.on("redraw",function(){n._draw(n.chart)}),this.chart.on("set:state",function(){var e=n.$el.find("#info"),t=n.$el.find("container"),r=e.find("#icon");r.removeClass(),e.show(),e.find("#text").html(n.chart.get("state_info")),t.hide();var i=n.chart.get("state");switch(i){case"ok":e.hide(),t.show();break;case"failed":r.addClass("icon fa fa-warning");break;default:r.addClass("icon fa fa-spinner fa-spin")}})},show:function(){this.$el.show()},hide:function(){this.$el.hide()},_fullscreen:function(e,t){e.css("height",$(window).height()-t),$(window).resize(function(){e.css("height",$(window).height()-t)})},_createContainer:function(e,t){t=t||1;for(var n in this.container_list)this.container_list[n].remove();this.container_list=[],this.canvas_list=[];for(var n=0;n<t;n++){var r=$(this._templateContainer(e,parseInt(100/t)));this.$el.append(r),this.container_list[n]=r,e=="svg"?this.canvas_list[n]=d3.select(r.find("#canvas")[0]):this.canvas_list[n]=r.find("#canvas")}},_draw:function(e){var t=this,n=e.deferred.register(),r=e.get("type");this.chart_settings=this.app.types.get(r);var i=this.chart_settings.use_panels,s=1;i&&(s=e.groups.length),this._createContainer(this.chart_settings.tag,s),e.state("wait","Please wait...");if(!this.chart_settings.execute||this.chart_settings.execute&&e.get("modified"))this.app.jobs.cleanup(e),e.set("modified",!1);var t=this;require(["plugin/charts/"+r+"/wrapper"],function(r){var i=new r(t.app,{canvas:t.canvas_list});t.chart_settings.execute?t.app.jobs.request(e,t._defaultSettingsString(e),t._defaultRequestString(e),function(){i.draw(n,e,t._defaultRequestDictionary(e))},function(){e.deferred.done(n)}):i.draw(n,e,t._defaultRequestDictionary(e))})},_defaultRequestString:function(e){var t="",n=0,r=this;return e.groups.each(function(e){n++;for(var i in r.chart_settings.columns)t+=i+"_"+n+":"+(parseInt(e.get(i))+1)+", "}),t.substring(0,t.length-2)},_defaultSettingsString:function(e){var t="";for(key in e.settings.attributes)t+=key+":"+e.settings.get(key)+", ";return t.substring(0,t.length-2)},_defaultRequestDictionary:function(e){var t={groups:[]};this.chart_settings.execute?t.id=e.get("dataset_id_job"):t.id=e.get("dataset_id");var n=0,r=this;return e.groups.each(function(e){var i={};for(var s in r.chart_settings.columns){var o=r.chart_settings.columns[s];i[s]={index:e.get(s),is_label:o.is_label}}t.groups.push({key:++n+":"+e.get("key"),columns:i})}),t},_template:function(){return'<div class="charts-viewport"><div id="info" class="info"><span id="icon" class="icon" /><span id="text" class="text" /></div></div>'},_templateContainer:function(e,t){return'<div class="charts-viewport-container" style="width:'+t+'%;">'+'<div id="menu"/>'+"<"+e+' id="canvas" class="charts-viewport-canvas">'+"</div>"}})}),define("plugin/views/viewer",["utils/utils","plugin/library/ui","mvc/ui/ui-portlet","plugin/models/group","plugin/views/viewport","plugin/library/screenshot"],function(e,t,n,r,i,s){return Backbone.View.extend({initialize:function(e,r){this.app=e,this.chart=this.app.chart,this.viewport_view=new i(e);var o=this;this.portlet=new n.View({icon:"fa-bar-chart-o",title:"Viewport",operations:{edit_button:new t.ButtonIcon({icon:"fa-edit",tooltip:"Customize this chart",title:"Editor",onclick:function(){o._wait(o.chart,function(){o.app.go("editor")})}}),picture_button:new t.ButtonIcon({icon:"fa-camera",tooltip:"Download PDF-file ("+o.app.config.get("screenshot_url")+")",title:"Screenshot",onclick:function(){o._wait(o.chart,function(){s.create(o.viewport_view.$el,o.app.config.get("screenshot_url"),o.chart.get("title"))})}})}}),this.portlet.append(this.viewport_view.$el),this.setElement(this.portlet.$el);var o=this;this.chart.on("change:title",function(){o._refreshTitle()})},show:function(){this.$el.show(),$(window).trigger("resize")},hide:function(){this.$el.hide()},_refreshTitle:function(){var e=this.chart.get("title");this.portlet.title(e)},_wait:function(e,t){if(e.deferred.ready())t();else{var n=this;this.app.modal.show({title:"Please wait!",body:"Your chart is currently being processed. Please wait and try again.",buttons:{Close:function(){n.app.modal.hide()},Retry:function(){n.app.modal.hide(),setTimeout(function(){n._wait(e,t)},n.app.config.get("query_timeout"))}}})}}})}),define("mvc/ui/ui-tabs",["utils/utils"],function(e){var t=Backbone.View.extend({visible:!1,list:{},$nav:null,$content:null,first_tab:null,optionsDefault:{title_new:"",operations:null,onnew:null},initialize:function(t){this.options=e.merge(t,this.optionsDefault);var n=$(this._template(this.options));this.$nav=n.find(".tab-navigation"),this.$content=n.find(".tab-content"),this.setElement(n),this.list={};var r=this;this.options.operations&&$.each(this.options.operations,function(e,t){t.$el.prop("id",e),r.$nav.find(".operations").append(t.$el)});if(this.options.onnew){var i=$(this._template_tab_new(this.options));this.$nav.append(i),i.tooltip({title:"Add a new tab",placement:"bottom",container:r.$el}),i.on("click",function(e){i.tooltip("hide"),r.options.onnew()})}},add:function(e){var t=e.id,n={$title:$(this._template_tab(e)),$content:$(this._template_tab_content(e)),removable:e.ondel?!0:!1};this.list[t]=n,this.options.onnew?this.$nav.find("#new-tab").before(n.$title):this.$nav.append(n.$title),n.$content.append(e.$el),this.$content.append(n.$content),_.size(this.list)==1&&(n.$title.addClass("active"),n.$content.addClass("active"),this.first_tab=t);if(e.ondel){var r=this,i=n.$title.find("#delete");i.tooltip({title:"Delete this tab",placement:"bottom",container:r.$el}),i.on("click",function(){return i.tooltip("destroy"),r.$el.find(".tooltip").remove(),e.ondel(),!1})}e.onclick&&n.$title.on("click",function(){e.onclick()})},del:function(e){var t=this.list[e];t.$title.remove(),t.$content.remove(),delete t,this.first_tab==e&&(this.first_tab=null),this.first_tab!=null&&this.show(this.first_tab)},delRemovable:function(){for(var e in this.list){var t=this.list[e];t.removable&&this.del(e)}},show:function(e){this.$el.fadeIn("fast"),this.visible=!0,e&&this.list[e].$title.find("a").tab("show")},hide:function(){this.$el.fadeOut("fast"),this.visible=!1},hideOperation:function(e){this.$nav.find("#"+e).hide()},showOperation:function(e){this.$nav.find("#"+e).show()},setOperation:function(e,t){var n=this.$nav.find("#"+e);n.off("click"),n.on("click",t)},title:function(e,t){var n=this.list[e].$title.find("#text");return t&&n.html(t),n.html()},_template:function(e){return'<div class="tabbable tabs-left"><ul class="tab-navigation nav nav-tabs"><div class="operations" style="float: right; margin-bottom: 4px;"></div></ul><div class="tab-content"/></div>'},_template_tab_new:function(e){return'<li id="new-tab"><a href="javascript:void(0);"><i style="font-size: 0.8em; margin-right: 5px;" class="fa fa-plus-circle"/>'+e.title_new+"</a>"+"</li>"},_template_tab:function(e){var t='<li id="title-'+e.id+'">'+'<a title="" href="#tab-'+e.id+'" data-toggle="tab" data-original-title="">'+'<span id="text">'+e.title+"</span>";return e.ondel&&(t+='<i id="delete" style="font-size: 0.8em; margin-left: 5px; cursor: pointer;" class="fa fa-minus-circle"/>'),t+="</a></li>",t},_template_tab_content:function(e){return'<div id="tab-'+e.id+'" class="tab-pane"/>'}});return{View:t}}),define("plugin/library/ui-table",["utils/utils"],function(e){var t=Backbone.View.extend({row:null,row_count:0,optionsDefault:{content:"No content available.",onchange:null,ondblclick:null,onconfirm:null,cls:"grid"},events:{click:"_onclick",dblclick:"_ondblclick"},initialize:function(t){this.options=e.merge(t,this.optionsDefault);var n=$(this._template(t));this.$thead=n.find("thead"),this.$tbody=n.find("tbody"),this.$tmessage=n.find("tmessage"),this.setElement(n),this.row=$("<tr></tr>")},addHeader:function(e){var t=$("<th></th>");t.append(e),this.row.append(t)},appendHeader:function(){this.$thead.append(this.row),this.row=$("<tr></tr>")},add:function(e,t,n){var r=$("<td></td>");t&&r.css("width",t),n&&r.css("text-align",n),r.append(e),this.row.append(r)},append:function(e){this._commit(e)},prepend:function(e){this._commit(e,!0)},get:function(e){return this.$el.find("#"+e)},del:function(e){var t=this.$tbody.find("#"+e);t.length>0&&(t.remove(),this.row_count--,this._refresh())},delAll:function(){this.$tbody.html(""),this.row_count=0,this._refresh()},value:function(e){this.before=this.$tbody.find(".current").attr("id"),e!==undefined&&(this.$tbody.find("tr").removeClass("current"),e&&this.$tbody.find("#"+e).addClass("current"));var t=this.$tbody.find(".current").attr("id");return t===undefined?null:(t!=this.before&&this.options.onchange&&this.options.onchange(e),t)},size:function(){return this.$tbody.find("tr").length},_commit:function(e,t){this.del(e),this.row.attr("id",e),t?this.$tbody.prepend(this.row):this.$tbody.append(this.row),this.row=$("<tr></tr>"),this.row_count++,this._refresh()},_onclick:function(e){var t=this.value(),n=$(e.target).closest("tr").attr("id");n!=""&&n&&t!=n&&(this.options.onconfirm?this.options.onconfirm(n):this.value(n))},_ondblclick:function(e){var t=this.value();t&&this.options.ondblclick&&this.options.ondblclick(t)},_refresh:function(){this.row_count==0?this.$tmessage.show():this.$tmessage.hide()},_template:function(e){return'<div><table class="'+e.cls+'">'+"<thead></thead>"+'<tbody style="cursor: pointer;"></tbody>'+"</table>"+"<tmessage>"+e.content+"</tmessage>"+"<div>"}});return{View:t}}),define("plugin/views/group",["plugin/library/ui-table","plugin/library/ui","utils/utils"],function(e,t,n){return Backbone.View.extend({initialize:function(r,i){this.app=r;var s=this;this.chart=this.app.chart,this.group=i.group,this.group_key=new t.Input({placeholder:"Data label",onchange:function(e){s.group.set("key",e)}}),this.table=new e.View({content:"No data column."});var o=$("<div/>");o.append(n.wrap((new t.Label({title:"Provide a label:"})).$el)),o.append(n.wrap(this.group_key.$el)),o.append(n.wrap((new t.Label({title:"Select columns:"})).$el)),o.append(n.wrap(this.table.$el)),this.setElement(o);var s=this;this.chart.on("change:dataset_id",function(){s._refreshTable()}),this.chart.on("change:type",function(){s._refreshTable()}),this.group.on("change:key",function(){s._refreshGroupKey()}),this.group.on("change",function(){s._refreshGroup()}),this._refreshTable(),this._refreshGroupKey(),this._refreshGroup()},_refreshTable:function(){var e=this.chart.get("dataset_id"),n=this.chart.get("type");if(!e||!n)return;var r=this,i=this.app.types.get(n);this.table.delAll();var s={};for(var o in i.columns){var u=i.columns[o];if(!u){console.debug("Group::_refreshTable() - Skipping column definition.");continue}var a=new t.Select.View({id:"select_"+o,wait:!0});this.table.add(u.title,"25%"),this.table.add(a.$el),this.table.append(o),s[o]=a}this.chart.state("wait","Loading metadata...");var f=this.chart.deferred.register();this.app.datasets.request({id:e},function(e){for(var t in s)r._addRow(t,e,s[t],i.columns[t]);r.chart.state("ok","Metadata initialized..."),r.chart.deferred.done(f)})},_addRow:function(e,t,n,r){var i=r.is_label,s=r.is_auto,o=[];s&&o.push({label:"Column: Row Number",value:"auto"});var u=t.metadata_column_types;for(var a in u)(!i&&(u[a]=="int"||u[a]=="float")||i)&&o.push({label:"Column: "+(parseInt(a)+1)+" ["+u[a]+"]",value:a});n.update(o),n.show();if(!n.exists(this.group.get(e))){var f=n.first();console.debug('Group()::_addRow() - Switching model value from "'+this.group.get(e)+'" to "'+f+'".'),this.group.set(e,f)}n.value(this.group.get(e));var l=this;n.setOnChange(function(t){l.group.set(e,t),l.chart.set("modified",!0)})},_refreshGroup:function(){this.group.set("date",n.time())},_refreshGroupKey:function(){var e=this.group.get("key");e===undefined&&(e=""),this.group_key.value(e)}})}),define("plugin/library/ui-table-form",["plugin/library/ui-table","plugin/library/ui","utils/utils"],function(e,t,n){var r=Backbone.View.extend({optionsDefault:{title:"",content:"",mode:""},list:[],initialize:function(r){this.table_title=new t.Label({title:r.title}),this.table=new e.View({content:r.content});var i=$("<div/>");i.append(n.wrap(this.table_title.$el)),i.append(n.wrap(this.table.$el)),this.setElement(i)},title:function(e){this.table_title.title(e)},update:function(e,t){this.table.delAll(),this.list=[];for(var n in e)this._add(n,e[n],t);for(var n in this.list)this.list[n].trigger("change")},_add:function(e,n,r){var i=this,s=null,o=n.type;switch(o){case"text":s=new t.Input({id:"field_"+e,placeholder:n.placeholder,value:r.get(e),onchange:function(t){r.set(e,t)}});break;case"select":s=new t.Select.View({id:"field_"+e,data:n.data,value:r.get(e),onchange:function(t){r.set(e,t);var s=_.findWhere(n.data,{value:t});if(s){for(var o in s.show){var u=s.show[o];i.table.get(u).show()}for(var o in s.hide){var u=s.hide[o];i.table.get(u).hide()}}}});break;case"separator":s=$("<div/>");break;default:console.log("ui-table-form:_add","Unknown setting type ("+n.type+")");return}if(o!="separator"){r.get(e)||r.set(e,n.init),s.value(r.get(e)),this.list[e]=s;var u=$("<div/>");u.append(s.$el),u.append('<div class="toolParamHelp" style="font-size: 0.9em;">'+n.info+"</div>"),this.table.add('<span style="white-space: nowrap;">'+n.title+"</span>","25%"),this.table.add(u)}else this.table.add('<h6 style="white-space: nowrap;">'+n.title+":<h6/>"),this.table.add($("<div/>"));this.table.append(e)}});return{View:r}}),define("plugin/views/settings",["plugin/library/ui","plugin/library/ui-table-form","utils/utils"],function(e,t,n){return Backbone.View.extend({initialize:function(e,n){this.app=e;var r=this;this.chart=this.app.chart,this.form=new t.View({title:"Chart options:",content:"This chart type does not provide any options."}),this.setElement(this.form.$el);var r=this;this.chart.on("change",function(){r._refreshTable()})},_refreshTable:function(){var e=this.chart.get("type");if(!e)return;var t=this.app.types.get(e);this.form.title(t.category+" - "+t.title+":"),this.form.update(t.settings,this.chart.settings)}})}),define("plugin/views/types",["utils/utils","plugin/library/ui"],function(e,t){return Backbone.View.extend({optionsDefault:{onchange:null,ondblclick:null},events:{click:"_onclick",dblclick:"_ondblclick"},initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault);var r=$('<div class="charts-grid"/>');this.setElement(r);var i={},s=t.types.attributes;for(var o in s){var u=s[o],a=u.category;i[a]||(i[a]={}),i[a][o]=u}for(var a in i){var r=$('<div style="clear: both;"/>');r.append(e.wrap(this._template_header({title:a})));for(var o in i[a]){var u=i[a][o];r.append(e.wrap(this._template_item({id:o,title:u.title,url:config.app_root+"charts/"+o+"/logo.png"})))}this.$el.append(e.wrap(r))}},value:function(e){var t=this.$el.find(".current").attr("id");e!==undefined&&(this.$el.find(".current").removeClass("current"),this.$el.find("#"+e).addClass("current"));var n=this.$el.find(".current").attr("id");return n===undefined?null:(n!=t&&this.options.onchange&&this.options.onchange(e),n)},_onclick:function(e){var t=this.value(),n=$(e.target).closest(".item").attr("id");n!=""&&n&&t!=n&&this.value(n)},_ondblclick:function(e){var t=this.value();t&&this.options.ondblclick&&this.options.ondblclick(t)},_template_header:function(e){return'<div class="header">• '+e.title+"<div>"},_template_item:function(e){return'<div id="'+e.id+'" class="item">'+'<img class="image" src="'+e.url+'">'+'<div class="title">'+e.title+"</div>"+"<div>"}})}),define("plugin/views/editor",["mvc/ui/ui-tabs","plugin/library/ui","mvc/ui/ui-portlet","utils/utils","plugin/models/chart","plugin/models/group","plugin/views/group","plugin/views/settings","plugin/views/types"],function(e,t,n,r,i,s,o,u,a){return Backbone.View.extend({optionsDefault:{header:!0,content:"No content available."},initialize:function(i,s){var o=this;this.app=i,this.chart=this.app.chart,this.options=r.merge(s,this.optionsDefault),this.portlet=new n.View({icon:"fa-bar-chart-o",title:"Editor",operations:{save:new t.ButtonIcon({icon:"fa-save",tooltip:"Draw Chart",title:"Draw",onclick:function(){o.app.go("viewer"),o._saveChart()}}),back:new t.ButtonIcon({icon:"fa-caret-left",tooltip:"Return to Viewer",title:"Return",onclick:function(){o.app.go("viewer"),o.app.storage.load()}})}}),this.types=new a(i,{onchange:function(e){o.chart.settings.clear(),o.chart.set({type:e}),o.chart.set("modified",!0)},ondblclick:function(e){o.app.go("viewer"),o._saveChart()}}),this.tabs=new e.View({title_new:"Add Data",onnew:function(){var e=o._addGroupModel();o.tabs.show(e.id)}}),this.title=new t.Input({placeholder:"Chart title",onchange:function(){o.chart.set("title",o.title.value())}});var f=$("<div/>");f.append(r.wrap((new t.Label({title:"Provide a chart title:"})).$el)),f.append(r.wrap(this.title.$el)),f.append(r.wrap((new t.Label({title:"Select a chart type:"})).$el)),f.append(r.wrap(this.types.$el)),this.tabs.add({id:"main",title:"Start",$el:f}),this.settings=new u(this.app),this.tabs.add({id:"settings",title:"Configuration",$el:this.settings.$el}),this.portlet.append(this.tabs.$el),this.setElement(this.portlet.$el),this.tabs.hideOperation("back");var o=this;this.chart.on("change:title",function(e){o._refreshTitle()}),this.chart.on("change:type",function(e){o.types.value(e.get("type"))}),this.chart.on("reset",function(e){o._resetChart()}),this.app.chart.on("redraw",function(e){o.portlet.showOperation("back")}),this.app.chart.groups.on("add",function(e){o._addGroup(e)}),this.app.chart.groups.on("remove",function(e){o._removeGroup(e)}),this.app.chart.groups.on("reset",function(e){o._removeAllGroups()}),this.app.chart.groups.on("change:key",function(e){o._refreshGroupKey()}),this._resetChart()},show:function(){this.$el.show()},hide:function(){this.$el.hide()},_refreshTitle:function(){var e=this.chart.get("title");this.portlet.title(e),this.title.value(e)},_refreshGroupKey:function(){var e=this,t=0;this.chart.groups.each(function(n){var r=n.get("key","");r==""&&(r="Chart data"),e.tabs.title(n.id,++t+": "+r)})},_addGroupModel:function(){var e=new s({id:r.uuid()});return this.chart.groups.add(e),e},_addGroup:function(e){var t=this,n=new o(this.app,{group:e}),r=t.chart.groups.length;this.tabs.add({id:e.id,$el:n.$el,ondel:function(){t.chart.groups.remove(e.id)}}),this._refreshGroupKey()},_removeGroup:function(e){this.tabs.del(e.id),this._refreshGroupKey(),this.chart.set("modified",!0)},_removeAllGroups:function(e){this.tabs.delRemovable()},_resetChart:function(){this.chart.set("id",r.uuid()),this.chart.set("type","nvd3_bar"),this.chart.set("dataset_id",this.app.options.config.dataset_id),this.chart.set("title","New Chart"),this.portlet.hideOperation("back")},_saveChart:function(){this.chart.set({type:this.types.value(),title:this.title.value(),date:r.time()}),this.chart.groups.length==0&&this._addGroupModel();var e=this;this.chart.deferred.execute(function(){e.app.storage.save(),e.chart.trigger("redraw")})}})}),define("plugin/models/config",[],function(){return Backbone.Model.extend({defaults:{query_limit:5e3,query_timeout:500,screenshot_url:"http://export.highcharts.com/"}})}),define("plugin/charts/nvd3/config",[],function(){return{title:"",category:"",library:"nvd3.js",tag:"svg",settings:{separator_label:{title:"X axis",type:"separator"},x_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"X-axis",placeholder:"Axis label"},x_axis_type:{title:"Axis value type",info:"Select the value type of the axis.",type:"select",init:"auto",data:[{label:"-- Do not show values --",value:"hide",hide:["x_axis_tick"]},{label:"Auto",value:"auto",hide:["x_axis_tick"]},{label:"Float",value:"f",show:["x_axis_tick"]},{label:"Exponent",value:"e",show:["x_axis_tick"]},{label:"Integer",value:"d",hide:["x_axis_tick"]},{label:"Percentage",value:"p",show:["x_axis_tick"]},{label:"Rounded",value:"r",show:["x_axis_tick"]},{label:"SI-prefix",value:"s",show:["x_axis_tick"]}]},x_axis_tick:{title:"Axis tick format",info:"Select the tick format for the axis.",type:"select",init:".1",data:[{label:"0.00001",value:".5"},{label:"0.0001",value:".4"},{label:"0.001",value:".3"},{label:"0.01",value:".2"},{label:"0.1",value:".1"},{label:"1",value:"1"}]},separator_tick:{title:"Y axis",type:"separator"},y_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"Y-axis",placeholder:"Axis label"},y_axis_type:{title:"Axis value type",info:"Select the value type of the axis.",type:"select",init:"auto",data:[{label:"-- Do not show values --",value:"hide",hide:["y_axis_tick"]},{label:"Auto",value:"auto",hide:["y_axis_tick"]},{label:"Float",value:"f",show:["y_axis_tick"]},{label:"Exponent",value:"e",show:["y_axis_tick"]},{label:"Integer",value:"d",hide:["y_axis_tick"]},{label:"Percentage",value:"p",show:["y_axis_tick"]},{label:"Rounded",value:"r",show:["y_axis_tick"]},{label:"SI-prefix",value:"s",show:["y_axis_tick"]}]},y_axis_tick:{title:"Axis tick format",info:"Select the tick format for the axis.",type:"select",init:".1",data:[{label:"0.00001",value:".5"},{label:"0.0001",value:".4"},{label:"0.001",value:".3"},{label:"0.01",value:".2"},{label:"0.1",value:".1"},{label:"1",value:"1"}]},separator_legend:{title:"Others",type:"separator"},show_legend:{title:"Show legend",info:"Would you like to add a legend?",type:"select",init:"true",data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]}}}}),define("plugin/charts/nvd3_bar/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Regular",category:"Bar diagrams",columns:{x:{title:"Values for x-axis",is_label:!0,is_auto:!0},y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_bar_stacked/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Stacked",category:"Bar diagrams",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_bar_horizontal/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Horizontal",category:"Bar diagrams",columns:{x:{title:"Values for x-axis",is_label:!0,is_auto:!0},y:{title:"Values for y-axis"}},settings:{x_axis_type:{init:"hide"}}})}),define("plugin/charts/nvd3_bar_horizontal_stacked/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Stacked horizontal",category:"Bar diagrams",settings:{x_axis_type:{init:"hide"}},columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_histogram/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Histogram",category:"Data processing (requires 'charts' tool from Toolshed)",execute:"histogram",columns:{y:{title:"Observations"}},settings:{x_axis_label:{init:"Breaks"},y_axis_label:{init:"Density"},y_axis_type:{init:"f"},y_axis_tick:{init:".2"}}})}),define("plugin/charts/nvd3_histogram_discrete/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Histogram (discrete)",category:"Data processing (requires 'charts' tool from Toolshed)",execute:"histogramdiscrete",columns:{x:{title:"Observations",is_label:!0}},settings:{x_axis_label:{init:"Breaks"},y_axis_label:{init:"Density"},y_axis_type:{init:"f"},y_axis_tick:{init:".2"}}})}),define("plugin/charts/nvd3_line/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line chart",category:"Others",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_line_focus/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line with focus",category:"Others",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_pie/config",[],function(){return{title:"Pie chart",library:"nvd3.js",category:"Area charts",tag:"svg",use_panels:!0,columns:{label:{title:"Labels",is_label:!0,is_auto:!0},y:{title:"Values"}},settings:{show_legend:{title:"Show legend",info:"Would you like to add a legend?",type:"select",init:"false",data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]},donut_ratio:{title:"Donut ratio",info:"Determine how large the donut hole will be.",type:"select",init:"0.5",data:[{label:"50%",value:"0.5"},{label:"25%",value:"0.25"},{label:"10%",value:"0.10"},{label:"0%",value:"0"}]},label_separator:{type:"separator",title:"Label settings"},label_type:{title:"Donut label",info:"What would you like to show for each slice?",type:"select",init:"percent",data:[{label:"-- Nothing --",value:"hide",hide:"label_outside"},{label:"Label column",value:"key",show:"label_outside"},{label:"Value column",value:"value",show:"label_outside"},{label:"Percentage",value:"percent",show:"label_outside"}]},label_outside:{title:"Show outside",info:"Would you like to show labels outside the donut?",type:"select",init:"false",data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]}}}}),define("plugin/charts/nvd3_scatter/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Scatter plot",category:"Others",columns:{x:{title:"Values for x-axis"},y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_stackedarea/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Regular",category:"Area charts",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_stackedarea_full/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Expanded",category:"Area charts",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_stackedarea_stream/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Stream",category:"Area charts",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/highcharts_boxplot/config",[],function(){return{title:"Box plot",category:"Data processing (requires 'charts' tool from Toolshed)",library:"highcharts.js",tag:"div",execute:"boxplot",columns:{y:{title:"Observations"}},settings:{separator_label:{title:"X axis",type:"separator"},x_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"X-axis",placeholder:"Axis label"},separator_tick:{title:"Y axis",type:"separator"},y_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"Y-axis",placeholder:"Axis label"}}}}),define("plugin/charts/heatmap/config",[],function(){return{title:"Heatmap",category:"Data processing (requires 'charts' tool from Toolshed)",library:"",tag:"div",use_panels:!0,execute:"heatmap",columns:{row_label:{title:"Row labels",is_label:!0},col_label:{title:"Column labels",is_label:!0},value:{title:"Observations"}},settings:{color_set:{title:"Color scheme",info:"Select a color scheme for your heatmap",type:"select",init:"ocean",data:[{label:"Cold-to-Hot",value:"hot"},{label:"Cool",value:"cool"},{label:"Copper",value:"copper"},{label:"Gebco",value:"gebco"},{label:"Globe",value:"globe"},{label:"Gray scale",value:"gray"},{label:"Haxby",value:"haxby"},{label:"Jet",value:"jet"},{label:"No-Green",value:"no_green"},{label:"Ocean",value:"ocean"},{label:"Polar",value:"polar"},{label:"Rainbow",value:"rainbow"},{label:"Red-to-Green",value:"redgreen"},{label:"Red-to-green (saturated)",value:"red2green"},{label:"Relief",value:"relief"},{label:"Seismograph",value:"seis"},{label:"Sealand",value:"sealand"},{label:"Split",value:"split"},{label:"Topo",value:"topo"},{label:"Wysiwyg",value:"wysiwyg"}]},sorting:{title:"Sorting",info:"How should the columns be clustered?",type:"select",init:"hclust",data:[{label:"Read from dataset",value:"hclust"},{label:"Sort column and row labels",value:"byboth"},{label:"Sort column labels",value:"bycolumns"},{label:"Sort by rows",value:"byrow"}]}},menu:function(){return{color_set:this.settings.color_set}}}}),define("plugin/charts/types",["plugin/charts/nvd3_bar/config","plugin/charts/nvd3_bar_stacked/config","plugin/charts/nvd3_bar_horizontal/config","plugin/charts/nvd3_bar_horizontal_stacked/config","plugin/charts/nvd3_histogram/config","plugin/charts/nvd3_histogram_discrete/config","plugin/charts/nvd3_line/config","plugin/charts/nvd3_line_focus/config","plugin/charts/nvd3_pie/config","plugin/charts/nvd3_scatter/config","plugin/charts/nvd3_stackedarea/config","plugin/charts/nvd3_stackedarea_full/config","plugin/charts/nvd3_stackedarea_stream/config","plugin/charts/highcharts_boxplot/config","plugin/charts/heatmap/config"],function(e,t,n,r,i,s,o,u,a,f,l,c,h,p,d){return Backbone.Model.extend({defaults:{nvd3_bar:e,nvd3_bar_stacked:t,nvd3_bar_horizontal:n,nvd3_bar_horizontal_stacked:r,nvd3_stackedarea:l,nvd3_stackedarea_full:c,nvd3_stackedarea_stream:h,nvd3_line:o,nvd3_line_focus:u,nvd3_scatter:f,nvd3_pie:a,nvd3_histogram:i,nvd3_histogram_discrete:s,highcharts_boxplot:p,heatmap:d}})}),define("plugin/app",["mvc/ui/ui-modal","mvc/ui/ui-portlet","plugin/library/ui","utils/utils","plugin/library/jobs","plugin/library/datasets","plugin/library/storage","plugin/views/viewer","plugin/views/editor","plugin/models/config","plugin/models/chart","plugin/charts/types"],function(e,t,n,r,i,s,o,u,a,f,l,c){return Backbone.View.extend({initialize:function(t){this.options=t,Galaxy&&Galaxy.modal?this.modal=Galaxy.modal:this.modal=new e.View,this.config=new f,this.types=new c,this.chart=new l,this.jobs=new i(this),this.datasets=new s(this),this.storage=new o(this),this.viewer_view=new u(this),this.editor_view=new a(this),this.$el.append(this.viewer_view.$el),this.$el.append(this.editor_view.$el);if(!this.storage.load())this.go("editor");else{this.go("viewer");var n=this;this.chart.deferred.execute(function(){n.chart.trigger("redraw")})}},go:function(e){$(".tooltip").hide();switch(e){case"editor":this.editor_view.show(),this.viewer_view.hide();break;case"viewer":this.editor_view.hide(),this.viewer_view.show()}},execute:function(e){},onunload:function(){},log:function(e,t){console.log(e+" "+t)}})});
\ No newline at end of file
+define("mvc/ui/ui-modal",[],function(){var e=Backbone.View.extend({elMain:"body",optionsDefault:{title:"ui-modal",body:"",backdrop:!0,height:null,width:null,closing_events:!1,closing_callback:null},buttonList:{},initialize:function(e){e&&this._create(e)},show:function(e){this.initialize(e),this.options.height?(this.$body.css("height",this.options.height),this.$body.css("overflow","hidden")):this.$body.css("max-height",$(window).height()/2),this.options.width&&this.$dialog.css("width",this.options.width),this.visible?this.$el.show():this.$el.fadeIn("fast"),this.visible=!0},hide:function(){this.visible=!1,this.$el.fadeOut("fast"),this.options.closing_callback&&this.options.closing_callback()},enableButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).prop("disabled",!1)},disableButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).prop("disabled",!0)},showButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).show()},hideButton:function(e){var t=this.buttonList[e];this.$buttons.find("#"+t).hide()},getButton:function(e){var t=this.buttonList[e];return this.$buttons.find("#"+t)},scrollTop:function(){return this.$body.scrollTop()},_create:function(e){var t=this;this.options=_.defaults(e,this.optionsDefault),this.options.body=="progress"&&(this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')),this.$el&&(this.$el.remove(),$(document).off("keyup.ui-modal")),this.setElement(this._template(this.options.title)),this.$dialog=this.$el.find(".modal-dialog"),this.$body=this.$el.find(".modal-body"),this.$footer=this.$el.find(".modal-footer"),this.$buttons=this.$el.find(".buttons"),this.$backdrop=this.$el.find(".modal-backdrop"),this.$body.html(this.options.body),this.options.backdrop||this.$backdrop.removeClass("in");if(this.options.buttons){this.buttonList={};var n=0;$.each(this.options.buttons,function(e,r){var i="button-"+n++;t.$buttons.append($('<button id="'+i+'"></button>').text(e).click(r)).append(" "),t.buttonList[e]=i})}else this.$footer.hide();$(this.elMain).append($(this.el)),this.options.closing_events&&($(document).on("keyup.ui-modal",function(e){e.keyCode==27&&t.hide()}),this.$el.find(".modal-backdrop").on("click",function(){t.hide()}))},_template:function(e){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+e+"</h4>"+"</div>"+'<div class="modal-body" style="position: static;"></div>'+'<div class="modal-footer">'+'<div class="buttons" style="float: right;"></div>'+"</div>"+"</div"+"</div>"+"</div>"}});return{View:e}}),function(){var e=this,t=e._,n={},r=Array.prototype,i=Object.prototype,s=Function.prototype,o=r.push,u=r.slice,a=r.concat,f=i.toString,l=i.hasOwnProperty,c=r.forEach,h=r.map,p=r.reduce,d=r.reduceRight,v=r.filter,m=r.every,g=r.some,y=r.indexOf,b=r.lastIndexOf,w=Array.isArray,E=Object.keys,S=s.bind,x=function(e){if(e instanceof x)return e;if(!(this instanceof x))return new x(e);this._wrapped=e};typeof exports!="undefined"?(typeof module!="undefined"&&module.exports&&(exports=module.exports=x),exports._=x):e._=x,x.VERSION="1.4.4";var T=x.each=x.forEach=function(e,t,r){if(e==null)return;if(c&&e.forEach===c)e.forEach(t,r);else if(e.length===+e.length){for(var i=0,s=e.length;i<s;i++)if(t.call(r,e[i],i,e)===n)return}else for(var o in e)if(x.has(e,o)&&t.call(r,e[o],o,e)===n)return};x.map=x.collect=function(e,t,n){var r=[];return e==null?r:h&&e.map===h?e.map(t,n):(T(e,function(e,i,s){r.push(t.call(n,e,i,s))}),r)};var N="Reduce of empty array with no initial value";x.reduce=x.foldl=x.inject=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(p&&e.reduce===p)return r&&(t=x.bind(t,r)),i?e.reduce(t,n):e.reduce(t);T(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError(N);return n},x.reduceRight=x.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(d&&e.reduceRight===d)return r&&(t=x.bind(t,r)),i?e.reduceRight(t,n):e.reduceRight(t);var s=e.length;if(s!==+s){var o=x.keys(e);s=o.length}T(e,function(u,a,f){a=o?o[--s]:--s,i?n=t.call(r,n,e[a],a,f):(n=e[a],i=!0)});if(!i)throw new TypeError(N);return n},x.find=x.detect=function(e,t,n){var r;return C(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},x.filter=x.select=function(e,t,n){var r=[];return e==null?r:v&&e.filter===v?e.filter(t,n):(T(e,function(e,i,s){t.call(n,e,i,s)&&r.push(e)}),r)},x.reject=function(e,t,n){return x.filter(e,function(e,r,i){return!t.call(n,e,r,i)},n)},x.every=x.all=function(e,t,r){t||(t=x.identity);var i=!0;return e==null?i:m&&e.every===m?e.every(t,r):(T(e,function(e,s,o){if(!(i=i&&t.call(r,e,s,o)))return n}),!!i)};var C=x.some=x.any=function(e,t,r){t||(t=x.identity);var i=!1;return e==null?i:g&&e.some===g?e.some(t,r):(T(e,function(e,s,o){if(i||(i=t.call(r,e,s,o)))return n}),!!i)};x.contains=x.include=function(e,t){return e==null?!1:y&&e.indexOf===y?e.indexOf(t)!=-1:C(e,function(e){return e===t})},x.invoke=function(e,t){var n=u.call(arguments,2),r=x.isFunction(t);return x.map(e,function(e){return(r?t:e[t]).apply(e,n)})},x.pluck=function(e,t){return x.map(e,function(e){return e[t]})},x.where=function(e,t,n){return x.isEmpty(t)?n?void 0:[]:x[n?"find":"filter"](e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},x.findWhere=function(e,t){return x.where(e,t,!0)},x.max=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.max.apply(Math,e);if(!t&&x.isEmpty(e))return-Infinity;var r={computed:-Infinity,value:-Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},x.min=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.min.apply(Math,e);if(!t&&x.isEmpty(e))return Infinity;var r={computed:Infinity,value:Infinity};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o<r.computed&&(r={value:e,computed:o})}),r.value},x.shuffle=function(e){var t,n=0,r=[];return T(e,function(e){t=x.random(n++),r[n-1]=r[t],r[t]=e}),r};var k=function(e){return x.isFunction(e)?e:function(t){return t[e]}};x.sortBy=function(e,t,n){var r=k(t);return x.pluck(x.map(e,function(e,t,i){return{value:e,index:t,criteria:r.call(n,e,t,i)}}).sort(function(e,t){var n=e.criteria,r=t.criteria;if(n!==r){if(n>r||n===void 0)return 1;if(n<r||r===void 0)return-1}return e.index<t.index?-1:1}),"value")};var L=function(e,t,n,r){var i={},s=k(t==null?x.identity:t);return T(e,function(t,o){var u=s.call(n,t,o,e);r(i,u,t)}),i};x.groupBy=function(e,t,n){return L(e,t,n,function(e,t,n){(x.has(e,t)?e[t]:e[t]=[]).push(n)})},x.countBy=function(e,t,n){return L(e,t,n,function(e,t){x.has(e,t)||(e[t]=0),e[t]++})},x.sortedIndex=function(e,t,n,r){n=n==null?x.identity:k(n);var i=n.call(r,t),s=0,o=e.length;while(s<o){var u=s+o>>>1;n.call(r,e[u])<i?s=u+1:o=u}return s},x.toArray=function(e){return e?x.isArray(e)?u.call(e):e.length===+e.length?x.map(e,x.identity):x.values(e):[]},x.size=function(e){return e==null?0:e.length===+e.length?e.length:x.keys(e).length},x.first=x.head=x.take=function(e,t,n){return e==null?void 0:t!=null&&!n?u.call(e,0,t):e[0]},x.initial=function(e,t,n){return u.call(e,0,e.length-(t==null||n?1:t))},x.last=function(e,t,n){return e==null?void 0:t!=null&&!n?u.call(e,Math.max(e.length-t,0)):e[e.length-1]},x.rest=x.tail=x.drop=function(e,t,n){return u.call(e,t==null||n?1:t)},x.compact=function(e){return x.filter(e,x.identity)};var A=function(e,t,n){return T(e,function(e){x.isArray(e)?t?o.apply(n,e):A(e,t,n):n.push(e)}),n};x.flatten=function(e,t){return A(e,t,[])},x.without=function(e){return x.difference(e,u.call(arguments,1))},x.uniq=x.unique=function(e,t,n,r){x.isFunction(t)&&(r=n,n=t,t=!1);var i=n?x.map(e,n,r):e,s=[],o=[];return T(i,function(n,r){if(t?!r||o[o.length-1]!==n:!x.contains(o,n))o.push(n),s.push(e[r])}),s},x.union=function(){return x.uniq(a.apply(r,arguments))},x.intersection=function(e){var t=u.call(arguments,1);return x.filter(x.uniq(e),function(e){return x.every(t,function(t){return x.indexOf(t,e)>=0})})},x.difference=function(e){var t=a.apply(r,u.call(arguments,1));return x.filter(e,function(e){return!x.contains(t,e)})},x.zip=function(){var e=u.call(arguments),t=x.max(x.pluck(e,"length")),n=new Array(t);for(var r=0;r<t;r++)n[r]=x.pluck(e,""+r);return n},x.unzip=function(e){var t=[];return x.each(e,function(e,n){x.each(e,function(e,r){t.length<=r&&(t[r]=[]),t[r][n]=e})}),t},x.object=function(e,t){if(e==null)return{};var n={};for(var r=0,i=e.length;r<i;r++)t?n[e[r]]=t[r]:n[e[r][0]]=e[r][1];return n},x.indexOf=function(e,t,n){if(e==null)return-1;var r=0,i=e.length;if(n){if(typeof n!="number")return r=x.sortedIndex(e,t),e[r]===t?r:-1;r=n<0?Math.max(0,i+n):n}if(y&&e.indexOf===y)return e.indexOf(t,n);for(;r<i;r++)if(e[r]===t)return r;return-1},x.lastIndexOf=function(e,t,n){if(e==null)return-1;var r=n!=null;if(b&&e.lastIndexOf===b)return r?e.lastIndexOf(t,n):e.lastIndexOf(t);var i=r?n:e.length;while(i--)if(e[i]===t)return i;return-1},x.range=function(e,t,n){arguments.length<=1&&(t=e||0,e=0),n=arguments[2]||1;var r=Math.max(Math.ceil((t-e)/n),0),i=0,s=new Array(r);while(i<r)s[i++]=e,e+=n;return s};var O=function(){};x.bind=function(e,t){var n,r;if(e.bind===S&&S)return S.apply(e,u.call(arguments,1));if(!x.isFunction(e))throw new TypeError;return n=u.call(arguments,2),r=function(){if(this instanceof r){O.prototype=e.prototype;var i=new O;O.prototype=null;var s=e.apply(i,n.concat(u.call(arguments)));return Object(s)===s?s:i}return e.apply(t,n.concat(u.call(arguments)))}},x.partial=function(e){var t=u.call(arguments,1);return function(){return e.apply(this,t.concat(u.call(arguments)))}},x.bindAll=function(e){var t=u.call(arguments,1);if(t.length===0)throw new Error("bindAll must be passed function names");return T(t,function(t){e[t]=x.bind(e[t],e)}),e},x.memoize=function(e,t){var n={};return t||(t=x.identity),function(){var r=t.apply(this,arguments);return x.has(n,r)?n[r]:n[r]=e.apply(this,arguments)}},x.delay=function(e,t){var n=u.call(arguments,2);return setTimeout(function(){return e.apply(null,n)},t)},x.defer=function(e){return x.delay.apply(x,[e,1].concat(u.call(arguments,1)))},x.throttle=function(e,t,n){var r,i,s,o,u=0,a=function(){u=new Date,s=null,o=e.apply(r,i)};return function(){var f=new Date;!u&&n===!1&&(u=f);var l=t-(f-u);return r=this,i=arguments,l<=0?(clearTimeout(s),s=null,u=f,o=e.apply(r,i)):s||(s=setTimeout(a,l)),o}},x.debounce=function(e,t,n){var r,i;return function(){var s=this,o=arguments,u=function(){r=null,n||(i=e.apply(s,o))},a=n&&!r;return clearTimeout(r),r=setTimeout(u,t),a&&(i=e.apply(s,o)),i}},x.once=function(e){var t=!1,n;return function(){return t?n:(t=!0,n=e.apply(this,arguments),e=null,n)}},x.wrap=function(e,t){return function(){var n=[e];return o.apply(n,arguments),t.apply(this,n)}},x.compose=function(){var e=arguments;return function(){var t=arguments;for(var n=e.length-1;n>=0;n--)t=[e[n].apply(this,t)];return t[0]}},x.after=function(e,t){return e<=0?t():function(){if(--e<1)return t.apply(this,arguments)}},x.keys=E||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)x.has(e,n)&&t.push(n);return t},x.values=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push(e[n]);return t},x.pairs=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push([n,e[n]]);return t},x.invert=function(e){var t={};for(var n in e)x.has(e,n)&&(t[e[n]]=n);return t},x.functions=x.methods=function(e){var t=[];for(var n in e)x.isFunction(e[n])&&t.push(n);return t.sort()},x.extend=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]=t[n]}),e},x.pick=function(e){var t={},n=a.apply(r,u.call(arguments,1));return T(n,function(n){n in e&&(t[n]=e[n])}),t},x.omit=function(e){var t={},n=a.apply(r,u.call(arguments,1));for(var i in e)x.contains(n,i)||(t[i]=e[i]);return t},x.defaults=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]===void 0&&(e[n]=t[n])}),e},x.clone=function(e){return x.isObject(e)?x.isArray(e)?e.slice():x.extend({},e):e},x.tap=function(e,t){return t(e),e};var M=function(e,t,n,r){if(e===t)return e!==0||1/e==1/t;if(e==null||t==null)return e===t;e instanceof x&&(e=e._wrapped),t instanceof x&&(t=t._wrapped);var i=f.call(e);if(i!=f.call(t))return!1;switch(i){case"[object String]":return e==String(t);case"[object Number]":return e!=+e?t!=+t:e==0?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if(typeof e!="object"||typeof t!="object")return!1;var s=n.length;while(s--)if(n[s]==e)return r[s]==t;n.push(e),r.push(t);var o=0,u=!0;if(i=="[object Array]"){o=e.length,u=o==t.length;if(u)while(o--)if(!(u=M(e[o],t[o],n,r)))break}else{var a=e.constructor,l=t.constructor;if(a!==l&&!(x.isFunction(a)&&a instanceof a&&x.isFunction(l)&&l instanceof l))return!1;for(var c in e)if(x.has(e,c)){o++;if(!(u=x.has(t,c)&&M(e[c],t[c],n,r)))break}if(u){for(c in t)if(x.has(t,c)&&!(o--))break;u=!o}}return n.pop(),r.pop(),u};x.isEqual=function(e,t){return M(e,t,[],[])},x.isEmpty=function(e){if(e==null)return!0;if(x.isArray(e)||x.isString(e))return e.length===0;for(var t in e)if(x.has(e,t))return!1;return!0},x.isElement=function(e){return!!e&&e.nodeType===1},x.isArray=w||function(e){return f.call(e)=="[object Array]"},x.isObject=function(e){return e===Object(e)},T(["Arguments","Function","String","Number","Date","RegExp"],function(e){x["is"+e]=function(t){return f.call(t)=="[object "+e+"]"}}),x.isArguments(arguments)||(x.isArguments=function(e){return!!e&&!!x.has(e,"callee")}),typeof /./!="function"&&(x.isFunction=function(e){return typeof e=="function"}),x.isFinite=function(e){return isFinite(e)&&!isNaN(parseFloat(e))},x.isNaN=function(e){return x.isNumber(e)&&e!=+e},x.isBoolean=function(e){return e===!0||e===!1||f.call(e)=="[object Boolean]"},x.isNull=function(e){return e===null},x.isUndefined=function(e){return e===void 0},x.has=function(e,t){return l.call(e,t)},x.noConflict=function(){return e._=t,this},x.identity=function(e){return e},x.times=function(e,t,n){var r=Array(e);for(var i=0;i<e;i++)r[i]=t.call(n,i);return r},x.random=function(e,t){return t==null&&(t=e,e=0),e+Math.floor(Math.random()*(t-e+1))};var _={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};_.unescape=x.invert(_.escape);var D={escape:new RegExp("["+x.keys(_.escape).join("")+"]","g"),unescape:new RegExp("("+x.keys(_.unescape).join("|")+")","g")};x.each(["escape","unescape"],function(e){x[e]=function(t){return t==null?"":(""+t).replace(D[e],function(t){return _[e][t]})}}),x.result=function(e,t){if(e==null)return void 0;var n=e[t];return x.isFunction(n)?n.call(e):n},x.mixin=function(e){T(x.functions(e),function(t){var n=x[t]=e[t];x.prototype[t]=function(){var e=[this._wrapped];return o.apply(e,arguments),F.call(this,n.apply(x,e))}})};var P=0;x.uniqueId=function(e){var t=++P+"";return e?e+t:t},x.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var H=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},j=/\\|'|\r|\n|\t|\u2028|\u2029/g;x.template=function(e,t,n){var r;n=x.defaults({},n,x.templateSettings);var i=new RegExp([(n.escape||H).source,(n.interpolate||H).source,(n.evaluate||H).source].join("|")+"|$","g"),s=0,o="__p+='";e.replace(i,function(t,n,r,i,u){return o+=e.slice(s,u).replace(j,function(e){return"\\"+B[e]}),n&&(o+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),r&&(o+="'+\n((__t=("+r+"))==null?'':__t)+\n'"),i&&(o+="';\n"+i+"\n__p+='"),s=u+t.length,t}),o+="';\n",n.variable||(o="with(obj||{}){\n"+o+"}\n"),o="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{r=new Function(n.variable||"obj","_",o)}catch(u){throw u.source=o,u}if(t)return r(t,x);var a=function(e){return r.call(this,e,x)};return a.source="function("+(n.variable||"obj")+"){\n"+o+"}",a},x.chain=function(e){return x(e).chain()};var F=function(e){return this._chain?x(e).chain():e};x.mixin(x),T(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];x.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),(e=="shift"||e=="splice")&&n.length===0&&delete n[0],F.call(this,n)}}),T(["concat","join","slice"],function(e){var t=r[e];x.prototype[e]=function(){return F.call(this,t.apply(this._wrapped,arguments))}}),x.extend(x.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}.call(this),define("libs/underscore",function(e){return function(){var t,n;return t||e._}}(this)),define("utils/utils",["libs/underscore"],function(e){function t(e,t,r){n("GET",e,{},t,r)}function n(e,t,n,r,i){if(e=="GET"||e=="DELETE")t.indexOf("?")==-1?t+="?":t+="&",t+=$.param(n);var s=new XMLHttpRequest;s.open(e,t,!0),s.setRequestHeader("Accept","application/json"),s.setRequestHeader("Cache-Control","no-cache"),s.setRequestHeader("X-Requested-With","XMLHttpRequest"),s.setRequestHeader("Content-Type","application/json"),s.onloadend=function(){var e=s.status;try{response=jQuery.parseJSON(s.responseText)}catch(t){response=s.responseText}e==200?r&&r(response):i&&i(response)},e=="GET"||e=="DELETE"?s.send():s.send(JSON.stringify(n))}function r(e,t){var n=$('<div class="'+e+'"></div>');n.appendTo(":eq(0)");var r=n.css(t);return n.remove(),r}function i(e){$('link[href^="'+e+'"]').length||$('<link href="'+galaxy_config.root+e+'" rel="stylesheet">').appendTo("head")}function s(t,n){return t?e.defaults(t,n):n}function o(e,t){var n="";if(e>=1e11)e/=1e11,n="TB";else if(e>=1e8)e/=1e8,n="GB";else if(e>=1e5)e/=1e5,n="MB";else if(e>=100)e/=100,n="KB";else{if(!(e>0))return"<strong>-</strong>";e*=10,n="b"}var r=Math.round(e)/10;return t?r+" "+n:"<strong>"+r+"</strong> "+n}function u(){return(new Date).getTime().toString(36)}function a(e){var t=$("<p></p>");return t.append(e),t}function f(){var e=new Date,t=(e.getHours()<10?"0":"")+e.getHours(),n=(e.getMinutes()<10?"0":"")+e.getMinutes(),r=e.getDate()+"/"+(e.getMonth()+1)+"/"+e.getFullYear()+", "+t+":"+n;return r}return{cssLoadFile:i,cssGetAttribute:r,get:t,merge:s,bytesToString:o,uuid:u,time:f,wrap:a,request:n}}),define("mvc/ui/ui-portlet",["utils/utils"],function(e){var t=Backbone.View.extend({visible:!1,optionsDefault:{title:"",icon:"",buttons:null,body:null,height:null,operations:null,placement:"bottom",overflow:"auto"},$title:null,$content:null,$buttons:null,$operations:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.$content=this.$el.find("#content"),this.$title=this.$el.find("#title-text"),this.options.height&&(this.$el.find("#body").css("height",this.options.height),this.$el.find("#content").css("overflow",this.options.overflow)),this.$buttons=$(this.el).find("#buttons");if(this.options.buttons){var n=this;$.each(this.options.buttons,function(e,t){t.$el.prop("id",e),n.$buttons.append(t.$el)})}else this.$buttons.remove();this.$operations=$(this.el).find("#operations");if(this.options.operations){var n=this;$.each(this.options.operations,function(e,t){t.$el.prop("id",e),n.$operations.append(t.$el)})}this.options.body&&this.append(this.options.body)},append:function(t){this.$content.append(e.wrap(t))},content:function(){return this.$content},show:function(){this.$el.fadeIn("fast"),this.visible=!0},hide:function(){this.$el.fadeOut("fast"),this.visible=!1},enableButton:function(e){this.$buttons.find("#"+e).prop("disabled",!1)},disableButton:function(e){this.$buttons.find("#"+e).prop("disabled",!0)},hideOperation:function(e){this.$operations.find("#"+e).hide()},showOperation:function(e){this.$operations.find("#"+e).show()},setOperation:function(e,t){var n=this.$operations.find("#"+e);n.off("click"),n.on("click",t)},title:function(e){var t=this.$title;return e&&t.html(e),t.html()},_template:function(e){var t='<div class="toolForm portlet-view no-highlight">';if(e.title||e.icon)t+='<div id="title" class="toolFormTitle portlet-title"><div id="operations" class="portlet-operations"/><div style="overflow: hidden;">',e.icon&&(t+='<div class="portlet-title-icon fa '+e.icon+'"> </div>'),t+='<div id="title-text" class="portlet-title-text">'+e.title+"</div>",t+="</div></div>";return t+='<div id="body" class="toolFormBody portlet-body">',e.placement=="top"&&(t+='<div id="buttons" class="portlet-buttons"/>'),t+='<div id="content" class="portlet-content"/>',e.placement=="bottom"&&(t+='<div id="buttons" class="portlet-buttons"/>'),t+="</div></div>",t}});return{View:t}}),define("plugin/library/ui-select",["utils/utils"],function(e){var t=Backbone.View.extend({optionsDefault:{id:"",cls:"",empty:"No data available",visible:!0,wait:!1},selected:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.selected=this.options.value,this.setElement(this._template(this.options)),this.$select=this.$el.find("#select"),this.$icon=this.$el.find("#icon");var n=this;this.$select.on("change",function(){n.value(n.$select.val())}),this.on("change",function(){n.options.onchange&&n.options.onchange(n.value())}),this._refresh(),this.options.visible||this.hide(),this.options.wait?this.wait():this.show()},value:function(e){var t=this.selected;e!==undefined&&(this.selected=e,this.$select.val(e));var n=this.selected;return n&&n!=t&&this.options.onchange&&this.options.onchange(n),n},first:function(){var e=this.$select.find("option");return e.length>0?e.val():undefined},text:function(){return this.$select.find("option:selected").text()},show:function(){this.$icon.removeClass(),this.$icon.addClass("fa fa-caret-down"),this.$select.show(),this.$el.show()},hide:function(){this.$el.hide()},wait:function(){this.$icon.removeClass(),this.$icon.addClass("fa fa-spinner fa-spin"),this.$select.hide()},disabled:function(){return this.$select.is(":disabled")},enable:function(){this.$select.prop("disabled",!1)},disable:function(){this.$select.prop("disabled",!0)},add:function(e){this.$select.append(this._templateOption(e)),this._refresh()},del:function(e){this.$select.find("option[value="+e+"]").remove(),this.$select.trigger("change"),this._refresh()},update:function(e){this.$select.find("option").remove();for(var t in e)this.$select.append(this._templateOption(e[t]));this._refresh()},setOnChange:function(e){this.options.onchange=e},exists:function(e){return this.$select.find("option[value="+e+"]").length>0},_refresh:function(){this.$select.find("option[value=null]").remove();var e=this.$select.find("option").length;e==0?(this.disable(),this.$select.append(this._templateOption({value:"null",label:this.options.empty}))):this.enable(),this.selected&&this.$select.val(this.selected)},_templateOption:function(e){return'<option value="'+e.value+'">'+e.label+"</option>"},_template:function(e){var t='<div id="'+e.id+'" class="styled-select">'+'<div class="button">'+'<i id="icon"/>'+"</div>"+'<select id="select" class="select '+e.cls+" "+e.id+'">';for(key in e.data){var n=e.data[key],r="";if(n.value==e.value||n.value=="")r="selected";t+='<option value="'+n.value+'" '+r+">"+n.label+"</option>"}return t+="</select></div>",t}});return{View:t}}),define("plugin/library/ui",["utils/utils","plugin/library/ui-select"],function(e,t){var n=Backbone.View.extend({optionsDefault:{url:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options))},_template:function(e){return'<img src="'+e.url+'"/>'}}),r=Backbone.View.extend({optionsDefault:{title:"",cls:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options))},title:function(e){this.$el.find("b").html(e)},_template:function(e){return'<label class="'+e.cls+'"><b>'+e.title+"</b></label>"},value:function(){return options.title}}),i=Backbone.View.extend({optionsDefault:{title:"",cls:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options))},title:function(e){this.$el.html(e)},_template:function(e){return'<div class="'+e.cls+'">'+e.title+"</div>"},value:function(){return options.title}}),s=Backbone.View.extend({optionsDefault:{id:null,title:"","float":"right",cls:"btn-default",icon:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){var t='<button id="'+e.id+'" type="submit" style="margin-right: 5px; float: '+e.float+';" type="button" class="btn '+e.cls+'">';return e.icon&&(t+='<i class="icon fa '+e.icon+'"></i> '),t+=e.title+"</button>",t}}),o=Backbone.View.extend({optionsDefault:{title:"",id:null,"float":"right",cls:"icon-btn",icon:"",tooltip:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){var t="";e.title&&(t="width: auto;");var n='<div id="'+e.id+'" style="margin-right: 5px; float: '+e.float+"; "+t+'" class="'+e.cls+'">';return e.title?n+='<div style="margin-right: 5px; margin-left: 5px;"><i class="icon fa '+e.icon+'"/> '+'<span style="position: relative; font-size: 0.8em; font-weight: normal; top: -1px;">'+e.title+"</span>"+"</div>":n+='<i class="icon fa '+e.icon+'"/>',n+="</div>",n}}),u=Backbone.View.extend({optionsDefault:{"float":"right",icon:"",tooltip:"",placement:"bottom",title:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).tooltip({title:t.tooltip,placement:"bottom"})},_template:function(e){return'<div><span class="fa '+e.icon+'" style="font-size: 1.2em;"/> '+e.title+"</div>"}}),a=Backbone.View.extend({optionsDefault:{title:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),$(this.el).on("click",t.onclick)},_template:function(e){return'<div><a href="javascript:void(0)">'+e.title+"</a></div>"}}),f=Backbone.View.extend({optionsDefault:{message:"",status:"info",persistent:!1},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement("<div></div>")},update:function(t){this.options=e.merge(t,this.optionsDefault);if(t.message!=""){this.$el.html(this._template(this.options)),this.$el.fadeIn();if(!t.persistent){var n=this;window.setTimeout(function(){n.$el.is(":visible")?n.$el.fadeOut():n.$el.hide()},3e3)}}else this.$el.fadeOut()},_template:function(e){return'<div class="alert alert-'+e.status+'" style="padding: 2px 2px 2px 10px;">'+e.message+"</div>"}}),l=Backbone.View.extend({optionsDefault:{onclick:null,searchword:""},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options));var n=this;this.options.onclick&&this.$el.on("submit",function(e){var t=n.$el.find("#search");n.options.onclick(t.val())})},_template:function(e){return'<div class="search"><form onsubmit="return false;"><input id="search" class="form-control input-sm" type="text" name="search" placeholder="Search..." value="'+e.searchword+'">'+'<button type="submit" class="btn search-btn">'+'<i class="fa fa-search"></i>'+"</button>"+"</form>"+"</div>"}}),c=Backbone.View.extend({optionsDefault:{title:"Unlabeled",body:null},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.options.body&&this.$el.find(".body").append(this.options.body)},_template:function(e){return'<div id="title" class="title">'+e.title+":"+"</div>"}}),h=Backbone.View.extend({optionsDefault:{id:"",title:"",target:"",href:"",onunload:null,onclick:null,visible:!0,icon:null,tag:""},$menu:null,initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement($(this._template(this.options)));var n=$(this.el).find(".root"),r=this;n.on("click",function(e){e.preventDefault(),r.options.onclick&&r.options.onclick()}),this.options.visible||this.hide()},show:function(){$(this.el).show()},hide:function(){$(this.el).hide()},addMenu:function(t){var n={title:"",target:"",href:"",onclick:null,divider:!1,icon:null};n=e.merge(t,n),this.$menu||($(this.el).append(this._templateMenu()),this.$menu=$(this.el).find(".menu"));var r=$(this._templateMenuItem(n));r.on("click",function(e){e.preventDefault(),n.onclick&&n.onclick()}),this.$menu.append(r),n.divider&&this.$menu.append($(this._templateDivider()))},_templateMenuItem:function(e){var t='<li><a href="'+e.href+'" target="'+e.target+'">';return e.icon&&(t+='<i class="fa '+e.icon+'"></i>'),t+=" "+e.title+"</a>"+"</li>",t},_templateMenu:function(){return'<ul class="menu dropdown-menu pull-right" role="menu"></ul>'},_templateDivider:function(){return'<li class="divider"></li>'},_template:function(e){var t='<div id="'+e.id+'" class="button-menu btn-group">'+'<button type="button" class="root btn btn-default dropdown-toggle" data-toggle="dropdown">';return e.icon&&(t+='<i class="fa '+e.icon+'"></i>'),t+="</button></div>",t}}),p=Backbone.View.extend({optionsDefault:{value:"",type:"text",placeholder:"",disabled:!1,visible:!0},initialize:function(t){this.options=e.merge(t,this.optionsDefault),this.setElement(this._template(this.options)),this.options.disabled&&this.$el.prop("disabled",!0),this.options.visible||this.$el.hide();var n=this;this.$el.on("input",function(){n.options.onchange&&n.options.onchange(n.$el.val())})},value:function(e){return e!==undefined&&this.$el.val(e),this.$el.val()},_template:function(e){return'<input id="'+e.id+'" type="'+e.type+'" value="'+e.value+'" placeholder="'+e.placeholder+'" class="form-control">'}});return{Label:r,Image:n,Button:s,Icon:u,ButtonIcon:o,Input:p,Anchor:a,Message:f,Searchbox:l,Title:c,Text:i,Select:t,ButtonMenu:h}}),define("plugin/library/jobs",["utils/utils"],function(e){return Backbone.Model.extend({initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault)},request:function(e,t,n,r,i){var s=this;e.state("wait","Requesting job results...");var o=e.get("dataset_id_job");o!=""?s._wait(e,r,i):s._submit(e,t,n,r,i)},cleanup:function(t){var n=this,r=t.get("dataset_id_job");r!=""&&(e.request("PUT",config.root+"api/histories/none/contents/"+r,{deleted:!0},function(){n._refreshHdas()}),t.set("dataset_id_job",""))},_submit:function(t,n,r,i,s){var o=this,u=t.id,a=t.get("type"),f=this.app.types.get(a);data={tool_id:"charts",inputs:{input:{id:t.get("dataset_id"),src:"hda"},module:f.execute,columns:r,settings:n}},t.state("wait","Sending job request..."),e.request("POST",config.root+"api/tools",data,function(e){if(!e.outputs||e.outputs.length==0)t.state("failed","Job submission failed. No response."),s&&s();else{o._refreshHdas();var n=e.outputs[0];t.state("wait","Your job has been queued. You may close the browser window. The job will run in the background."),t.set("dataset_id_job",n.id),this.app.storage.save(),o._wait(t,i,s)}},function(e){var n="";e&&e.message&&e.message.data&&e.message.data.input&&(n=e.message.data.input+"."),t.state("failed","This visualization requires the 'charts' tool. Please make sure it is installed. "+n),s&&s()})},_wait:function(t,n,r){var i=this;e.request("GET",config.root+"api/datasets/"+t.get("dataset_id_job"),{},function(e){var s=!1;switch(e.state){case"ok":t.state("wait","Job completed successfully..."),n&&n(e),s=!0;break;case"error":t.state("failed","Job has failed. Please check the history for details."),r&&r(e),s=!0;break;case"running":t.state("wait","Your job is running. You may close the browser window. The job will continue in the background.")}s||setTimeout(function(){i._wait(t,n,r)},i.app.config.get("query_timeout"))})},_refreshHdas:function(){Galaxy&&Galaxy.currHistoryPanel&&Galaxy.currHistoryPanel.refreshContents()}})}),define("plugin/library/datasets",["utils/utils"],function(e){return Backbone.Collection.extend({list:{},cache:{},initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault)},request:function(e,t,n){var r=this;e.groups?this._get_dataset(e.id,function(){r._get(e,t)}):this._get_dataset(e.id,t,n)},_get_dataset:function(t,n,r){var i=this.list[t];if(i){n(i);return}var s=this;e.request("GET",config.root+"api/datasets/"+t,{},function(e){switch(e.state){case"error":r&&r(e);break;default:s.list[t]=e,n(e)}})},_block_id:function(e,t){return e.id+"_"+e.start+"_"+e.end+"_"+t},_get:function(e,t){e.start||(e.start=0),e.end||(e.end=this.app.config.get("query_limit"));var n=[],r={},i=0;for(var s in e.groups){var o=e.groups[s];for(var u in o.columns){var a=o.columns[u].index,f=this._block_id(e,a);if(this.cache[f]||a==="auto")continue;!r[a]&&a!==undefined&&(r[a]=i,n.push(a),i++)}}if(n.length==0){this._fill_from_cache(e),t(e);return}var l={dataset_id:e.id,start:e.start,end:e.end,columns:n},c=this;this._fetch(l,function(r){for(var i in r){var s=n[i],o=c._block_id(e,s);c.cache[o]=r[i]}c._fill_from_cache(e),t(e)})},_fill_from_cache:function(e){console.debug("Datasets::_fill_from_cache() - Filling request from cache.");var t=e.start,n=0;for(var r in e.groups){var i=e.groups[r];for(var s in i.columns){var o=i.columns[s],u=this._block_id(e,o.index),a=this.cache[u];a&&(n=Math.max(n,a.length))}}n==0&&console.debug("FAILED - Datasets::_fill_from_cache() - Invalid range.");for(var r in e.groups){var i=e.groups[r];i.values=[];for(var f=0;f<n;f++)i.values[f]={x:parseInt(f)+t}}for(var r in e.groups){var i=e.groups[r];for(var s in i.columns){var o=i.columns[s];if(o.index=="auto")for(var f=0;f<n;f++){var l=i.values[f];l[s]=parseInt(f)+t}else{var u=this._block_id(e,o.index),a=this.cache[u];for(var f=0;f<n;f++){var l=i.values[f],c=a[f];isNaN(c)&&!o.is_label&&(c=0),l[s]=c}}}}},_fetch:function(t,n){var r=t.start?t.start:0,i=Math.abs(t.end-t.start),s=this.app.config.get("query_limit");if(!i||i>s)i=s;var o=0;t.columns&&(o=t.columns.length,console.debug("Datasets::_fetch() - Fetching "+o+" column(s)")),o==0&&console.debug("Datasets::_fetch() - No columns requested");var u="";for(var a in t.columns)u+=t.columns[a]+",";u=u.substring(0,u.length-1);var f=this;e.request("GET",config.root+"api/datasets/"+t.dataset_id,{data_type:"raw_data",provider:"dataset-column",limit:i,offset:r,indeces:u},function(e){var t=new Array(o);for(var r=0;r<o;r++)t[r]=[];for(var r in e.data){var i=e.data[r];for(var s in i){var u=i[s];u!==undefined&&u!=2147483647&&t[s].push(u)}}console.debug("Datasets::_fetch() - Fetching complete."),n(t)})}})}),define("plugin/models/group",[],function(){return Backbone.Model.extend({defaults:{key:"Data label",date:""},reset:function(){this.clear({silent:!0}).set(this.defaults),this.trigger("reset",this)}})}),define("plugin/models/groups",["plugin/models/group"],function(e){return Backbone.Collection.extend({model:e})}),define("plugin/library/deferred",["utils/utils"],function(e){return Backbone.Model.extend({queue:[],process:{},counter:0,initialize:function(){this.on("refresh",function(){if(this.counter==0)for(var e in this.queue){var t=this.queue[e];this.queue.splice(e,1),t()}})},execute:function(e){this.queue.push(e),this.trigger("refresh")},register:function(){var t=e.uuid();return this.process[t]=!0,this.counter++,console.debug("Deferred:register() - Registering "+t),t},done:function(e){this.process[e]&&(delete this.process[e],this.counter--,console.debug("Deferred:done() - Unregistering "+e),this.trigger("refresh"))},ready:function(){return this.counter==0}})});var Visualization=Backbone.Model.extend({defaults:{config:{}},urlRoot:function(){var e="/api/visualizations";return window.galaxy_config&&galaxy_config.root?galaxy_config.root+e:e},initialize:function(e){_.isObject(e.config)&&_.isObject(this.defaults.config)&&_.defaults(e.config,this.defaults.config),this._setUpListeners()},_setUpListeners:function(){},set:function(e,t){if(e==="config"){var n=this.get("config");_.isObject(n)&&(t=_.extend(_.clone(n),t))}return Backbone.Model.prototype.set.call(this,e,t),this},toString:function(){var e=this.get("id")||"";return this.get("title")&&(e+=":"+this.get("title")),"Visualization("+e+")"}}),VisualizationCollection=Backbone.Collection.extend({model:Visualization,url:function(){return galaxy_config.root+"api/visualizations"},initialize:function(e,t){t=t||{}},set:function(e,t){var n=this;e=_.map(e,function(e){var t=n.get(e.id);if(!t)return e;var r=t.toJSON();return _.extend(r,e),r}),Backbone.Collection.prototype.set.call(this,e,t)},toString:function(){return["VisualizationCollection(",[this.historyId,this.length].join(),")"].join("")}});define("mvc/visualization/visualization-model",function(){}),define("plugin/models/chart",["plugin/models/groups","plugin/library/deferred","mvc/visualization/visualization-model"],function(e,t){return Backbone.Model.extend({defaults:{id:null,title:"",type:"",date:null,state:"",state_info:"",modified:!1,dataset_id:"",dataset_id_job:""},initialize:function(n){this.groups=new e,this.settings=new Backbone.Model,this.deferred=new t},reset:function(){this.clear({silent:!0}).set(this.defaults),this.groups.reset(),this.settings.clear(),this.trigger("reset",this)},copy:function(e){var t=this;t.clear({silent:!0}).set(this.defaults),t.set(e.attributes),t.settings=e.settings.clone(),t.groups.reset(),e.groups.each(function(e){t.groups.add(e.clone())}),t.trigger("change",t)},state:function(e,t){this.set("state",e),this.set("state_info",t),this.trigger("set:state"),console.debug("Chart:state() - "+t+" ("+e+")")}})}),define("plugin/library/storage",["utils/utils","plugin/models/chart","plugin/models/group"],function(e,t,n){return Backbone.Model.extend({vis:null,initialize:function(e){this.app=e,this.chart=this.app.chart,this.options=this.app.options,this.id=this.options.id,this.vis=new Visualization({type:"charts",config:{dataset_id:this.options.config.dataset_id,chart_dict:{}}}),this.id&&(this.vis.id=this.id);var t=this.options.config.chart_dict;t&&(this.vis.get("config").chart_dict=t)},save:function(){var e=this.app.chart;this.vis.get("config").chart_dict={};var t=e.get("title");t!=""&&this.vis.set("title",t);var n={attributes:e.attributes,settings:e.settings.attributes,groups:[]};e.groups.each(function(e){n.groups.push(e.attributes)}),this.vis.get("config").chart_dict=n;var r=this;this.vis.save().fail(function(e,t,n){console.error(e,t,n)}).then(function(e){e&&e.id&&(r.id=e.id)})},load:function(){var e=this.vis.get("config").chart_dict;if(!e.attributes)return!1;this.chart.set(e.attributes),this.chart.state("ok","Loading saved visualization..."),this.chart.settings.set(e.settings);for(var t in e.groups)this.chart.groups.add(new n(e.groups[t]));return this.chart.set("modified",!1),!0}})}),define("plugin/library/screenshot",["libs/underscore"],function(e){function t(e,t,i){var s=new XMLSerializer,o="",u=this,a=0,f=0;return e.find("svg").each(function(){var e=$(this);n(e),a=parseInt(e.css("height")),f=parseInt(e.css("width")),e.attr({version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:f,height:a}),o+=s.serializeToString(this)}),r(e,t,{filename:i||"chart",type:"application/pdf",height:a,width:f,scale:2,svg:o}),o}function n(e){for(var t in document.styleSheets){var n=document.styleSheets[t],r=n.cssRules;if(r)for(var i=0,s=r.length;i<s;i++)try{e.find(r[i].selectorText).each(function(e,t){t.style.cssText+=r[i].style.cssText})}catch(o){}}}function r(e,t,n){var r=e.find("#viewport-form");r.length===0&&(r=$("<form>",{id:"viewport-form",method:"post",action:t,display:"none"}),e.append(r)),r.empty();for(name in n){var i=$("<input/>",{type:"hidden",name:name,value:n[name]});r.append(i)}try{r.submit()}catch(s){console.log(s)}}return{create:t}}),define("plugin/views/viewport",["mvc/ui/ui-portlet","plugin/library/ui","plugin/library/screenshot","utils/utils"],function(e,t,n,r){return Backbone.View.extend({container_list:[],canvas_list:[],initialize:function(e,t){this.app=e,this.chart=this.app.chart,this.options=r.merge(t,this.optionsDefault),this.setElement($(this._template())),this._fullscreen(this.$el,80),this._createContainer("div");var n=this;this.chart.on("redraw",function(){n._draw(n.chart)}),this.chart.on("set:state",function(){var e=n.$el.find("#info"),t=n.$el.find("container"),r=e.find("#icon");r.removeClass(),e.show(),e.find("#text").html(n.chart.get("state_info")),t.hide();var i=n.chart.get("state");switch(i){case"ok":e.hide(),t.show();break;case"failed":r.addClass("icon fa fa-warning");break;default:r.addClass("icon fa fa-spinner fa-spin")}})},show:function(){this.$el.show()},hide:function(){this.$el.hide()},_fullscreen:function(e,t){e.css("height",$(window).height()-t),$(window).resize(function(){e.css("height",$(window).height()-t)})},_createContainer:function(e,t){t=t||1;for(var n in this.container_list)this.container_list[n].remove();this.container_list=[],this.canvas_list=[];for(var n=0;n<t;n++){var r=$(this._templateContainer(e,parseInt(100/t)));this.$el.append(r),this.container_list[n]=r,e=="svg"?this.canvas_list[n]=d3.select(r.find("#canvas")[0]):this.canvas_list[n]=r.find("#canvas")}},_draw:function(e){var t=this,n=e.deferred.register(),r=e.get("type");this.chart_settings=this.app.types.get(r);var i=this.chart_settings.use_panels,s=1;i&&(s=e.groups.length),this._createContainer(this.chart_settings.tag,s),e.state("wait","Please wait...");if(!this.chart_settings.execute||this.chart_settings.execute&&e.get("modified"))this.app.jobs.cleanup(e),e.set("modified",!1);var t=this;require(["plugin/charts/"+r+"/wrapper"],function(r){var i=new r(t.app,{canvas:t.canvas_list});t.chart_settings.execute?t.app.jobs.request(e,t._defaultSettingsString(e),t._defaultRequestString(e),function(){i.draw(n,e,t._defaultRequestDictionary(e))},function(){e.deferred.done(n)}):i.draw(n,e,t._defaultRequestDictionary(e))})},_defaultRequestString:function(e){var t="",n=0,r=this;return e.groups.each(function(e){n++;for(var i in r.chart_settings.columns)t+=i+"_"+n+":"+(parseInt(e.get(i))+1)+", "}),t.substring(0,t.length-2)},_defaultSettingsString:function(e){var t="";for(key in e.settings.attributes)t+=key+":"+e.settings.get(key)+", ";return t.substring(0,t.length-2)},_defaultRequestDictionary:function(e){var t={groups:[]};this.chart_settings.execute?t.id=e.get("dataset_id_job"):t.id=e.get("dataset_id");var n=0,r=this;return e.groups.each(function(e){var i={};for(var s in r.chart_settings.columns){var o=r.chart_settings.columns[s];i[s]={index:e.get(s),is_label:o.is_label}}t.groups.push({key:++n+":"+e.get("key"),columns:i})}),t},_template:function(){return'<div class="charts-viewport"><div id="info" class="info"><span id="icon" class="icon" /><span id="text" class="text" /></div></div>'},_templateContainer:function(e,t){return'<div class="charts-viewport-container" style="width:'+t+'%;">'+'<div id="menu"/>'+"<"+e+' id="canvas" class="charts-viewport-canvas">'+"</div>"}})}),define("plugin/views/viewer",["utils/utils","plugin/library/ui","mvc/ui/ui-portlet","plugin/models/group","plugin/views/viewport","plugin/library/screenshot"],function(e,t,n,r,i,s){return Backbone.View.extend({initialize:function(e,r){this.app=e,this.chart=this.app.chart,this.viewport_view=new i(e);var o=this;this.portlet=new n.View({icon:"fa-bar-chart-o",title:"Viewport",operations:{edit_button:new t.ButtonIcon({icon:"fa-edit",tooltip:"Customize this chart",title:"Editor",onclick:function(){o._wait(o.chart,function(){o.app.go("editor")})}}),picture_button:new t.ButtonIcon({icon:"fa-camera",tooltip:"Download PDF-file ("+o.app.config.get("screenshot_url")+")",title:"Screenshot",onclick:function(){o._wait(o.chart,function(){s.create(o.viewport_view.$el,o.app.config.get("screenshot_url"),o.chart.get("title"))})}})}}),this.portlet.append(this.viewport_view.$el),this.setElement(this.portlet.$el);var o=this;this.chart.on("change:title",function(){o._refreshTitle()})},show:function(){this.$el.show(),$(window).trigger("resize")},hide:function(){this.$el.hide()},_refreshTitle:function(){var e=this.chart.get("title");this.portlet.title(e)},_wait:function(e,t){if(e.deferred.ready())t();else{var n=this;this.app.modal.show({title:"Please wait!",body:"Your chart is currently being processed. Please wait and try again.",buttons:{Close:function(){n.app.modal.hide()},Retry:function(){n.app.modal.hide(),setTimeout(function(){n._wait(e,t)},n.app.config.get("query_timeout"))}}})}}})}),define("mvc/ui/ui-tabs",["utils/utils"],function(e){var t=Backbone.View.extend({optionsDefault:{title_new:"",operations:null,onnew:null},initialize:function(t){this.visible=!1,this.$nav=null,this.$content=null,this.first_tab=null,this.options=e.merge(t,this.optionsDefault);var n=$(this._template(this.options));this.$nav=n.find(".tab-navigation"),this.$content=n.find(".tab-content"),this.setElement(n),this.list={};var r=this;this.options.operations&&$.each(this.options.operations,function(e,t){t.$el.prop("id",e),r.$nav.find(".operations").append(t.$el)});if(this.options.onnew){var i=$(this._template_tab_new(this.options));this.$nav.append(i),i.tooltip({title:"Add a new tab",placement:"bottom",container:r.$el}),i.on("click",function(e){i.tooltip("hide"),r.options.onnew()})}},add:function(e){var t=this,n=e.id,r=$(this._template_tab(e)),i=$(this._template_tab_content(e));this.list[n]=e.ondel?!0:!1,this.options.onnew?this.$nav.find("#new-tab").before(r):this.$nav.append(r),i.append(e.$el),this.$content.append(i),_.size(this.list)==1&&(r.addClass("active"),i.addClass("active"),this.first_tab=n);if(e.ondel){var s=r.find("#delete");s.tooltip({title:"Delete this tab",placement:"bottom",container:t.$el}),s.on("click",function(){return s.tooltip("destroy"),t.$el.find(".tooltip").remove(),e.ondel(),!1})}r.on("click",function(r){r.preventDefault(),e.onclick?e.onclick():t.show(n)})},del:function(e){this.$el.find("#tab-"+e).remove(),this.$el.find("#tab-content-"+e).remove(),this.first_tab==e&&(this.first_tab=null),this.first_tab!=null&&this.show(this.first_tab)},delRemovable:function(){for(var e in this.list)this.list[e]&&this.del(e)},show:function(e){this.$el.fadeIn("fast"),this.visible=!0,e&&(this.$el.find(".active").removeClass("active"),this.$el.find("#tab-"+e).addClass("active"),this.$el.find("#tab-content-"+e).addClass("active"))},hide:function(){this.$el.fadeOut("fast"),this.visible=!1},hideOperation:function(e){this.$nav.find("#"+e).hide()},showOperation:function(e){this.$nav.find("#"+e).show()},setOperation:function(e,t){var n=this.$nav.find("#"+e);n.off("click"),n.on("click",t)},title:function(e,t){var n=this.$el.find("#tab-title-text-"+e);return t&&n.html(t),n.html()},_template:function(e){return'<div class="ui-tabs tabbable tabs-left"><ul id="tab-navigation" class="tab-navigation nav nav-tabs"><div class="operations" style="float: right; margin-bottom: 4px;"></div></ul><div id="tab-content" class="tab-content"/></div>'},_template_tab_new:function(e){return'<li id="new-tab"><a href="javascript:void(0);"><i class="ui-tabs-add fa fa-plus-circle"/>'+e.title_new+"</a>"+"</li>"},_template_tab:function(e){var t='<li id="tab-'+e.id+'">'+'<a id="tab-title-link-'+e.id+'" title="" href="#tab-content-'+e.id+'" data-original-title="">'+'<span id="tab-title-text-'+e.id+'">'+e.title+"</span>";return e.ondel&&(t+='<i id="delete" class="ui-tabs-delete fa fa-minus-circle"/>'),t+="</a></li>",t},_template_tab_content:function(e){return'<div id="tab-content-'+e.id+'" class="tab-pane"/>'}});return{View:t}}),define("plugin/library/ui-table",["utils/utils"],function(e){var t=Backbone.View.extend({row:null,row_count:0,optionsDefault:{content:"No content available.",onchange:null,ondblclick:null,onconfirm:null,cls:"grid"},events:{click:"_onclick",dblclick:"_ondblclick"},initialize:function(t){this.options=e.merge(t,this.optionsDefault);var n=$(this._template(t));this.$thead=n.find("thead"),this.$tbody=n.find("tbody"),this.$tmessage=n.find("tmessage"),this.setElement(n),this.row=$("<tr></tr>")},addHeader:function(e){var t=$("<th></th>");t.append(e),this.row.append(t)},appendHeader:function(){this.$thead.append(this.row),this.row=$("<tr></tr>")},add:function(e,t,n){var r=$("<td></td>");t&&r.css("width",t),n&&r.css("text-align",n),r.append(e),this.row.append(r)},append:function(e){this._commit(e)},prepend:function(e){this._commit(e,!0)},get:function(e){return this.$el.find("#"+e)},del:function(e){var t=this.$tbody.find("#"+e);t.length>0&&(t.remove(),this.row_count--,this._refresh())},delAll:function(){this.$tbody.html(""),this.row_count=0,this._refresh()},value:function(e){this.before=this.$tbody.find(".current").attr("id"),e!==undefined&&(this.$tbody.find("tr").removeClass("current"),e&&this.$tbody.find("#"+e).addClass("current"));var t=this.$tbody.find(".current").attr("id");return t===undefined?null:(t!=this.before&&this.options.onchange&&this.options.onchange(e),t)},size:function(){return this.$tbody.find("tr").length},_commit:function(e,t){this.del(e),this.row.attr("id",e),t?this.$tbody.prepend(this.row):this.$tbody.append(this.row),this.row=$("<tr></tr>"),this.row_count++,this._refresh()},_onclick:function(e){var t=this.value(),n=$(e.target).closest("tr").attr("id");n!=""&&n&&t!=n&&(this.options.onconfirm?this.options.onconfirm(n):this.value(n))},_ondblclick:function(e){var t=this.value();t&&this.options.ondblclick&&this.options.ondblclick(t)},_refresh:function(){this.row_count==0?this.$tmessage.show():this.$tmessage.hide()},_template:function(e){return'<div><table class="'+e.cls+'">'+"<thead></thead>"+'<tbody style="cursor: pointer;"></tbody>'+"</table>"+"<tmessage>"+e.content+"</tmessage>"+"<div>"}});return{View:t}}),define("plugin/views/group",["plugin/library/ui-table","plugin/library/ui","utils/utils"],function(e,t,n){return Backbone.View.extend({initialize:function(r,i){this.app=r;var s=this;this.chart=this.app.chart,this.group=i.group,this.group_key=new t.Input({placeholder:"Data label",onchange:function(e){s.group.set("key",e)}}),this.table=new e.View({content:"No data column."});var o=$("<div/>");o.append(n.wrap((new t.Label({title:"Provide a label:"})).$el)),o.append(n.wrap(this.group_key.$el)),o.append(n.wrap((new t.Label({title:"Select columns:"})).$el)),o.append(n.wrap(this.table.$el)),this.setElement(o);var s=this;this.chart.on("change:dataset_id",function(){s._refreshTable()}),this.chart.on("change:type",function(){s._refreshTable()}),this.group.on("change:key",function(){s._refreshGroupKey()}),this.group.on("change",function(){s._refreshGroup()}),this._refreshTable(),this._refreshGroupKey(),this._refreshGroup()},_refreshTable:function(){var e=this.chart.get("dataset_id"),n=this.chart.get("type");if(!e||!n)return;var r=this,i=this.app.types.get(n);this.table.delAll();var s={};for(var o in i.columns){var u=i.columns[o];if(!u){console.debug("Group::_refreshTable() - Skipping column definition.");continue}var a=new t.Select.View({id:"select_"+o,wait:!0});this.table.add(u.title,"25%"),this.table.add(a.$el),this.table.append(o),s[o]=a}this.chart.state("wait","Loading metadata...");var f=this.chart.deferred.register();this.app.datasets.request({id:e},function(e){for(var t in s)r._addRow(t,e,s[t],i.columns[t]);r.chart.state("ok","Metadata initialized..."),r.chart.deferred.done(f)})},_addRow:function(e,t,n,r){var i=r.is_label,s=r.is_auto,o=[];s&&o.push({label:"Column: Row Number",value:"auto"});var u=t.metadata_column_types;for(var a in u)(!i&&(u[a]=="int"||u[a]=="float")||i)&&o.push({label:"Column: "+(parseInt(a)+1)+" ["+u[a]+"]",value:a});n.update(o),n.show();if(!n.exists(this.group.get(e))){var f=n.first();console.debug('Group()::_addRow() - Switching model value from "'+this.group.get(e)+'" to "'+f+'".'),this.group.set(e,f)}n.value(this.group.get(e));var l=this;n.setOnChange(function(t){l.group.set(e,t),l.chart.set("modified",!0)})},_refreshGroup:function(){this.group.set("date",n.time())},_refreshGroupKey:function(){var e=this.group.get("key");e===undefined&&(e=""),this.group_key.value(e)}})}),define("plugin/library/ui-table-form",["plugin/library/ui-table","plugin/library/ui","utils/utils"],function(e,t,n){var r=Backbone.View.extend({optionsDefault:{title:"",content:"",mode:""},list:[],initialize:function(r){this.table_title=new t.Label({title:r.title}),this.table=new e.View({content:r.content});var i=$("<div/>");i.append(n.wrap(this.table_title.$el)),i.append(n.wrap(this.table.$el)),this.setElement(i)},title:function(e){this.table_title.title(e)},update:function(e,t){this.table.delAll(),this.list=[];for(var n in e)this._add(n,e[n],t);for(var n in this.list)this.list[n].trigger("change")},_add:function(e,n,r){var i=this,s=null,o=n.type;switch(o){case"text":s=new t.Input({id:"field_"+e,placeholder:n.placeholder,value:r.get(e),onchange:function(t){r.set(e,t)}});break;case"select":s=new t.Select.View({id:"field_"+e,data:n.data,value:r.get(e),onchange:function(t){r.set(e,t);var s=_.findWhere(n.data,{value:t});if(s){for(var o in s.show){var u=s.show[o];i.table.get(u).show()}for(var o in s.hide){var u=s.hide[o];i.table.get(u).hide()}}}});break;case"separator":s=$("<div/>");break;default:console.log("ui-table-form:_add","Unknown setting type ("+n.type+")");return}if(o!="separator"){r.get(e)||r.set(e,n.init),s.value(r.get(e)),this.list[e]=s;var u=$("<div/>");u.append(s.$el),u.append('<div class="toolParamHelp" style="font-size: 0.9em;">'+n.info+"</div>"),this.table.add('<span style="white-space: nowrap;">'+n.title+"</span>","25%"),this.table.add(u)}else this.table.add('<h6 style="white-space: nowrap;">'+n.title+":<h6/>"),this.table.add($("<div/>"));this.table.append(e)}});return{View:r}}),define("plugin/views/settings",["plugin/library/ui","plugin/library/ui-table-form","utils/utils"],function(e,t,n){return Backbone.View.extend({initialize:function(e,n){this.app=e;var r=this;this.chart=this.app.chart,this.form=new t.View({title:"Chart options:",content:"This chart type does not provide any options."}),this.setElement(this.form.$el);var r=this;this.chart.on("change",function(){r._refreshTable()})},_refreshTable:function(){var e=this.chart.get("type");if(!e)return;var t=this.app.types.get(e);this.form.title(t.category+" - "+t.title+":"),this.form.update(t.settings,this.chart.settings)}})}),define("plugin/views/types",["utils/utils","plugin/library/ui"],function(e,t){return Backbone.View.extend({optionsDefault:{onchange:null,ondblclick:null},events:{click:"_onclick",dblclick:"_ondblclick"},initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault);var r=$('<div class="charts-grid"/>');this.setElement(r);var i={},s=t.types.attributes;for(var o in s){var u=s[o],a=u.category;i[a]||(i[a]={}),i[a][o]=u}for(var a in i){var r=$('<div style="clear: both;"/>');r.append(e.wrap(this._template_header({title:a})));for(var o in i[a]){var u=i[a][o];r.append(e.wrap(this._template_item({id:o,title:u.title,url:config.app_root+"charts/"+o+"/logo.png"})))}this.$el.append(e.wrap(r))}},value:function(e){var t=this.$el.find(".current").attr("id");e!==undefined&&(this.$el.find(".current").removeClass("current"),this.$el.find("#"+e).addClass("current"));var n=this.$el.find(".current").attr("id");return n===undefined?null:(n!=t&&this.options.onchange&&this.options.onchange(e),n)},_onclick:function(e){var t=this.value(),n=$(e.target).closest(".item").attr("id");n!=""&&n&&t!=n&&this.value(n)},_ondblclick:function(e){var t=this.value();t&&this.options.ondblclick&&this.options.ondblclick(t)},_template_header:function(e){return'<div class="header">• '+e.title+"<div>"},_template_item:function(e){return'<div id="'+e.id+'" class="item">'+'<img class="image" src="'+e.url+'">'+'<div class="title">'+e.title+"</div>"+"<div>"}})}),define("plugin/views/editor",["mvc/ui/ui-tabs","plugin/library/ui","mvc/ui/ui-portlet","utils/utils","plugin/models/chart","plugin/models/group","plugin/views/group","plugin/views/settings","plugin/views/types"],function(e,t,n,r,i,s,o,u,a){return Backbone.View.extend({optionsDefault:{header:!0,content:"No content available."},initialize:function(i,s){var o=this;this.app=i,this.chart=this.app.chart,this.options=r.merge(s,this.optionsDefault),this.portlet=new n.View({icon:"fa-bar-chart-o",title:"Editor",operations:{save:new t.ButtonIcon({icon:"fa-save",tooltip:"Draw Chart",title:"Draw",onclick:function(){o.app.go("viewer"),o._saveChart()}}),back:new t.ButtonIcon({icon:"fa-caret-left",tooltip:"Return to Viewer",title:"Return",onclick:function(){o.app.go("viewer"),o.app.storage.load()}})}}),this.types=new a(i,{onchange:function(e){o.chart.settings.clear(),o.chart.set({type:e}),o.chart.set("modified",!0)},ondblclick:function(e){o.app.go("viewer"),o._saveChart()}}),this.tabs=new e.View({title_new:"Add Data",onnew:function(){var e=o._addGroupModel();o.tabs.show(e.id)}}),this.title=new t.Input({placeholder:"Chart title",onchange:function(){o.chart.set("title",o.title.value())}});var f=$("<div/>");f.append(r.wrap((new t.Label({title:"Provide a chart title:"})).$el)),f.append(r.wrap(this.title.$el)),f.append(r.wrap((new t.Label({title:"Select a chart type:"})).$el)),f.append(r.wrap(this.types.$el)),this.tabs.add({id:"main",title:"Start",$el:f}),this.settings=new u(this.app),this.tabs.add({id:"settings",title:"Configuration",$el:this.settings.$el}),this.portlet.append(this.tabs.$el),this.setElement(this.portlet.$el),this.tabs.hideOperation("back");var o=this;this.chart.on("change:title",function(e){o._refreshTitle()}),this.chart.on("change:type",function(e){o.types.value(e.get("type"))}),this.chart.on("reset",function(e){o._resetChart()}),this.app.chart.on("redraw",function(e){o.portlet.showOperation("back")}),this.app.chart.groups.on("add",function(e){o._addGroup(e)}),this.app.chart.groups.on("remove",function(e){o._removeGroup(e)}),this.app.chart.groups.on("reset",function(e){o._removeAllGroups()}),this.app.chart.groups.on("change:key",function(e){o._refreshGroupKey()}),this._resetChart()},show:function(){this.$el.show()},hide:function(){this.$el.hide()},_refreshTitle:function(){var e=this.chart.get("title");this.portlet.title(e),this.title.value(e)},_refreshGroupKey:function(){var e=this,t=0;this.chart.groups.each(function(n){var r=n.get("key","");r==""&&(r="Chart data"),e.tabs.title(n.id,++t+": "+r)})},_addGroupModel:function(){var e=new s({id:r.uuid()});return this.chart.groups.add(e),e},_addGroup:function(e){var t=this,n=new o(this.app,{group:e}),r=t.chart.groups.length;this.tabs.add({id:e.id,$el:n.$el,ondel:function(){t.chart.groups.remove(e.id)}}),this._refreshGroupKey()},_removeGroup:function(e){this.tabs.del(e.id),this._refreshGroupKey(),this.chart.set("modified",!0)},_removeAllGroups:function(e){this.tabs.delRemovable()},_resetChart:function(){this.chart.set("id",r.uuid()),this.chart.set("type","nvd3_bar"),this.chart.set("dataset_id",this.app.options.config.dataset_id),this.chart.set("title","New Chart"),this.portlet.hideOperation("back")},_saveChart:function(){this.chart.set({type:this.types.value(),title:this.title.value(),date:r.time()}),this.chart.groups.length==0&&this._addGroupModel();var e=this;this.chart.deferred.execute(function(){e.app.storage.save(),e.chart.trigger("redraw")})}})}),define("plugin/models/config",[],function(){return Backbone.Model.extend({defaults:{query_limit:5e3,query_timeout:500,screenshot_url:"http://export.highcharts.com/"}})}),define("plugin/charts/nvd3/config",[],function(){return{title:"",category:"",library:"nvd3.js",tag:"svg",settings:{separator_label:{title:"X axis",type:"separator"},x_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"X-axis",placeholder:"Axis label"},x_axis_type:{title:"Axis value type",info:"Select the value type of the axis.",type:"select",init:"auto",data:[{label:"-- Do not show values --",value:"hide",hide:["x_axis_tick"]},{label:"Auto",value:"auto",hide:["x_axis_tick"]},{label:"Float",value:"f",show:["x_axis_tick"]},{label:"Exponent",value:"e",show:["x_axis_tick"]},{label:"Integer",value:"d",hide:["x_axis_tick"]},{label:"Percentage",value:"p",show:["x_axis_tick"]},{label:"Rounded",value:"r",show:["x_axis_tick"]},{label:"SI-prefix",value:"s",show:["x_axis_tick"]}]},x_axis_tick:{title:"Axis tick format",info:"Select the tick format for the axis.",type:"select",init:".1",data:[{label:"0.00001",value:".5"},{label:"0.0001",value:".4"},{label:"0.001",value:".3"},{label:"0.01",value:".2"},{label:"0.1",value:".1"},{label:"1",value:"1"}]},separator_tick:{title:"Y axis",type:"separator"},y_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"Y-axis",placeholder:"Axis label"},y_axis_type:{title:"Axis value type",info:"Select the value type of the axis.",type:"select",init:"auto",data:[{label:"-- Do not show values --",value:"hide",hide:["y_axis_tick"]},{label:"Auto",value:"auto",hide:["y_axis_tick"]},{label:"Float",value:"f",show:["y_axis_tick"]},{label:"Exponent",value:"e",show:["y_axis_tick"]},{label:"Integer",value:"d",hide:["y_axis_tick"]},{label:"Percentage",value:"p",show:["y_axis_tick"]},{label:"Rounded",value:"r",show:["y_axis_tick"]},{label:"SI-prefix",value:"s",show:["y_axis_tick"]}]},y_axis_tick:{title:"Axis tick format",info:"Select the tick format for the axis.",type:"select",init:".1",data:[{label:"0.00001",value:".5"},{label:"0.0001",value:".4"},{label:"0.001",value:".3"},{label:"0.01",value:".2"},{label:"0.1",value:".1"},{label:"1",value:"1"}]},separator_legend:{title:"Others",type:"separator"},show_legend:{title:"Show legend",info:"Would you like to add a legend?",type:"select",init:"true",data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]}}}}),define("plugin/charts/nvd3_bar/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Regular",category:"Bar diagrams",columns:{x:{title:"Values for x-axis",is_label:!0,is_auto:!0},y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_bar_stacked/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Stacked",category:"Bar diagrams",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_bar_horizontal/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Horizontal",category:"Bar diagrams",columns:{x:{title:"Values for x-axis",is_label:!0,is_auto:!0},y:{title:"Values for y-axis"}},settings:{x_axis_type:{init:"hide"}}})}),define("plugin/charts/nvd3_bar_horizontal_stacked/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Stacked horizontal",category:"Bar diagrams",settings:{x_axis_type:{init:"hide"}},columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_histogram/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Histogram",category:"Data processing (requires 'charts' tool from Toolshed)",execute:"histogram",columns:{y:{title:"Observations"}},settings:{x_axis_label:{init:"Breaks"},y_axis_label:{init:"Density"},y_axis_type:{init:"f"},y_axis_tick:{init:".2"}}})}),define("plugin/charts/nvd3_histogram_discrete/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Histogram (discrete)",category:"Data processing (requires 'charts' tool from Toolshed)",execute:"histogramdiscrete",columns:{x:{title:"Observations",is_label:!0}},settings:{x_axis_label:{init:"Breaks"},y_axis_label:{init:"Density"},y_axis_type:{init:"f"},y_axis_tick:{init:".2"}}})}),define("plugin/charts/nvd3_line/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line chart",category:"Others",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_line_focus/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line with focus",category:"Others",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_pie/config",[],function(){return{title:"Pie chart",library:"nvd3.js",category:"Area charts",tag:"svg",use_panels:!0,columns:{label:{title:"Labels",is_label:!0,is_auto:!0},y:{title:"Values"}},settings:{show_legend:{title:"Show legend",info:"Would you like to add a legend?",type:"select",init:"false",data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]},donut_ratio:{title:"Donut ratio",info:"Determine how large the donut hole will be.",type:"select",init:"0.5",data:[{label:"50%",value:"0.5"},{label:"25%",value:"0.25"},{label:"10%",value:"0.10"},{label:"0%",value:"0"}]},label_separator:{type:"separator",title:"Label settings"},label_type:{title:"Donut label",info:"What would you like to show for each slice?",type:"select",init:"percent",data:[{label:"-- Nothing --",value:"hide",hide:"label_outside"},{label:"Label column",value:"key",show:"label_outside"},{label:"Value column",value:"value",show:"label_outside"},{label:"Percentage",value:"percent",show:"label_outside"}]},label_outside:{title:"Show outside",info:"Would you like to show labels outside the donut?",type:"select",init:"false",data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]}}}}),define("plugin/charts/nvd3_scatter/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Scatter plot",category:"Others",columns:{x:{title:"Values for x-axis"},y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_stackedarea/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Regular",category:"Area charts",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_stackedarea_full/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Expanded",category:"Area charts",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/nvd3_stackedarea_stream/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Stream",category:"Area charts",columns:{y:{title:"Values for y-axis"}}})}),define("plugin/charts/highcharts_boxplot/config",[],function(){return{title:"Box plot",category:"Data processing (requires 'charts' tool from Toolshed)",library:"highcharts.js",tag:"div",execute:"boxplot",columns:{y:{title:"Observations"}},settings:{separator_label:{title:"X axis",type:"separator"},x_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"X-axis",placeholder:"Axis label"},separator_tick:{title:"Y axis",type:"separator"},y_axis_label:{title:"Axis label",info:"Provide a label for the axis.",type:"text",init:"Y-axis",placeholder:"Axis label"}}}}),define("plugin/charts/heatmap/config",[],function(){return{title:"Heatmap",category:"Data processing (requires 'charts' tool from Toolshed)",library:"",tag:"div",use_panels:!0,execute:"heatmap",columns:{row_label:{title:"Row labels",is_label:!0},col_label:{title:"Column labels",is_label:!0},value:{title:"Observations"}},settings:{color_set:{title:"Color scheme",info:"Select a color scheme for your heatmap",type:"select",init:"ocean",data:[{label:"Cold-to-Hot",value:"hot"},{label:"Cool",value:"cool"},{label:"Copper",value:"copper"},{label:"Gebco",value:"gebco"},{label:"Globe",value:"globe"},{label:"Gray scale",value:"gray"},{label:"Haxby",value:"haxby"},{label:"Jet",value:"jet"},{label:"No-Green",value:"no_green"},{label:"Ocean",value:"ocean"},{label:"Polar",value:"polar"},{label:"Rainbow",value:"rainbow"},{label:"Red-to-Green",value:"redgreen"},{label:"Red-to-green (saturated)",value:"red2green"},{label:"Relief",value:"relief"},{label:"Seismograph",value:"seis"},{label:"Sealand",value:"sealand"},{label:"Split",value:"split"},{label:"Topo",value:"topo"},{label:"Wysiwyg",value:"wysiwyg"}]},sorting:{title:"Sorting",info:"How should the columns be clustered?",type:"select",init:"hclust",data:[{label:"Read from dataset",value:"hclust"},{label:"Sort column and row labels",value:"byboth"},{label:"Sort column labels",value:"bycolumns"},{label:"Sort by rows",value:"byrow"}]}},menu:function(){return{color_set:this.settings.color_set}}}}),define("plugin/charts/types",["plugin/charts/nvd3_bar/config","plugin/charts/nvd3_bar_stacked/config","plugin/charts/nvd3_bar_horizontal/config","plugin/charts/nvd3_bar_horizontal_stacked/config","plugin/charts/nvd3_histogram/config","plugin/charts/nvd3_histogram_discrete/config","plugin/charts/nvd3_line/config","plugin/charts/nvd3_line_focus/config","plugin/charts/nvd3_pie/config","plugin/charts/nvd3_scatter/config","plugin/charts/nvd3_stackedarea/config","plugin/charts/nvd3_stackedarea_full/config","plugin/charts/nvd3_stackedarea_stream/config","plugin/charts/highcharts_boxplot/config","plugin/charts/heatmap/config"],function(e,t,n,r,i,s,o,u,a,f,l,c,h,p,d){return Backbone.Model.extend({defaults:{nvd3_bar:e,nvd3_bar_stacked:t,nvd3_bar_horizontal:n,nvd3_bar_horizontal_stacked:r,nvd3_stackedarea:l,nvd3_stackedarea_full:c,nvd3_stackedarea_stream:h,nvd3_line:o,nvd3_line_focus:u,nvd3_scatter:f,nvd3_pie:a,nvd3_histogram:i,nvd3_histogram_discrete:s,highcharts_boxplot:p,heatmap:d}})}),define("plugin/app",["mvc/ui/ui-modal","mvc/ui/ui-portlet","plugin/library/ui","utils/utils","plugin/library/jobs","plugin/library/datasets","plugin/library/storage","plugin/views/viewer","plugin/views/editor","plugin/models/config","plugin/models/chart","plugin/charts/types"],function(e,t,n,r,i,s,o,u,a,f,l,c){return Backbone.View.extend({initialize:function(t){this.options=t,Galaxy&&Galaxy.modal?this.modal=Galaxy.modal:this.modal=new e.View,this.config=new f,this.types=new c,this.chart=new l,this.jobs=new i(this),this.datasets=new s(this),this.storage=new o(this),this.viewer_view=new u(this),this.editor_view=new a(this),this.$el.append(this.viewer_view.$el),this.$el.append(this.editor_view.$el);if(!this.storage.load())this.go("editor");else{this.go("viewer");var n=this;this.chart.deferred.execute(function(){n.chart.trigger("redraw")})}},go:function(e){$(".tooltip").hide();switch(e){case"editor":this.editor_view.show(),this.viewer_view.hide();break;case"viewer":this.editor_view.hide(),this.viewer_view.show()}},execute:function(e){},onunload:function(){},log:function(e,t){console.log(e+" "+t)}})});
\ No newline at end of file
diff -r c42da63a174f7695eb48ee71107b18589e034418 -r f94244370cfa4b6e685a5ea46a2b2d4ede42b749 static/style/blue/base.css
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -1278,6 +1278,8 @@
.upload-ftp .upload-ftp-help{margin-bottom:10px}
.upload-ftp .upload-ftp-warning{text-align:center;margin-top:20px}
.upload-settings .upload-settings-cover{position:absolute;width:100%;height:100%;top:0px;left:0px;background:#fff;opacity:0.4;cursor:no-drop}
+.ui-tabs .ui-tabs-add{font-size:0.8em;margin-right:5px}
+.ui-tabs .ui-tabs-delete{font-size:0.8em;margin-left:5px;cursor:pointer}
.no-highlight{-webkit-user-select:none;-moz-user-select:none;-khtml-user-select:none;-ms-user-select:none;}
.portlet-view{min-width:300px}.portlet-view .portlet-title-icon{float:left;font-size:1.2em;padding-top:4px}
.portlet-view .portlet-title{overflow:hidden;padding:2px 5px 2px 10px}
diff -r c42da63a174f7695eb48ee71107b18589e034418 -r f94244370cfa4b6e685a5ea46a2b2d4ede42b749 static/style/src/less/ui.less
--- a/static/style/src/less/ui.less
+++ b/static/style/src/less/ui.less
@@ -1,3 +1,16 @@
+.ui-tabs {
+ .ui-tabs-add {
+ font-size : 0.8em;
+ margin-right : 5px;
+ }
+
+ .ui-tabs-delete {
+ font-size : 0.8em;
+ margin-left : 5px;
+ cursor : pointer;
+ }
+}
+
.no-highlight {
-webkit-user-select: none; /* webkit (safari, chrome) browsers */
-moz-user-select: none; /* mozilla browsers */
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