galaxy-commits
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions
commit/galaxy-central: Jeremy Goecks: Trackster: add color for read track histograms so that histograms are colorful. Read blocks are still gray to allow for seeing variants easily.
by commits-noreply@bitbucket.org 23 Apr '14
by commits-noreply@bitbucket.org 23 Apr '14
23 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/54695d3fa468/
Changeset: 54695d3fa468
User: Jeremy Goecks
Date: 2014-04-23 19:56:26
Summary: Trackster: add color for read track histograms so that histograms are colorful. Read blocks are still gray to allow for seeing variants easily.
Affected #: 2 files
diff -r b801d178c90c0ca22d4fe9c219af7f9ae603b8c9 -r 54695d3fa468250b1ee13a90facc753dc3d9831d static/scripts/viz/trackster/painters.js
--- a/static/scripts/viz/trackster/painters.js
+++ b/static/scripts/viz/trackster/painters.js
@@ -781,7 +781,7 @@
seq_offset = 0,
gap = Math.round(w_scale/2),
char_width_px = ctx.canvas.manager.char_width_px,
- block_color = (strand === "+" ? this.prefs.block_color : this.prefs.reverse_strand_color),
+ block_color = (strand === "+" ? this.prefs.detail_block_color : this.prefs.reverse_strand_color),
pack_mode = (mode === 'Pack'),
paint_utils = new ReadPainterUtils(ctx, (pack_mode ? PACK_FEATURE_HEIGHT : SQUISH_FEATURE_HEIGHT), w_scale, mode),
drawing_blocks = [];
diff -r b801d178c90c0ca22d4fe9c219af7f9ae603b8c9 -r 54695d3fa468250b1ee13a90facc753dc3d9831d static/scripts/viz/trackster/tracks.js
--- a/static/scripts/viz/trackster/tracks.js
+++ b/static/scripts/viz/trackster/tracks.js
@@ -4234,8 +4234,9 @@
extend(ReadTrack.prototype, Drawable.prototype, TiledTrack.prototype, FeatureTrack.prototype, {
config_params: _.union( Drawable.prototype.config_params, [
- { key: 'block_color', label: 'Block and sense strand color', type: 'color', 'default_value': '#DDDDDD' },
- { key: 'reverse_strand_color', label: 'Antisense strand color', type: 'color', 'default_value': '#AAAAAA' },
+ { key: 'block_color', label: 'Histogram color', type: 'color' },
+ { key: 'detail_block_color', label: 'Sense strand block color', type: 'color', 'default_value': '#AAAAAA' },
+ { key: 'reverse_strand_color', label: 'Antisense strand block color', type: 'color', 'default_value': '#DDDDDD' },
{ key: 'label_color', label: 'Label color', type: 'color', default_value: 'black' },
{ key: 'show_insertions', label: 'Show insertions', type: 'bool', default_value: false },
{ key: 'show_differences', label: 'Show differences only', type: 'bool', default_value: 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
commit/galaxy-central: carlfeberhard: Visualization framework: remove deprecated 'template_root' element left from when visualizations were loaded from galaxy's template directory
by commits-noreply@bitbucket.org 23 Apr '14
by commits-noreply@bitbucket.org 23 Apr '14
23 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/b801d178c90c/
Changeset: b801d178c90c
User: carlfeberhard
Date: 2014-04-23 17:59:21
Summary: Visualization framework: remove deprecated 'template_root' element left from when visualizations were loaded from galaxy's template directory
Affected #: 5 files
diff -r 1d1292f5b4ab6da6863f845ec538389c1e8659a9 -r b801d178c90c0ca22d4fe9c219af7f9ae603b8c9 config/plugins/visualizations/circster/config/circster.xml
--- a/config/plugins/visualizations/circster/config/circster.xml
+++ b/config/plugins/visualizations/circster/config/circster.xml
@@ -21,8 +21,6 @@
<param_modifier type="string" modifies="dataset_id">hda_ldda</param_modifier><param type="dbkey">dbkey</param></params>
- <!-- template_root and template are currently ignored for the 'built-in' visualizations -->
- <template_root>webapps/galaxy/visualization</template_root><template>circster.mako</template><render_target>_top</render_target></visualization>
diff -r 1d1292f5b4ab6da6863f845ec538389c1e8659a9 -r b801d178c90c0ca22d4fe9c219af7f9ae603b8c9 config/plugins/visualizations/phyloviz/config/phyloviz.xml
--- a/config/plugins/visualizations/phyloviz/config/phyloviz.xml
+++ b/config/plugins/visualizations/phyloviz/config/phyloviz.xml
@@ -14,8 +14,6 @@
<param type="dataset" var_name_in_template="hda" required="true">dataset_id</param><param type="integer" default="0">tree_index</param></params>
- <!-- template_root and template are currently ignored for the 'built-in' visualizations -->
- <template_root>webapps/galaxy/visualization</template_root><template>phyloviz.mako</template><render_target>_top</render_target></visualization>
diff -r 1d1292f5b4ab6da6863f845ec538389c1e8659a9 -r b801d178c90c0ca22d4fe9c219af7f9ae603b8c9 config/plugins/visualizations/sweepster/config/sweepster.xml
--- a/config/plugins/visualizations/sweepster/config/sweepster.xml
+++ b/config/plugins/visualizations/sweepster/config/sweepster.xml
@@ -20,8 +20,6 @@
<param type="hda_or_ldda" var_name_in_template="dataset">dataset_id</param><param_modifier type="string" modifies="dataset_id">hda_ldda</param_modifier></params>
- <!-- template_root and template are currently ignored for the 'built-in' visualizations -->
- <template_root>webapps/galaxy/visualization</template_root><template>sweepster.mako</template><render_target>_top</render_target></visualization>
diff -r 1d1292f5b4ab6da6863f845ec538389c1e8659a9 -r b801d178c90c0ca22d4fe9c219af7f9ae603b8c9 config/plugins/visualizations/trackster/config/trackster.xml
--- a/config/plugins/visualizations/trackster/config/trackster.xml
+++ b/config/plugins/visualizations/trackster/config/trackster.xml
@@ -23,8 +23,6 @@
<param type="genome_region">genome_region</param><param type="dbkey">dbkey</param></params>
- <!-- template_root and template are currently ignored for the 'built-in' visualizations -->
- <template_root>webapps/galaxy/visualization/tracks</template_root><template>browser.mako</template><render_target>_top</render_target></visualization>
diff -r 1d1292f5b4ab6da6863f845ec538389c1e8659a9 -r b801d178c90c0ca22d4fe9c219af7f9ae603b8c9 config/plugins/visualizations/visualization.dtd
--- a/config/plugins/visualizations/visualization.dtd
+++ b/config/plugins/visualizations/visualization.dtd
@@ -126,10 +126,6 @@
var_name_in_template CDATA #IMPLIED
>
-<!-- template_root: the directory to search for the template relative to templates/webapps/galaxy
- (optional) DEFAULT: visualizations
--->
-<!ELEMENT template_root (#PCDATA)><!-- template: the template used to render the visualization. REQUIRED --><!ELEMENT template (#PCDATA)><!-- render_target: used as the target attribute of the link to the visualization.
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
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/1d1292f5b4ab/
Changeset: 1d1292f5b4ab
User: dannon
Date: 2014-04-23 15:48:48
Summary: Merge stable.
Affected #: 1 file
diff -r 26575805a77b61e893a6601401499c970f156a0c -r 1d1292f5b4ab6da6863f845ec538389c1e8659a9 lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -764,7 +764,7 @@
def callback( input, value, prefixed_name, prefixed_label ):
if isinstance( input, DataToolParameter ):
data_input_names[ prefixed_name ] = True
- multiple_input[input.name] = input.multiple
+ multiple_input[ prefixed_name ] = input.multiple
visit_input_values( module.tool.inputs, module.state.inputs, callback )
# Filter
# FIXME: this removes connection without displaying a message currently!
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: dannon: Merged in jmchilton/galaxy-central-fork-1/stable (pull request #373)
by commits-noreply@bitbucket.org 23 Apr '14
by commits-noreply@bitbucket.org 23 Apr '14
23 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3b4272535922/
Changeset: 3b4272535922
Branch: stable
User: dannon
Date: 2014-04-23 15:44:28
Summary: Merged in jmchilton/galaxy-central-fork-1/stable (pull request #373)
[stable] Bugfix for multiple input data parameters.
Affected #: 1 file
diff -r 7a7985a007fbce2bfa7bea5d8d23af1d63fecbb1 -r 3b42725359224832317a066d95dff596f93ab33f lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -763,7 +763,7 @@
def callback( input, value, prefixed_name, prefixed_label ):
if isinstance( input, DataToolParameter ):
data_input_names[ prefixed_name ] = True
- multiple_input[input.name] = input.multiple
+ multiple_input[ prefixed_name ] = input.multiple
visit_input_values( module.tool.inputs, module.state.inputs, callback )
# Filter
# FIXME: this removes connection without displaying a message currently!
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/078dd25c5fa7/
Changeset: 078dd25c5fa7
Branch: stable
User: jmchilton
Date: 2014-04-19 21:37:02
Summary: Bugfix for multiple input data parameters.
Workflow editor would drop all but one multiple input data connections if and when the parameter itself was nested in repeats/conds.
Affected #: 1 file
diff -r e6876f6918548a0805fa0cc5306ba1ced6fb184a -r 078dd25c5fa78199f8c21b60b0bbe6b35f1eb0ad lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -763,7 +763,7 @@
def callback( input, value, prefixed_name, prefixed_label ):
if isinstance( input, DataToolParameter ):
data_input_names[ prefixed_name ] = True
- multiple_input[input.name] = input.multiple
+ multiple_input[ prefixed_name ] = input.multiple
visit_input_values( module.tool.inputs, module.state.inputs, callback )
# Filter
# FIXME: this removes connection without displaying a message currently!
https://bitbucket.org/galaxy/galaxy-central/commits/3b4272535922/
Changeset: 3b4272535922
Branch: stable
User: dannon
Date: 2014-04-23 15:44:28
Summary: Merged in jmchilton/galaxy-central-fork-1/stable (pull request #373)
[stable] Bugfix for multiple input data parameters.
Affected #: 1 file
diff -r 7a7985a007fbce2bfa7bea5d8d23af1d63fecbb1 -r 3b42725359224832317a066d95dff596f93ab33f lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -763,7 +763,7 @@
def callback( input, value, prefixed_name, prefixed_label ):
if isinstance( input, DataToolParameter ):
data_input_names[ prefixed_name ] = True
- multiple_input[input.name] = input.multiple
+ multiple_input[ prefixed_name ] = input.multiple
visit_input_values( module.tool.inputs, module.state.inputs, callback )
# Filter
# FIXME: this removes connection without displaying a message currently!
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
11 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a841d01847df/
Changeset: a841d01847df
User: jmchilton
Date: 2014-03-26 17:01:46
Summary: Unit tests to exercise job and task mapping.
Affected #: 1 file
diff -r 09985439d17f9bef026554938b05d0d6eedd06cb -r a841d01847dffac0f554f9730ec45829c38c05e8 test/unit/test_galaxy_mapping.py
--- a/test/unit/test_galaxy_mapping.py
+++ b/test/unit/test_galaxy_mapping.py
@@ -210,6 +210,29 @@
assert hist1.name == "History 2b"
# gvk TODO need to ad test for GalaxySessions, but not yet sure what they should look like.
+ def test_jobs( self ):
+ model = self.model
+ u = model.User( email="jobtest(a)foo.bar.baz", password="password" )
+ job = model.Job()
+ job.user = u
+ job.tool_id = "cat1"
+
+ self.persist( u, job )
+
+ loaded_job = model.session.query( model.Job ).filter( model.Job.user == u ).first()
+ assert loaded_job.tool_id == "cat1"
+
+ def test_tasks( self ):
+ model = self.model
+ u = model.User( email="jobtest(a)foo.bar.baz", password="password" )
+ job = model.Job()
+ task = model.Task( job=job, working_directory="/tmp", prepare_files_cmd="split.sh" )
+ job.user = u
+ self.persist( u, job, task )
+
+ loaded_task = model.session.query( model.Task ).filter( model.Task.job == job ).first()
+ assert loaded_task.prepare_input_files_cmd == "split.sh"
+
def test_history_contents( self ):
model = self.model
u = model.User( email="contents(a)foo.bar.baz", password="password" )
https://bitbucket.org/galaxy/galaxy-central/commits/ad576bf6497e/
Changeset: ad576bf6497e
User: jmchilton
Date: 2014-03-26 17:01:46
Summary: Layout models and mapping for a job metrics plugin framework.
Affected #: 4 files
diff -r a841d01847dffac0f554f9730ec45829c38c05e8 -r ad576bf6497ec40b64d84802bda596bb63af63af lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -17,6 +17,7 @@
import json
import socket
import time
+import numbers
from uuid import UUID, uuid4
from string import Template
from itertools import ifilter
@@ -87,6 +88,21 @@
return name
+class HasJobMetrics:
+
+ def _init_metrics( self ):
+ self.text_metrics = []
+ self.numeric_metrics = []
+
+ def add_metric( self, plugin, metric_name, metric_value ):
+ if isinstance( metric_value, numbers.Number ):
+ metric = self._numeric_metric( plugin, metric_name, metric_value )
+ self.numeric_metrics.append( metric )
+ else:
+ metric = self._text_metric( plugin, metric_name, metric_value )
+ self.text_metrics.append( metric )
+
+
class User( object, Dictifiable ):
use_pbkdf2 = True
"""
@@ -226,7 +242,31 @@
return Template( in_string ).safe_substitute( environment )
-class Job( object, Dictifiable ):
+class BaseJobMetric( object ):
+
+ def __init__( self, plugin, metric_name, metric_value ):
+ self.plugin = plugin
+ self.metric_name = metric_name
+ self.metric_value = metric_value
+
+
+class JobMetricText( BaseJobMetric ):
+ pass
+
+
+class JobMetricNumeric( BaseJobMetric ):
+ pass
+
+
+class TaskMetricText( BaseJobMetric ):
+ pass
+
+
+class TaskMetricNumeric( BaseJobMetric ):
+ pass
+
+
+class Job( object, HasJobMetrics, Dictifiable ):
dict_collection_visible_keys = [ 'id', 'state', 'exit_code', 'update_time', 'create_time' ]
dict_element_visible_keys = [ 'id', 'state', 'exit_code', 'update_time', 'create_time' ]
@@ -234,6 +274,9 @@
A job represents a request to run a tool given input datasets, tool
parameters, and output datasets.
"""
+ _numeric_metric = JobMetricNumeric
+ _text_metric = JobMetricText
+
states = Bunch( NEW = 'new',
UPLOAD = 'upload',
WAITING = 'waiting',
@@ -267,6 +310,7 @@
self.imported = False
self.handler = None
self.exit_code = None
+ self._init_metrics()
@property
def finished( self ):
@@ -472,10 +516,14 @@
return rval
-class Task( object ):
+
+class Task( object, HasJobMetrics ):
"""
A task represents a single component of a job.
"""
+ _numeric_metric = TaskMetricNumeric
+ _text_metric = TaskMetricText
+
states = Bunch( NEW = 'new',
WAITING = 'waiting',
QUEUED = 'queued',
@@ -498,6 +546,7 @@
self.stderr = ""
self.exit_code = None
self.prepare_input_files_cmd = prepare_files_cmd
+ self._init_metrics()
def get_param_values( self, app ):
"""
@@ -608,6 +657,7 @@
def set_prepare_input_files_cmd( self, prepare_input_files_cmd ):
self.prepare_input_files_cmd = prepare_input_files_cmd
+
class JobParameter( object ):
def __init__( self, name, value ):
self.name = name
diff -r a841d01847dffac0f554f9730ec45829c38c05e8 -r ad576bf6497ec40b64d84802bda596bb63af63af lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -470,6 +470,52 @@
Column( "archive_dir", TEXT )
)
+
+JOB_METRIC_MAX_LENGTH = 1023
+
+model.JobMetricText.table = Table(
+ "job_metric_text",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Unicode(JOB_METRIC_MAX_LENGTH), ),
+)
+
+model.TaskMetricText.table = Table(
+ "task_metric_text",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Unicode(JOB_METRIC_MAX_LENGTH), ),
+)
+
+
+model.JobMetricNumeric.table = Table(
+ "job_metric_numeric",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Numeric( 22, 7 ), ),
+)
+
+
+model.TaskMetricNumeric.table = Table(
+ "task_metric_numeric",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Numeric( 22, 7 ), ),
+)
+
+
model.GenomeIndexToolData.table = Table( "genome_index_tool_data", metadata,
Column( "id", Integer, primary_key=True ),
Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
@@ -1569,6 +1615,26 @@
job=relation( model.Job ), dataset=relation(
model.LibraryDatasetDatasetAssociation, lazy=False ) ) )
+simple_mapping(
+ model.JobMetricText,
+ job=relation( model.Job, backref="text_metrics" ),
+)
+
+simple_mapping(
+ model.TaskMetricText,
+ task=relation( model.Task, backref="text_metrics" ),
+)
+
+simple_mapping(
+ model.JobMetricNumeric,
+ job=relation( model.Job, backref="numeric_metrics" ),
+)
+
+simple_mapping(
+ model.TaskMetricNumeric,
+ task=relation( model.Task, backref="numeric_metrics" ),
+)
+
mapper( model.JobParameter, model.JobParameter.table )
mapper( model.JobExternalOutputMetadata, model.JobExternalOutputMetadata.table,
diff -r a841d01847dffac0f554f9730ec45829c38c05e8 -r ad576bf6497ec40b64d84802bda596bb63af63af lib/galaxy/model/migrate/versions/0119_job_metrics.py
--- /dev/null
+++ b/lib/galaxy/model/migrate/versions/0119_job_metrics.py
@@ -0,0 +1,102 @@
+"""
+Migration script for job metric plugins.
+"""
+
+from sqlalchemy import *
+from sqlalchemy.orm import *
+from migrate import *
+from migrate.changeset import *
+
+import datetime
+now = datetime.datetime.utcnow
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData()
+
+TEXT_METRIC_MAX_LENGTH = 1023
+
+JobMetricText_table = Table(
+ "job_metric_text",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Unicode(TEXT_METRIC_MAX_LENGTH), ),
+)
+
+
+TaskMetricText_table = Table(
+ "task_metric_text",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Unicode(TEXT_METRIC_MAX_LENGTH), ),
+)
+
+
+JobMetricNumeric_table = Table(
+ "job_metric_numeric",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Numeric( 22, 7 ), ),
+)
+
+
+TaskMetricNumeric_table = Table(
+ "task_metric_numeric",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Numeric( 22, 7 ), ),
+)
+
+
+TABLES = [
+ JobMetricText_table,
+ TaskMetricText_table,
+ JobMetricNumeric_table,
+ TaskMetricNumeric_table,
+]
+
+
+def upgrade( migrate_engine ):
+ metadata.bind = migrate_engine
+ print __doc__
+ metadata.reflect()
+
+ for table in TABLES:
+ __create(table)
+
+
+def downgrade(migrate_engine):
+ metadata.bind = migrate_engine
+ metadata.reflect()
+
+ for table in TABLES:
+ __drop(table)
+
+
+def __create(table):
+ try:
+ table.create()
+ except Exception as e:
+ print str(e)
+ log.debug("Creating %s table failed: %s" % (table.name, str( e ) ) )
+
+
+def __drop(table):
+ try:
+ table.drop()
+ except Exception as e:
+ print str(e)
+ log.debug("Dropping %s table failed: %s" % (table.name, str( e ) ) )
diff -r a841d01847dffac0f554f9730ec45829c38c05e8 -r ad576bf6497ec40b64d84802bda596bb63af63af test/unit/test_galaxy_mapping.py
--- a/test/unit/test_galaxy_mapping.py
+++ b/test/unit/test_galaxy_mapping.py
@@ -222,6 +222,18 @@
loaded_job = model.session.query( model.Job ).filter( model.Job.user == u ).first()
assert loaded_job.tool_id == "cat1"
+ def test_job_metrics( self ):
+ model = self.model
+ u = model.User( email="jobtest(a)foo.bar.baz", password="password" )
+ job = model.Job()
+ job.user = u
+ job.tool_id = "cat1"
+
+ job.add_metric( "gx", "galaxy_slots", 5 )
+ job.add_metric( "system", "system_name", "localhost" )
+
+ self.persist( u, job )
+
def test_tasks( self ):
model = self.model
u = model.User( email="jobtest(a)foo.bar.baz", password="password" )
https://bitbucket.org/galaxy/galaxy-central/commits/3ffd1ca6ac44/
Changeset: 3ffd1ca6ac44
User: jmchilton
Date: 2014-03-26 17:01:46
Summary: Implement plugin framework for collecting data about runtime job execution.
An example job_metrics_conf.xml.sample is included that describes which plugins are enabled and how they are configured. This will be updated for each new plugin added. By default not instrumentation or data collection occurs - but if a job_metrics.xml file is present it will serve as the default for all job destination. Additionally, individual job destinations may disable, load a different job metrics file, or define metrics directly in job_conf.xml in an embedded fashion. See comment at top of job_metrics_conf.xml for more information.
This commit include an initial plugin (named 'core') to demonstrate the framework and capture the highest priority data - namely the number of cores allocated to the job and the runtime of the job on the cluster. These two pieces of information alone should provide a much clearer picture of what Galaxy is actually allocating cluster compute cycles to.
Current limitations - This only works with job runners utilizing the job script module and the LWR (it utilizes the job script module on the remote server), hence it won't yet work with...
- Local job runner - I do have a downstream fork of Galaxy where I have reworked the local job runner to use the common job script template.
https://github.com/jmchilton/galaxy-central/commits/local_job_script
https://github.com/jmchilton/galaxy-central/commit/949db2cd14c7191cedf1febe…
- CLI runner - CLI runner needs to be reworked to use the job script module anyway so GALAXY_SLOTS works - the LWR version of the CLI runner uses the job script module - this work just needs to be back ported to Galaxy.
If a job_metrics_conf.xml is present and some jobs route to the above destinations - the jobs won't fail but annoying errors will appear in the logs. Simply attach a 'metrics="off"' those these specific job destinations to disable any attempt to use metrics for these jobs and disable these errors.
Affected #: 15 files
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 job_conf.xml.sample_advanced
--- a/job_conf.xml.sample_advanced
+++ b/job_conf.xml.sample_advanced
@@ -62,6 +62,11 @@
<!-- Warning: Local slot count doesn't tie up additional worker threads, to prevent over
allocating machine define a second local runner with different name and fewer workers
to run this destination. -->
+ <job_metrics />
+ <!-- Above element demonstrates embedded job metrics definition - see
+ job_metrics_conf.xml.sample for full documentation on possible nested
+ elements. This empty block will simply disable job metrics for the
+ corresponding destination. --></destination><destination id="pbs" runner="pbs" tags="mycluster"/><destination id="pbs_longjobs" runner="pbs" tags="mycluster,longjobs">
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 job_metrics_conf.xml.sample
--- /dev/null
+++ b/job_metrics_conf.xml.sample
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!-- If job_metrics.xml exists, this file will define the default job metric
+ plugin used for all jobs. Individual job_conf.xml destinations can
+ disable metric collection by setting metrics="off" on that destination.
+ The metrics attribute on destination definition elements can also be
+ a path - in which case that XML metrics file will be loaded and used for
+ that destination. Finally, the destination element may contain a job_metrics
+ child element (with all options defined below) to define job metrics in an
+ embedded manner directly in the job_conf.xml file.
+-->
+<job_metrics>
+ <!-- Each element in this file corresponds to a job instrumentation plugin
+ used to generate metrics in lib/galaxy/jobs/metrics/instrumenters. -->
+
+ <!-- Core plugin captures Galaxy slots, start and end of job (in seconds
+ since epoch) and computes runtime in seconds. -->
+ <core />
+</job_metrics>
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 lib/galaxy/app.py
--- a/lib/galaxy/app.py
+++ b/lib/galaxy/app.py
@@ -15,7 +15,7 @@
from galaxy.sample_tracking import external_service_types
from galaxy.openid.providers import OpenIDProviders
from galaxy.tools.data_manager.manager import DataManagers
-
+from galaxy.jobs import metrics as job_metrics
from galaxy.web.base import pluginframework
import logging
@@ -60,6 +60,10 @@
self._configure_tool_data_tables( from_shed_config=False )
+ # Initialize job metrics manager, needs to be in place before
+ # config so per-destination modifications can be made.
+ self.job_metrics = job_metrics.JobMetrics( self.config.job_metrics_config_file, app=self )
+
# Initialize the job management configuration
self.job_config = jobs.JobConfiguration(self)
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -137,6 +137,7 @@
self.template_path = resolve_path( kwargs.get( "template_path", "templates" ), self.root )
self.template_cache = resolve_path( kwargs.get( "template_cache_path", "database/compiled_templates" ), self.root )
self.dependency_resolvers_config_file = resolve_path( kwargs.get( 'dependency_resolvers_config_file', 'dependency_resolvers_conf.xml' ), self.root )
+ self.job_metrics_config_file = resolve_path( kwargs.get( 'job_metrics_config_file', 'job_metrics_conf.xml' ), self.root )
self.job_config_file = resolve_path( kwargs.get( 'job_config_file', 'job_conf.xml' ), self.root )
self.local_job_queue_workers = int( kwargs.get( "local_job_queue_workers", "5" ) )
self.cluster_job_queue_workers = int( kwargs.get( "cluster_job_queue_workers", "3" ) )
@@ -453,6 +454,11 @@
admin_users = [ x.strip() for x in self.get( "admin_users", "" ).split( "," ) ]
return ( user is not None and user.email in admin_users )
+ def resolve_path( self, path ):
+ """ Resolve a path relative to Galaxy's root.
+ """
+ return resolve_path( path, self.root )
+
def get_database_engine_options( kwargs, model_prefix='' ):
"""
Allow options for the SQLAlchemy database engine to be passed by using
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -162,8 +162,21 @@
# Parse destinations
destinations = root.find('destinations')
+ job_metrics = self.app.job_metrics
for destination in self.__findall_with_required(destinations, 'destination', ('id', 'runner')):
id = destination.get('id')
+ destination_metrics = destination.get( "metrics", None )
+ if destination_metrics:
+ if not util.asbool( destination_metrics ):
+ # disable
+ job_metrics.set_destination_instrumenter( id, None )
+ else:
+ metrics_conf_path = self.app.config.resolve_path( destination_metrics )
+ job_metrics.set_destination_conf_file( id, metrics_conf_path )
+ else:
+ metrics_elements = self.__findall_with_required( destination, 'job_metrics', () )
+ if metrics_elements:
+ job_metrics.set_destination_conf_element( id, metrics_elements[ 0 ] )
job_destination = JobDestination(**dict(destination.items()))
job_destination['params'] = self.__get_params(destination)
self.destinations[id] = (job_destination,)
@@ -1068,8 +1081,10 @@
# Finally set the job state. This should only happen *after* all
# dataset creation, and will allow us to eliminate force_history_refresh.
job.state = final_job_state
+ if not job.tasks:
+ # If job was composed of tasks, don't attempt to recollect statisitcs
+ self._collect_metrics( job )
self.sa_session.flush()
-
log.debug( 'job %d ended' % self.job_id )
delete_files = self.app.config.cleanup_job == 'always' or ( job.state == job.states.OK and self.app.config.cleanup_job == 'onsuccess' )
self.cleanup( delete_files=delete_files )
@@ -1094,6 +1109,16 @@
except:
log.exception( "Unable to cleanup job %d" % self.job_id )
+ def _collect_metrics( self, has_metrics ):
+ job = has_metrics.get_job()
+ per_plugin_properties = self.app.job_metrics.collect_properties( job.destination_id, self.job_id, self.working_directory )
+ if per_plugin_properties:
+ log.info( "Collecting job metrics for %s" % has_metrics )
+ for plugin, properties in per_plugin_properties.iteritems():
+ for metric_name, metric_value in properties.iteritems():
+ if metric_value is not None:
+ has_metrics.add_metric( plugin, metric_name, metric_value )
+
def get_output_sizes( self ):
sizes = []
output_paths = self.get_output_fnames()
@@ -1508,6 +1533,7 @@
task.stdout = util.shrink_string_by_size( stdout, DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True )
if len( stderr ) > DATABASE_MAX_STRING_SIZE:
log.error( "stderr for task %d is greater than %s, only a portion will be logged to database" % ( task.id, DATABASE_MAX_STRING_SIZE_PRETTY ) )
+ self._collect_metrics( task )
task.stderr = util.shrink_string_by_size( stderr, DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True )
task.exit_code = tool_exit_code
task.command_line = self.command_line
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 lib/galaxy/jobs/metrics/__init__.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/__init__.py
@@ -0,0 +1,134 @@
+import collections
+import os
+
+from xml.etree import ElementTree
+
+from galaxy.util.submodules import submodules
+from galaxy import util
+
+from ..metrics import formatting
+
+import logging
+log = logging.getLogger( __name__ )
+
+
+DEFAULT_FORMATTER = formatting.JobMetricFormatter()
+
+
+class JobMetrics( object ):
+
+ def __init__( self, conf_file=None, **kwargs ):
+ """
+ """
+ self.plugin_classes = self.__plugins_dict()
+ self.default_job_instrumenter = JobInstrumenter.from_file( self.plugin_classes, conf_file, **kwargs )
+ self.job_instrumenters = collections.defaultdict( lambda: self.default_job_instrumenter )
+
+ def format( self, plugin, key, value ):
+ if plugin in self.plugin_classes:
+ plugin_class = self.plugin_classes[ plugin ]
+ formatter = plugin_class.formatter
+ else:
+ formatter = DEFAULT_FORMATTER
+ return formatter.format( key, value )
+
+ def set_destination_conf_file( self, destination_id, conf_file ):
+ instrumenter = JobInstrumenter.from_file( self.plugin_classes, conf_file )
+ self.set_destination_instrumenter( destination_id, instrumenter )
+
+ def set_destination_conf_element( self, destination_id, element ):
+ instrumenter = JobInstrumenter( self.plugin_classes, element )
+ self.set_destination_instrumenter( destination_id, instrumenter )
+
+ def set_destination_instrumenter( self, destination_id, job_instrumenter=None ):
+ if job_instrumenter is None:
+ job_instrumenter = NULL_JOB_INSTRUMENTER
+ self.job_instrumenters[ destination_id ] = job_instrumenter
+
+ def collect_properties( self, destination_id, job_id, job_directory ):
+ return self.job_instrumenters[ destination_id ].collect_properties( job_id, job_directory )
+
+ def __plugins_dict( self ):
+ plugin_dict = {}
+ for plugin_module in self.__plugin_modules():
+ for clazz in plugin_module.__all__:
+ plugin_type = getattr( clazz, 'plugin_type', None )
+ if plugin_type:
+ plugin_dict[ plugin_type ] = clazz
+ return plugin_dict
+
+ def __plugin_modules( self ):
+ import galaxy.jobs.metrics.instrumenters
+ return submodules( galaxy.jobs.metrics.instrumenters )
+
+
+class NullJobInstrumenter( object ):
+
+ def pre_execute_commands( self, job_directory ):
+ return None
+
+ def post_execute_commands( self, job_directory ):
+ return None
+
+ def collect_properties( self, job_id, job_directory ):
+ return {}
+
+NULL_JOB_INSTRUMENTER = NullJobInstrumenter()
+
+
+class JobInstrumenter( object ):
+
+ def __init__( self, plugin_classes, metrics_element, **kwargs ):
+ self.extra_kwargs = kwargs
+ self.plugin_classes = plugin_classes
+ self.plugins = self.__plugins_for_element( metrics_element )
+
+ def pre_execute_commands( self, job_directory ):
+ commands = []
+ for plugin in self.plugins:
+ try:
+ plugin_commands = plugin.pre_execute_instrument( job_directory )
+ if plugin_commands:
+ commands.extend( util.listify( plugin_commands ) )
+ except Exception:
+ log.exception( "Failed to generate pre-execute commands for plugin %s" % plugin )
+ return "\n".join( [ c for c in commands if c ] )
+
+ def post_execute_commands( self, job_directory ):
+ commands = []
+ for plugin in self.plugins:
+ try:
+ plugin_commands = plugin.post_execute_instrument( job_directory )
+ if plugin_commands:
+ commands.extend( util.listify( plugin_commands ) )
+ except Exception:
+ log.exception( "Failed to generate post-execute commands for plugin %s" % plugin )
+ return "\n".join( [ c for c in commands if c ] )
+
+ def collect_properties( self, job_id, job_directory ):
+ per_plugin_properites = {}
+ for plugin in self.plugins:
+ try:
+ properties = plugin.job_properties( job_id, job_directory )
+ if properties:
+ per_plugin_properites[ plugin.plugin_type ] = properties
+ except Exception:
+ log.exception( "Failed to collect job properties for plugin %s" % plugin )
+ return per_plugin_properites
+
+ def __plugins_for_element( self, plugins_element ):
+ plugins = []
+ for plugin_element in plugins_element.getchildren():
+ plugin_type = plugin_element.tag
+ plugin_kwds = dict( plugin_element.items() )
+ plugin_kwds.update( self.extra_kwargs )
+ plugin = self.plugin_classes[ plugin_type ]( **plugin_kwds )
+ plugins.append( plugin )
+ return plugins
+
+ @staticmethod
+ def from_file( plugin_classes, conf_file, **kwargs ):
+ if not conf_file or not os.path.exists( conf_file ):
+ return NULL_JOB_INSTRUMENTER
+ plugins_element = ElementTree.parse( conf_file ).getroot()
+ return JobInstrumenter( plugin_classes, plugins_element, **kwargs )
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 lib/galaxy/jobs/metrics/formatting.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/formatting.py
@@ -0,0 +1,18 @@
+
+
+class JobMetricFormatter( object ):
+ """ Format job metric key-value pairs for human consumption in Web UI. """
+
+ def format( self, key, value ):
+ return ( str( key ), str( value ) )
+
+
+## Formatting utilities
+
+def seconds_to_str( value ):
+ if value < 60:
+ return "%s seconds" % value
+ elif value < 3600:
+ return "%s minutes" % ( value / 60 )
+ else:
+ return "%s days and %s minutes" % ( value / 3600, ( value % 3600 ) / 60 )
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 lib/galaxy/jobs/metrics/instrumenters/__init__.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/__init__.py
@@ -0,0 +1,53 @@
+from abc import ABCMeta
+from abc import abstractmethod
+
+import os.path
+
+from ...metrics import formatting
+
+
+INSTRUMENT_FILE_PREFIX = "__instrument"
+
+
+class InstrumentPlugin( object ):
+ """ A plugin describing how to instrument Galaxy jobs and retrieve metrics
+ from this instrumentation.
+ """
+ __metaclass__ = ABCMeta
+ formatter = formatting.JobMetricFormatter()
+
+ @property
+ @abstractmethod
+ def plugin_type( self ):
+ """ Short string providing labelling this plugin """
+
+ def pre_execute_instrument( self, job_directory ):
+ """ Optionally return one or more commands to instrument job. These
+ commands will be executed on the compute server prior to the job
+ running.
+ """
+ return None
+
+ def post_execute_instrument( self, job_directory ):
+ """ Optionally return one or more commands to instrument job. These
+ commands will be executed on the compute server after the tool defined
+ command is ran.
+ """
+ return None
+
+ @abstractmethod
+ def job_properties( self, job_id, job_directory ):
+ """ Collect properties for this plugin from specified job directory.
+ This method will run on the Galaxy server and can assume files created
+ in job_directory with pre_execute_instrument and
+ post_execute_instrument are available.
+ """
+
+ def _instrument_file_name( self, name ):
+ """ Provide a common pattern for naming files used by instrumentation
+ plugins - to ease their staging out of remote job directories.
+ """
+ return "%s_%s_%s" % ( INSTRUMENT_FILE_PREFIX, self.plugin_type, name )
+
+ def _instrument_file_path( self, job_directory, name ):
+ return os.path.join( job_directory, self._instrument_file_name( name ) )
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 lib/galaxy/jobs/metrics/instrumenters/core.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/core.py
@@ -0,0 +1,85 @@
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+import time
+
+import logging
+log = logging.getLogger( __name__ )
+
+GALAXY_SLOTS_KEY = "galaxy_slots"
+START_EPOCH_KEY = "start_epoch"
+END_EPOCH_KEY = "end_epoch"
+RUNTIME_SECONDS_KEY = "runtime_seconds"
+
+
+class CorePluginFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ value = int( value )
+ if key == GALAXY_SLOTS_KEY:
+ return ( "Cores Allocated", "%d" % value )
+ elif key == RUNTIME_SECONDS_KEY:
+ return ( "Job Runtime (Wall Clock)", formatting.seconds_to_str( value ) )
+ else:
+ # TODO: Use localized version of this from universe_wsgi.ini
+ title = "Job Start Time" if key == START_EPOCH_KEY else "Job End Time"
+ return (title, time.strftime( '%Y-%m-%d %H:%M:%S', time.localtime( value ) ) )
+
+
+class CorePlugin( InstrumentPlugin ):
+ """ Simple plugin that collects data without external dependencies. In
+ particular it currently collects value set for Galaxy slots.
+ """
+ plugin_type = "core"
+ formatter = CorePluginFormatter()
+
+ def __init__( self, **kwargs ):
+ pass
+
+ def pre_execute_instrument( self, job_directory ):
+ commands = []
+ commands.append( self.__record_galaxy_slots_command( job_directory ) )
+ commands.append( self.__record_seconds_since_epoch_to_file( job_directory, "start" ) )
+ return commands
+
+ def post_execute_instrument( self, job_directory ):
+ commands = []
+ commands.append( self.__record_seconds_since_epoch_to_file( job_directory, "end" ) )
+ return commands
+
+ def job_properties( self, job_id, job_directory ):
+ galaxy_slots_file = self.__galaxy_slots_file( job_directory )
+
+ properties = {}
+ properties[ GALAXY_SLOTS_KEY ] = self.__read_integer( galaxy_slots_file )
+ start = self.__read_seconds_since_epoch( job_directory, "start" )
+ end = self.__read_seconds_since_epoch( job_directory, "end" )
+ if start is not None and end is not None:
+ properties[ START_EPOCH_KEY ] = start
+ properties[ END_EPOCH_KEY ] = end
+ properties[ RUNTIME_SECONDS_KEY ] = end - start
+ return properties
+
+ def __record_galaxy_slots_command( self, job_directory ):
+ galaxy_slots_file = self.__galaxy_slots_file( job_directory )
+ return '''echo "$GALAXY_SLOTS" > '%s' ''' % galaxy_slots_file
+
+ def __record_seconds_since_epoch_to_file( self, job_directory, name ):
+ path = self._instrument_file_path( job_directory, "epoch_%s" % name )
+ return 'date +"%s" > ' + path
+
+ def __read_seconds_since_epoch( self, job_directory, name ):
+ path = self._instrument_file_path( job_directory, "epoch_%s" % name )
+ return self.__read_integer( path )
+
+ def __galaxy_slots_file( self, job_directory ):
+ return self._instrument_file_path( job_directory, "galaxy_slots" )
+
+ def __read_integer( self, path ):
+ value = None
+ try:
+ value = int( open( path, "r" ).read() )
+ except Exception:
+ pass
+ return value
+
+__all__ = [ CorePlugin ]
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -251,7 +251,10 @@
log.debug( 'execution of external set_meta for job %d finished' % job_wrapper.job_id )
def get_job_file(self, job_wrapper, **kwds):
+ job_metrics = job_wrapper.app.job_metrics
+ job_instrumenter = job_metrics.job_instrumenters[ job_wrapper.job_destination.id ]
options = dict(
+ job_instrumenter=job_instrumenter,
galaxy_lib=job_wrapper.galaxy_lib_dir,
env_setup_commands=job_wrapper.get_env_setup_clause(),
working_directory=os.path.abspath( job_wrapper.working_directory ),
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 lib/galaxy/jobs/runners/util/job_script/DEFAULT_JOB_FILE_TEMPLATE.sh
--- a/lib/galaxy/jobs/runners/util/job_script/DEFAULT_JOB_FILE_TEMPLATE.sh
+++ b/lib/galaxy/jobs/runners/util/job_script/DEFAULT_JOB_FILE_TEMPLATE.sh
@@ -12,6 +12,8 @@
export PYTHONPATH
fi
$env_setup_commands
+$instrument_pre_commands
cd $working_directory
$command
echo $? > $exit_code_path
+$instrument_post_commands
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 lib/galaxy/jobs/runners/util/job_script/__init__.py
--- a/lib/galaxy/jobs/runners/util/job_script/__init__.py
+++ b/lib/galaxy/jobs/runners/util/job_script/__init__.py
@@ -18,6 +18,8 @@
'headers': '',
'env_setup_commands': '',
'slots_statement': SLOTS_STATEMENT_CLUSTER_DEFAULT,
+ 'instrument_pre_commands': '',
+ 'instrument_post_commands': '',
}
@@ -47,6 +49,13 @@
"""
if any([param not in kwds for param in REQUIRED_TEMPLATE_PARAMS]):
raise Exception("Failed to create job_script, a required parameter is missing.")
+ job_instrumenter = kwds.get("job_instrumenter", None)
+ if job_instrumenter:
+ del kwds[ "job_instrumenter" ]
+ working_directory = kwds["working_directory"]
+ kwds["instrument_pre_commands"] = job_instrumenter.pre_execute_commands(working_directory) or ''
+ kwds["instrument_post_commands"] = job_instrumenter.post_execute_commands(working_directory) or ''
+
template_params = OPTIONAL_TEMPLATE_PARAMS.copy()
template_params.update(**kwds)
if not isinstance(template, Template):
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -102,6 +102,11 @@
metric = self._text_metric( plugin, metric_name, metric_value )
self.text_metrics.append( metric )
+ @property
+ def metrics( self ):
+ # TODO: Make iterable, concatenate with chain
+ return self.text_metrics + self.numeric_metrics
+
class User( object, Dictifiable ):
use_pbkdf2 = True
@@ -414,6 +419,12 @@
self.info = info
def set_runner_name( self, job_runner_name ):
self.job_runner_name = job_runner_name
+
+ def get_job( self ):
+ # Added so job and task have same interface (.get_job() ) to get at
+ # underlying job object.
+ return self
+
def set_runner_external_id( self, job_runner_external_id ):
self.job_runner_external_id = job_runner_external_id
def set_post_job_actions( self, post_job_actions ):
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 templates/show_params.mako
--- a/templates/show_params.mako
+++ b/templates/show_params.mako
@@ -126,6 +126,13 @@
%if job and job.command_line and trans.user_is_admin():
<tr><td>Job Command-Line:</td><td>${ job.command_line | h }</td></tr>
%endif
+ %if job and trans.user_is_admin():
+ <% job_metrics = trans.app.job_metrics %>
+ %for metric in job.metrics:
+ <% metric_title, metric_value = job_metrics.format( metric.plugin, metric.metric_name, metric.metric_value ) %>
+ <tr><td>${ metric_title | h }</td><td>${ metric_value | h }</td></tr>
+ %endfor
+ %endif
</table><br />
diff -r ad576bf6497ec40b64d84802bda596bb63af63af -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 test/unit/test_galaxy_mapping.py
--- a/test/unit/test_galaxy_mapping.py
+++ b/test/unit/test_galaxy_mapping.py
@@ -234,6 +234,11 @@
self.persist( u, job )
+ task = model.Task( job=job, working_directory="/tmp", prepare_files_cmd="split.sh" )
+ task.add_metric( "gx", "galaxy_slots", 5 )
+ task.add_metric( "system", "system_name", "localhost" )
+ self.persist( task )
+
def test_tasks( self ):
model = self.model
u = model.User( email="jobtest(a)foo.bar.baz", password="password" )
https://bitbucket.org/galaxy/galaxy-central/commits/2a7ff2234880/
Changeset: 2a7ff2234880
User: jmchilton
Date: 2014-03-26 17:01:46
Summary: Implement env job instrumentation plugin.
The 'env' plugin can collect all environment variables set at the time of job execution or just targetted environment variables. Environment variables like PATH or PYTHONPATH may be useful for debugging and environment variables such as HOSTNAME, PID, and DRM-specific variables like SLURM_JOB_ID could be useful for integrating Galaxy's view of jobs and users with external accounting/metric systems. See job_metrics.xml.sample for how to configure environment variable collection.
Affected #: 2 files
diff -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 -r 2a7ff223488017279b3bdaf854821a6500ff688c job_metrics_conf.xml.sample
--- a/job_metrics_conf.xml.sample
+++ b/job_metrics_conf.xml.sample
@@ -15,4 +15,10 @@
<!-- Core plugin captures Galaxy slots, start and end of job (in seconds
since epoch) and computes runtime in seconds. --><core />
+ <!-- Uncomment following to enable plugin dumping complete environment
+ for each job, potentially useful for debuging -->
+ <!-- <env /> -->
+ <!-- env plugin can also record more targetted, obviously useful variables
+ as well. -->
+ <!-- <env variables="HOSTNAME,SLURM_CPUS_ON_NODE,SLURM_JOBID" /> --></job_metrics>
diff -r 3ffd1ca6ac44736cba9e36abfaf3b1e607ddefb6 -r 2a7ff223488017279b3bdaf854821a6500ff688c lib/galaxy/jobs/metrics/instrumenters/env.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/env.py
@@ -0,0 +1,54 @@
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+
+import logging
+log = logging.getLogger( __name__ )
+
+
+class EnvFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ return ( "%s (runtime environment variable)" % key, value )
+
+
+class EnvPlugin( InstrumentPlugin ):
+ """ Instrumentation plugin capable of recording all or specific environment
+ variables for a job at runtime.
+ """
+ plugin_type = "env"
+ formatter = EnvFormatter()
+
+ def __init__( self, **kwargs ):
+ variables_str = kwargs.get( "variables", None )
+ if variables_str:
+ variables = [ v.strip() for v in variables_str.split(",") ]
+ else:
+ variables = None
+ self.variables = variables
+
+ def pre_execute_instrument( self, job_directory ):
+ """ Use env to dump all environment variables to a file.
+ """
+ return "env > '%s'" % self.__env_file( job_directory )
+
+ def post_execute_instrument( self, job_directory ):
+ return None
+
+ def job_properties( self, job_id, job_directory ):
+ """ Recover environment variables dumped out on compute server and filter
+ out specific variables if needed.
+ """
+ variables = self.variables
+
+ properties = {}
+ for line in open( self.__env_file( job_directory ) ).readlines():
+ var, value = line.split( "=", 1 )
+ if not variables or var in variables:
+ properties[ var ] = value
+
+ return properties
+
+ def __env_file( self, job_directory ):
+ return self._instrument_file_path( job_directory, "vars" )
+
+__all__ = [ EnvPlugin ]
https://bitbucket.org/galaxy/galaxy-central/commits/de4745c66e9d/
Changeset: de4745c66e9d
User: jmchilton
Date: 2014-03-26 17:01:46
Summary: Implement cpuinfo job instrumentation plugin.
Affected #: 2 files
diff -r 2a7ff223488017279b3bdaf854821a6500ff688c -r de4745c66e9d030a7df3a8eec9586fa9d65aa6c6 job_metrics_conf.xml.sample
--- a/job_metrics_conf.xml.sample
+++ b/job_metrics_conf.xml.sample
@@ -15,6 +15,13 @@
<!-- Core plugin captures Galaxy slots, start and end of job (in seconds
since epoch) and computes runtime in seconds. --><core />
+
+ <!-- Uncomment to dump processor count for each job - linux only. -->
+ <!-- <cpuinfo /> -->
+ <!-- Uncomment to dump information about all processors for for each
+ job - this is likely too much data. Linux only. -->
+ <!-- <cpuinfo verbose="true" /> -->
+
<!-- Uncomment following to enable plugin dumping complete environment
for each job, potentially useful for debuging --><!-- <env /> -->
diff -r 2a7ff223488017279b3bdaf854821a6500ff688c -r de4745c66e9d030a7df3a8eec9586fa9d65aa6c6 lib/galaxy/jobs/metrics/instrumenters/cpuinfo.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/cpuinfo.py
@@ -0,0 +1,62 @@
+import re
+
+from galaxy import util
+
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+
+import logging
+log = logging.getLogger( __name__ )
+
+PROCESSOR_LINE = re.compile(r"processor\s*\:\s*(\d+)")
+
+
+class CpuInfoFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ if key == "processor_count":
+ return "Processor Count", "%s" % int( value )
+ else:
+ return key, value
+
+
+class CpuInfoPlugin( InstrumentPlugin ):
+ """ Gather information about processor configuration from /proc/cpuinfo.
+ Linux only.
+ """
+ plugin_type = "cpuinfo"
+ formatter = CpuInfoFormatter()
+
+ def __init__( self, **kwargs ):
+ self.verbose = util.asbool( kwargs.get( "verbose", False ) )
+
+ def pre_execute_instrument( self, job_directory ):
+ return "cat /proc/cpuinfo > '%s'" % self.__instrument_cpuinfo_path( job_directory )
+
+ def job_properties( self, job_id, job_directory ):
+ properties = {}
+ processor_count = 0
+ with open( self.__instrument_cpuinfo_path( job_directory ) ) as f:
+ current_processor = None
+ for line in f:
+ line = line.strip().lower()
+ if not line: # Skip empty lines
+ continue
+
+ processor_line_match = PROCESSOR_LINE.match( line )
+ if processor_line_match:
+ processor_count += 1
+ current_processor = processor_line_match.group( 1 )
+ elif current_processor and self.verbose:
+ # If verbose, dump information about each processor
+ # into database...
+ key, value = line.split( ":", 1 )
+ key = "processor_%s_%s" % ( current_processor, key.strip() )
+ value = value
+ properties[ "processor_count" ] = processor_count
+ return properties
+
+ def __instrument_cpuinfo_path( self, job_directory ):
+ return self._instrument_file_path( job_directory, "cpuinfo" )
+
+__all__ = [ CpuInfoPlugin ]
https://bitbucket.org/galaxy/galaxy-central/commits/2d9d06f5b39e/
Changeset: 2d9d06f5b39e
User: jmchilton
Date: 2014-03-26 17:01:46
Summary: Implement meminfo job instrumentation plugin.
Affected #: 2 files
diff -r de4745c66e9d030a7df3a8eec9586fa9d65aa6c6 -r 2d9d06f5b39eb5d7d2b926dc1d74d7720cf43e2f job_metrics_conf.xml.sample
--- a/job_metrics_conf.xml.sample
+++ b/job_metrics_conf.xml.sample
@@ -22,6 +22,10 @@
job - this is likely too much data. Linux only. --><!-- <cpuinfo verbose="true" /> -->
+ <!-- Uncomment to dump system memory information for each job - linux
+ only. -->
+ <!-- <meminfo /> -->
+
<!-- Uncomment following to enable plugin dumping complete environment
for each job, potentially useful for debuging --><!-- <env /> -->
diff -r de4745c66e9d030a7df3a8eec9586fa9d65aa6c6 -r 2d9d06f5b39eb5d7d2b926dc1d74d7720cf43e2f lib/galaxy/jobs/metrics/instrumenters/meminfo.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/meminfo.py
@@ -0,0 +1,59 @@
+import re
+
+from galaxy import util
+
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+
+MEMINFO_LINE = re.compile(r"(\w+)\s*\:\s*(\d+) kB")
+
+# Important (non-verbose) meminfo property titles.
+MEMINFO_TITLES = {
+ "memtotal": "Total System Memory",
+ "swaptotal": "Total System Swap"
+}
+
+
+class MemInfoFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ title = MEMINFO_TITLES.get( key, key )
+ return title, util.nice_size( value * 1000 ) # kB = *1000, KB = *1024 - wikipedia
+
+
+class MemInfoPlugin( InstrumentPlugin ):
+ """ Gather information about processor configuration from /proc/cpuinfo.
+ Linux only.
+ """
+ plugin_type = "meminfo"
+ formatter = MemInfoFormatter()
+
+ def __init__( self, **kwargs ):
+ self.verbose = util.asbool( kwargs.get( "verbose", False ) )
+
+ def pre_execute_instrument( self, job_directory ):
+ return "cat /proc/meminfo > '%s'" % self.__instrument_meminfo_path( job_directory )
+
+ def job_properties( self, job_id, job_directory ):
+ properties = {}
+ with open( self.__instrument_meminfo_path( job_directory ) ) as f:
+ for line in f:
+ line = line.strip()
+ if not line: # Skip empty lines
+ continue
+ line_match = MEMINFO_LINE.match( line )
+ if not line_match:
+ continue
+ key = line_match.group( 1 ).lower()
+ # By default just grab important meminfo properties with titles
+ # defined for formatter. Grab everything in verbose mode for
+ # an arbitrary snapshot of memory at beginning of run.
+ if key in MEMINFO_TITLES or self.verbose:
+ value = long( line_match.group( 2 ) )
+ properties[ key ] = value
+ return properties
+
+ def __instrument_meminfo_path( self, job_directory ):
+ return self._instrument_file_path( job_directory, "meminfo" )
+
+__all__ = [ MemInfoPlugin ]
https://bitbucket.org/galaxy/galaxy-central/commits/54d890bb498d/
Changeset: 54d890bb498d
User: jmchilton
Date: 2014-03-26 17:01:46
Summary: Implement uname job instrumentation plugin.
Gathers information on operating system configuration job is run on - linux only.
Affected #: 2 files
diff -r 2d9d06f5b39eb5d7d2b926dc1d74d7720cf43e2f -r 54d890bb498dc7bb90a1a61c5b4dc636daf8385a job_metrics_conf.xml.sample
--- a/job_metrics_conf.xml.sample
+++ b/job_metrics_conf.xml.sample
@@ -26,6 +26,10 @@
only. --><!-- <meminfo /> -->
+ <!-- Uncomment to record operating system each job is executed on - linux
+ only. -->
+ <!-- <uname /> -->
+
<!-- Uncomment following to enable plugin dumping complete environment
for each job, potentially useful for debuging --><!-- <env /> -->
diff -r 2d9d06f5b39eb5d7d2b926dc1d74d7720cf43e2f -r 54d890bb498dc7bb90a1a61c5b4dc636daf8385a lib/galaxy/jobs/metrics/instrumenters/uname.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/uname.py
@@ -0,0 +1,34 @@
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+
+
+class UnameFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ return "Operating System", value
+
+
+class UnamePlugin( InstrumentPlugin ):
+ """ Use uname to gather operating system information about remote system
+ job is running on. Linux only.
+ """
+ plugin_type = "uname"
+ formatter = UnameFormatter()
+
+ def __init__( self, **kwargs ):
+ self.uname_args = kwargs.get( "args", "-a" )
+
+ def pre_execute_instrument( self, job_directory ):
+ return "uname %s > '%s'" % ( self.uname_args, self.__instrument_uname_path( job_directory ) )
+
+ def job_properties( self, job_id, job_directory ):
+ properties = {}
+ with open( self.__instrument_uname_path( job_directory ) ) as f:
+ properties[ "uname" ] = f.read()
+ return properties
+
+ def __instrument_uname_path( self, job_directory ):
+ return self._instrument_file_path( job_directory, "uname" )
+
+
+__all__ = [ UnamePlugin ]
https://bitbucket.org/galaxy/galaxy-central/commits/8cd359ea420e/
Changeset: 8cd359ea420e
User: jmchilton
Date: 2014-03-26 17:01:46
Summary: Implement collectl job instrumentation plugin.
This changeset documentation is an abridged form fo the documentation included in job_metrics_conf.xml.sample.
Collectl (http://collectl.sourceforge.net/) is a powerful monitoring utility capable of gathering numerous system and process level statistics of running applications. The Galaxy collectl job metrics plugin by default will grab a variety of process level metrics aggregated across all processes corresponding to a job, this behavior is highly customiziable - both using the attributes documented below or simply hacking up the code in lib/galaxy/jobs/metrics.
Warning: In order to use this plugin collectl must be available on the compute server the job runs on and on the local Galaxy server as well (unless in this latter case summarize_process_data is set to False).
Attributes (the follow describes attributes that can be used with the collectl job metrics element above to modify its behavior).
'summarize_process_data': Boolean indicating whether to run collectl
in playback mode after jobs complete and gather process level
statistics for the job run. These statistics can be customized
with the 'process_statistics' attribute. (defaults to True)
'saved_logs_path': If set (it is off by default), all collectl logs
will be saved to the specified path after jobs complete. These
logs can later be replayed using collectl offline to generate
full time-series data corresponding to a job run.
'subsystems': Comma separated list of collectl subystems to collect
data for. Plugin doesn't currently expose all of them or offer
summary data for any of them except 'process' but extensions
would be welcome. May seem pointless to include subsystems beside
process since they won't be processed online by Galaxy - but if
'saved_logs_path' these files can be played back at anytime.
Available subsystems - 'process', 'cpu', 'memory', 'network',
'disk', 'network'. (Default 'process').
'process_statistics': If 'summarize_process_data' this attribute can
be specified as a comma separated list to override the statistics
that are gathered. Each statistics is of the for X_Y where X if
one of 'min', 'max', 'count', 'avg', or 'sum' and Y is a value
from 'S', 'VmSize', 'VmLck', 'VmRSS', 'VmData', 'VmStk', 'VmExe',
'VmLib', 'CPU', 'SysT', 'UsrT', 'PCT', 'AccumT' 'WKB', 'RKBC',
'WKBC', 'RSYS', 'WSYS', 'CNCL', 'MajF', 'MinF'. Consult
lib/galaxy/jobs/metrics/collectl/processes.py for more details on
what each of these resource types means.
Defaults to 'max_VmSize,avg_VmSize,max_VmRSS,avg_VmRSS,sum_SysT,sum_UsrT,max_PCT avg_PCT,max_AccumT,sum_RSYS,sum_WSYS'
as variety of statistics roughly describing CPU and memory usage of
the program and VERY ROUGHLY describing I/O consumption.
Affected #: 7 files
diff -r 54d890bb498dc7bb90a1a61c5b4dc636daf8385a -r 8cd359ea420e3b0c3cbadcbc6fdf99ef614ad28a job_metrics_conf.xml.sample
--- a/job_metrics_conf.xml.sample
+++ b/job_metrics_conf.xml.sample
@@ -36,4 +36,89 @@
<!-- env plugin can also record more targetted, obviously useful variables
as well. --><!-- <env variables="HOSTNAME,SLURM_CPUS_ON_NODE,SLURM_JOBID" /> -->
+
+ <!-- <collectl /> -->
+ <!-- Collectl (http://collectl.sourceforge.net/) is a powerful monitoring
+ utility capable of gathering numerous system and process level
+ statistics of running applications. The Galaxy collectl job metrics
+ plugin by default will grab a variety of process level metrics
+ aggregated across all processes corresponding to a job, this behavior
+ is highly customiziable - both using the attributes documented below
+ or simply hacking up the code in lib/galaxy/jobs/metrics.
+
+ Warning: In order to use this plugin collectl must be available on the
+ compute server the job runs on and on the local Galaxy server as well
+ (unless in this latter case summarize_process_data is set to False).
+
+ Attributes (the follow describes attributes that can be used with
+ the collectl job metrics element above to modify its behavior).
+
+ 'summarize_process_data': Boolean indicating whether to run collectl
+ in playback mode after jobs complete and gather process level
+ statistics for the job run. These statistics can be customized
+ with the 'process_statistics' attribute. (defaults to True)
+
+ 'saved_logs_path': If set (it is off by default), all collectl logs
+ will be saved to the specified path after jobs complete. These
+ logs can later be replayed using collectl offline to generate
+ full time-series data corresponding to a job run.
+
+ 'subsystems': Comma separated list of collectl subystems to collect
+ data for. Plugin doesn't currently expose all of them or offer
+ summary data for any of them except 'process' but extensions
+ would be welcome. May seem pointless to include subsystems
+ beside process since they won't be processed online by Galaxy -
+ but if 'saved_logs_path' these files can be played back at anytime.
+
+ Available subsystems - 'process', 'cpu', 'memory', 'network',
+ 'disk', 'network'. (Default 'process').
+
+ Warning: If you override this - be sure to include 'process'
+ unless 'summarize_process_data' is set to false.
+
+ 'process_statistics': If 'summarize_process_data' this attribute can be
+ specified as a comma separated list to override the statistics
+ that are gathered. Each statistics is of the for X_Y where X
+ if one of 'min', 'max', 'count', 'avg', or 'sum' and Y is a
+ value from 'S', 'VmSize', 'VmLck', 'VmRSS', 'VmData', 'VmStk',
+ 'VmExe', 'VmLib', 'CPU', 'SysT', 'UsrT', 'PCT', 'AccumT' 'WKB',
+ 'RKBC', 'WKBC', 'RSYS', 'WSYS', 'CNCL', 'MajF', 'MinF'. Consult
+ lib/galaxy/jobs/metrics/collectl/processes.py for more details
+ on what each of these resource types means.
+
+ Defaults to 'max_VmSize,avg_VmSize,max_VmRSS,avg_VmRSS,sum_SysT,sum_UsrT,max_PCT avg_PCT,max_AccumT,sum_RSYS,sum_WSYS'
+ as variety of statistics roughly describing CPU and memory
+ usage of the program and VERY ROUGHLY describing I/O consumption.
+
+ 'procfilt_on': By default Galaxy will tell collectl to only collect
+ 'process' level data for the current user (as identified)
+ by 'username' (default) - this can be disabled by settting this
+ to 'none' - the plugin will still only aggregate process level
+ statistics for the jobs process tree - but the additional
+ information can still be used offline with 'saved_logs_path'
+ if set. Obsecurely, this can also be set 'uid' to identify
+ the current user to filter on by UID instead of username -
+ this may needed on some clusters(?).
+
+ 'interval': The time (in seconds) between data collection points.
+ Collectl uses a variety of different defaults for different
+ subsystems if this is not set, but process information (likely
+ the most pertinent for Galaxy jobs will collect data every
+ 60 seconds).
+
+ 'flush': Interval (in seconds I think) between when collectl will
+ flush its buffer to disk. Galaxy overrides this to disable
+ flushing by default if not set.
+
+ 'local_collectl_path', 'remote_collectl_path', 'collectl_path':
+ By default, jobs will just assume collectl is on the PATH, but
+ it can be overridden with 'local_collectl_path' and
+ 'remote_collectl_path' (or simply 'collectl_path' if it is not
+ on the path but installed in the same location both locally and
+ remotely).
+
+ There are more and more increasingly obsecure options including -
+ log_collectl_program_output, interval2, and interval3. Consult
+ source code for more details.
+ --></job_metrics>
diff -r 54d890bb498dc7bb90a1a61c5b4dc636daf8385a -r 8cd359ea420e3b0c3cbadcbc6fdf99ef614ad28a lib/galaxy/jobs/metrics/collectl/__init__.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/collectl/__init__.py
@@ -0,0 +1,5 @@
+""" This module contains helper functions and data structures for interacting
+with collectl and collectl generated data. More information on collectl can be
+found at: http://collectl.sourceforge.net/.
+
+"""
diff -r 54d890bb498dc7bb90a1a61c5b4dc636daf8385a -r 8cd359ea420e3b0c3cbadcbc6fdf99ef614ad28a lib/galaxy/jobs/metrics/collectl/cli.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/collectl/cli.py
@@ -0,0 +1,128 @@
+from string import Template
+import subprocess
+
+import logging
+log = logging.getLogger( __name__ )
+
+COMMAND_LINE_TEMPLATE = Template(
+ "$collectl_path $destination_arg $mode_arg $subsystems_arg $interval_arg $procfilt_arg $flush_arg $sep_arg"
+)
+MODE_RECORD = "record"
+MODE_PLAYBACK = "playback"
+
+
+class CollectlCli( object ):
+ """ Abstraction over (some of) the command-line arguments of collectl.
+ Ideally this will be useful for building up command line arguments for
+ remote execution as well as runnning directly on local host.
+
+ This is meant to be a fairly generic utility - for interfacing with
+ collectl CLI - logic more directly related to the Galaxy job metric plugin
+ plugin should be placed in other modules.
+
+ Keyword Arguments:
+ collectl_path: Path to collectl executable (defaults to collectl - i.e.
+ search the PATH).
+
+ playback_path (defaults to None): If this is None collectl will run in
+ record mode, else it will playback specified file.
+
+ Playback Mode Options:
+
+ sep : Separator used in playback mode (set to 9 to produce tsv)
+ (defaults to None).
+
+ Record Mode Options (some of these may work in playback mode also):
+
+ destination_path: Location of path files to write to (defaults to None
+ and collectl will just use cwd). Really this is just to prefix -
+ collectl will append hostname and datetime to file.
+ interval: Setup polling interval (secs) for most subsystems (defaults
+ to None and when unspecified collectl will use default of 1 second).
+ interval2: Setup polling interval (secs) for process information
+ (defaults to None and when unspecified collectl will use default to
+ 60 seconds).
+ interval3: Setup polling interval (secs) for environment information
+ (defaults to None and when unspecified collectl will use default to
+ 300 seconds).
+ procfilt: Optional argument to procfilt. (defaults to None).
+ flush : Optional flush interval (defaults to None).
+ """
+
+ def __init__( self, **kwargs ):
+ command_args = {}
+ command_args[ "collectl_path" ] = kwargs.get( "collectl_path", "collectl" )
+ playback_path = kwargs.get( "playback_path", None )
+ self.mode = MODE_RECORD if not playback_path else MODE_PLAYBACK
+ if self.mode == MODE_RECORD:
+ mode_arg = ""
+ elif self.mode == MODE_PLAYBACK:
+ mode_arg = "-P -p '%s'" % playback_path
+ else:
+ raise Exception( "Invalid mode supplied to CollectlCli - %s" % self.mode )
+ command_args[ "mode_arg" ] = mode_arg
+ command_args[ "interval_arg" ] = self.__interval_arg( kwargs )
+ destination = kwargs.get( "destination_path", None )
+ if destination:
+ destination_arg = "-f '%s'" % destination
+ else:
+ destination_arg = ""
+ command_args[ "destination_arg" ] = destination_arg
+ procfilt = kwargs.get( "procfilt", None )
+ command_args[ "procfilt_arg" ] = "" if not procfilt else "--procfilt %s" % procfilt
+ command_args[ "subsystems_arg" ] = self.__subsystems_arg( kwargs.get( "subsystems", [] ) )
+ flush = kwargs.get( "flush", None )
+ command_args[ "flush_arg"] = "--flush %s" % flush if flush else ""
+ sep = kwargs.get( "sep", None )
+ command_args[ "sep_arg" ] = "--sep=%s" % sep if sep else ""
+
+ self.command_args = command_args
+
+ def __subsystems_arg( self, subsystems ):
+ if subsystems:
+ return "-s%s" % "".join( [ s.command_line_arg for s in subsystems ] )
+ else:
+ return ""
+
+ def __interval_arg( self, kwargs ):
+ if self.mode != MODE_RECORD:
+ return ""
+
+ interval = kwargs.get( "interval", None )
+ if not interval:
+ return ""
+
+ self.__validate_interval_arg( interval )
+ interval_arg = "-i %s" % interval
+ interval2 = kwargs.get( "interval2", None )
+ if not interval2:
+ return interval_arg
+ self.__validate_interval_arg( interval2, multiple_of=int( interval ) )
+ interval_arg = "%s:%s" % ( interval_arg, interval2 )
+
+ interval3 = kwargs.get( "interval3", None )
+ if not interval3:
+ return interval_arg
+ self.__validate_interval_arg( interval3, multiple_of=int( interval ) )
+ interval_arg = "%s:%s" % ( interval_arg, interval3 )
+ return interval_arg
+
+ def __validate_interval_arg( self, value, multiple_of=None ):
+ if value and not str(value).isdigit():
+ raise Exception( "Invalid interval argument supplied, must be integer %s" % value )
+ if multiple_of:
+ if int( value ) % multiple_of != 0:
+ raise Exception( "Invalid interval argument supplied, must multiple of %s" % multiple_of )
+
+ def build_command_line( self ):
+ return COMMAND_LINE_TEMPLATE.substitute( **self.command_args )
+
+ def run( self, stdout=subprocess.PIPE, stderr=subprocess.PIPE ):
+ command_line = self.build_command_line()
+ log.info( "Executing %s" % command_line )
+ proc = subprocess.Popen( command_line, shell=True, stdout=stdout, stderr=stderr )
+ return_code = proc.wait()
+ if return_code:
+ raise Exception( "Problem running collectl command." )
+
+__all__ = [ CollectlCli ]
diff -r 54d890bb498dc7bb90a1a61c5b4dc636daf8385a -r 8cd359ea420e3b0c3cbadcbc6fdf99ef614ad28a lib/galaxy/jobs/metrics/collectl/processes.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/collectl/processes.py
@@ -0,0 +1,252 @@
+""" Modules will run collectl in playback mode and collect various process
+statistics for a given pid's process and process ancestors.
+"""
+import collections
+import csv
+import tempfile
+
+from galaxy import util
+
+from ..collectl import stats
+
+import logging
+log = logging.getLogger( __name__ )
+
+# Collectl process information cheat sheet:
+#
+# Record process information for current user.
+# % collectl -sZ -f./__instrument_collectl -i 10:10 --procfilt U$USER
+#
+# TSV Replay of processing information in plottable mode...
+#
+# % collectl -sZ -P --sep=9 -p __instrument_collectl-jlaptop13-20140322-120919.raw.gz
+#
+# Has following columns:
+# Date Time PID User PR PPID THRD S VmSize VmLck VmRSS VmData VmStk VmExe VmLib CPU SysT UsrT PCT AccumT RKB WKB RKBC WKBC RSYS WSYS CNCL MajF MinF Command
+#
+
+# Process data dumped one row per process per interval.
+#http://collectl.sourceforge.net/Data-detail.html
+PROCESS_COLUMNS = [
+ "#Date", # Date of interval - e.g. 20140322
+ "Time", # Time of interval - 12:18:58
+ "PID", # Process pid.
+ "User", # Process user.
+ "PR", # Priority of process.
+ "PPID", # Parent PID of process.
+ "THRD", # Thread???
+ "S", # Process state - S - Sleeping, D - Uninterruptable Sleep, R - Running, Z - Zombie or T - Stopped/Traced
+ ## Memory options - http://ewx.livejournal.com/579283.html
+ "VmSize",
+ "VmLck",
+ "VmRSS",
+ "VmData",
+ "VmStk",
+ "VmExe",
+ "VmLib",
+ "CPU", # CPU number of process
+ "SysT", # Amount of system time consumed during interval
+ "UsrT", # Amount user time consumed during interval
+ "PCT", # Percentage of current interval consumed by task
+ "AccumT", # Total accumulated System and User time since the process began execution
+ # kilobytes read/written - requires I/O level monitoring to be enabled in kernel.
+ "RKB", # kilobytes read by process - requires I/O monitoring in kernel
+ "WKB",
+ "RKBC",
+ "WKBC",
+ "RSYS", # Number of read system calls
+ "WSYS", # Number of write system calls
+ "CNCL",
+ "MajF", # Number of major page faults
+ "MinF", # Number of minor page faults
+ "Command", # Command executed
+]
+
+# Types of statistics this module can summarize
+STATISTIC_TYPES = [ "max", "min", "sum", "count", "avg" ]
+
+COLUMN_INDICES = dict( [ ( col, i ) for i, col in enumerate( PROCESS_COLUMNS ) ] )
+PID_INDEX = COLUMN_INDICES[ "PID" ]
+PARENT_PID_INDEX = COLUMN_INDICES[ "PPID" ]
+
+DEFAULT_STATISTICS = [
+ ("max", "VmSize"),
+ ("avg", "VmSize"),
+ ("max", "VmRSS"),
+ ("avg", "VmRSS"),
+ ("sum", "SysT"),
+ ("sum", "UsrT"),
+ ("max", "PCT"),
+ ("avg", "PCT"),
+ ("max", "AccumT"),
+ ("sum", "RSYS"),
+ ("sum", "WSYS"),
+]
+
+
+def parse_process_statistics( statistics ):
+ """ Turn string or list of strings into list of tuples in format ( stat,
+ resource ) where stat is a value from STATISTIC_TYPES and resource is a
+ value from PROCESS_COLUMNS.
+ """
+ if statistics is None:
+ statistics = DEFAULT_STATISTICS
+
+ statistics = util.listify( statistics )
+ statistics = map( __tuplize_statistic, statistics )
+ # Check for validity...
+ for statistic in statistics:
+ if statistic[ 0 ] not in STATISTIC_TYPES:
+ raise Exception( "Unknown statistic type encountered %s" % statistic[ 0 ] )
+ if statistic[ 1 ] not in PROCESS_COLUMNS:
+ raise Exception( "Unknown process column encountered %s" % statistic[ 1 ] )
+ return statistics
+
+
+def generate_process_statistics( collectl_playback_cli, pid, statistics=DEFAULT_STATISTICS ):
+ """ Playback collectl file and generate summary statistics.
+ """
+ with tempfile.NamedTemporaryFile( ) as tmp_tsv:
+ collectl_playback_cli.run( stdout=tmp_tsv )
+ with open( tmp_tsv.name, "r" ) as tsv_file:
+ return __read_process_statistics( tsv_file, pid, statistics )
+
+
+def __read_process_statistics( tsv_file, pid, statistics ):
+ process_summarizer = CollectlProcessSummarizer( pid, statistics )
+ current_interval = None
+
+ for row in csv.reader( tsv_file, dialect="excel-tab" ):
+ if current_interval is None:
+ for header, expected_header in zip( row, PROCESS_COLUMNS ):
+ if header.lower() != expected_header.lower():
+ raise Exception( "Unknown header value encountered while processing collectl playback - %s" % header )
+
+ # First row, check contains correct header.
+ current_interval = CollectlProcessInterval()
+ continue
+
+ if current_interval.row_is_in( row ):
+ current_interval.add_row( row )
+ else:
+ process_summarizer.handle_interval( current_interval )
+ current_interval = CollectlProcessInterval()
+
+ # Do we have unsummarized rows...
+ if current_interval and current_interval.rows:
+ process_summarizer.handle_interval( current_interval )
+
+ return process_summarizer.get_statistics()
+
+
+class CollectlProcessSummarizer( object ):
+
+ def __init__( self, pid, statistics ):
+ self.pid = pid
+ self.statistics = statistics
+ self.columns_of_interest = set( [ s[ 1 ] for s in statistics ] )
+ self.tree_statistics = collections.defaultdict( stats.StatisticsTracker )
+ self.process_accum_statistics = collections.defaultdict( stats.StatisticsTracker )
+ self.interval_count = 0
+
+ def handle_interval( self, interval ):
+ self.interval_count += 1
+ rows = self.__rows_for_process( interval.rows, self.pid )
+ for column_name in self.columns_of_interest:
+ column_index = COLUMN_INDICES[ column_name ]
+
+ if column_name == "AccumT":
+ # Should not sum this across pids each interval, sum max at end...
+ for r in rows:
+ pid_seconds = self.__time_to_seconds( r[ column_index ] )
+ self.process_accum_statistics[ r[ PID_INDEX ] ].track( pid_seconds )
+ else:
+ # All other stastics should be summed across whole process tree
+ # at each interval I guess.
+ if column_name in [ "SysT", "UsrT", "PCT" ]:
+ to_num = float
+ else:
+ to_num = long
+
+ interval_stat = sum( to_num( r[ column_index ] ) for r in rows )
+ self.tree_statistics[ column_name ].track( interval_stat )
+
+ def get_statistics( self ):
+ if self.interval_count == 0:
+ return []
+
+ computed_statistics = []
+ for statistic in self.statistics:
+ statistic_type, column = statistic
+ if column == "AccumT":
+ # Only thing that makes sense is sum
+ if statistic_type != "max":
+ log.warn( "Only statistic max makes sense for AccumT" )
+ continue
+
+ value = sum( [ v.max for v in self.process_accum_statistics.itervalues() ] )
+ else:
+ statistics_tracker = self.tree_statistics[ column ]
+ value = getattr( statistics_tracker, statistic_type )
+
+ computed_statistic = ( statistic, value )
+ computed_statistics.append( computed_statistic )
+
+ return computed_statistics
+
+ def __rows_for_process( self, rows, pid ):
+ process_rows = []
+ pids = self.__all_child_pids( rows, pid )
+ for row in rows:
+ if row[ PID_INDEX ] in pids:
+ process_rows.append( row )
+ return process_rows
+
+ def __all_child_pids( self, rows, pid ):
+ pids_in_process_tree = set( [ str( self.pid ) ] )
+ added = True
+ while added:
+ added = False
+ for row in rows:
+ pid = row[ PID_INDEX ]
+ parent_pid = row[ PARENT_PID_INDEX ]
+ if parent_pid in pids_in_process_tree and pid not in pids_in_process_tree:
+ pids_in_process_tree.add( pid )
+ added = True
+ return pids_in_process_tree
+
+ def __time_to_seconds( self, minutes_str ):
+ parts = minutes_str.split( ":" )
+ seconds = 0.0
+ for i, val in enumerate( parts ):
+ seconds += float(val) * ( 60 ** ( len( parts ) - ( i + 1 ) ) )
+ return seconds
+
+
+class CollectlProcessInterval( object ):
+ """ Represent all rows in collectl playback file for given time slice with
+ ability to filter out just rows corresponding to the process tree
+ corresponding to a given pid.
+ """
+
+ def __init__( self ):
+ self.rows = []
+
+ def row_is_in( self, row ):
+ if not self.rows: # No rows, this row defines interval.
+ return True
+ first_row = self.rows[ 0 ]
+ return first_row[ 0 ] == row[ 0 ] and first_row[ 1 ] == row[ 1 ]
+
+ def add_row( self, row ):
+ self.rows.append( row )
+
+
+def __tuplize_statistic( statistic ):
+ if not isinstance( statistic, tuple ):
+ statistic_split = statistic.split( "_", 1 )
+ statistic = ( statistic_split[ 0 ].lower(), statistic_split[ 1 ] )
+ return statistic
+
+
+__all__ = [ generate_process_statistics ]
diff -r 54d890bb498dc7bb90a1a61c5b4dc636daf8385a -r 8cd359ea420e3b0c3cbadcbc6fdf99ef614ad28a lib/galaxy/jobs/metrics/collectl/stats.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/collectl/stats.py
@@ -0,0 +1,27 @@
+""" Primitive module for tracking running statistics without storing values in
+memory.
+"""
+
+
+class StatisticsTracker( object ):
+
+ def __init__( self ):
+ self.min = None
+ self.max = None
+ self.count = 0
+ self.sum = 0
+
+ def track( self, value ):
+ if self.min is None or value < self.min:
+ self.min = value
+ if self.max is None or value > self.max:
+ self.max = value
+ self.count += 1
+ self.sum += value
+
+ @property
+ def avg( self ):
+ if self.count > 0:
+ return self.sum / self.count
+ else:
+ return None
diff -r 54d890bb498dc7bb90a1a61c5b4dc636daf8385a -r 8cd359ea420e3b0c3cbadcbc6fdf99ef614ad28a lib/galaxy/jobs/metrics/collectl/subsystems.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/collectl/subsystems.py
@@ -0,0 +1,72 @@
+from abc import ABCMeta
+from abc import abstractmethod
+
+
+class CollectlSubsystem( object ):
+ """ Class providing an abstraction of collectl subsytems.
+ """
+ __metaclass__ = ABCMeta
+
+ @property
+ @abstractmethod
+ def command_line_arg( self ):
+ """ Return single letter command-line argument used by collectl CLI.
+ """
+
+ @property
+ @abstractmethod
+ def name( self, job_directory ):
+ """ High-level name for subsystem as consumed by this module.
+ """
+
+
+class ProcessesSubsystem( CollectlSubsystem ):
+ command_line_arg = "Z"
+ name = "process"
+
+
+class CpuSubsystem( CollectlSubsystem ):
+ command_line_arg = "C"
+ name = "cpu"
+
+
+class DiskSubsystem( CollectlSubsystem ):
+ command_line_arg = "D"
+ name = "disk"
+
+
+class NetworkSubsystem( CollectlSubsystem ):
+ command_line_arg = "N"
+ name = "network"
+
+
+class EnvironmentSubsystem( CollectlSubsystem ):
+ command_line_arg = "E"
+ name = "environment"
+
+
+class MemorySubsystem( CollectlSubsystem ):
+ command_line_arg = "M"
+ name = "memory"
+
+
+SUBSYSTEMS = [
+ ProcessesSubsystem(),
+ CpuSubsystem(),
+ DiskSubsystem(),
+ NetworkSubsystem(),
+ EnvironmentSubsystem(),
+ MemorySubsystem(),
+]
+SUBSYSTEM_DICT = dict( [ (s.name, s) for s in SUBSYSTEMS ] )
+
+
+def get_subsystem( name ):
+ """
+
+ >>> get_subsystem( "process" ).command_line_arg == "Z"
+ True
+ """
+ return SUBSYSTEM_DICT[ name ]
+
+__all__ = [ get_subsystem ]
diff -r 54d890bb498dc7bb90a1a61c5b4dc636daf8385a -r 8cd359ea420e3b0c3cbadcbc6fdf99ef614ad28a lib/galaxy/jobs/metrics/instrumenters/collectl.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/collectl.py
@@ -0,0 +1,214 @@
+import os
+import shutil
+
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+from ..collectl import subsystems
+from ..collectl import cli
+from ..collectl import processes
+
+from galaxy import util
+from galaxy.util import directory_hash
+
+import logging
+log = logging.getLogger( __name__ )
+
+DEFAULT_PROCFILT_ON = "username" # By default, only grab statistics for user
+ # processes (as identifiers by username).
+DEFAULT_SUBSYSTEMS = "process"
+DEFAULT_FLUSH_INTERVAL = "0" # Set to zero to flush every collection.
+
+FORMATTED_RESOURCE_TITLES = {
+ "PCT": "Percent CPU Usage",
+ "RSYS": "Disk Reads",
+ "WSYS": "Disk Writes",
+}
+
+EMPTY_COLLECTL_FILE_MESSAGE = "Skipping process summary due to empty file... job probably did not run long enough for collectl to gather data."
+
+
+class CollectlFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ if key == "pid":
+ return ( "Process ID", int( value ) )
+ elif key == "raw_log_path":
+ return ( "Relative Path of Full Collectl Log", value )
+ elif key == "process_max_AccumT":
+ return ( "Job Runtime (System+User)", formatting.seconds_to_str( float( value ) ) )
+ else:
+ _, stat_type, resource_type = key.split( "_", 2 )
+ if resource_type.startswith( "Vm"):
+ value_str = "%s KB" % int( value )
+ elif resource_type in [ "RSYS", "WSYS" ] and stat_type in [ "count", "max", "sum" ]:
+ value_str = "%d (# system calls)" % int( value )
+ else:
+ value_str = str( value )
+ resource_title = FORMATTED_RESOURCE_TITLES.get( resource_type, resource_type )
+ return ( "%s (%s)" % ( resource_title, stat_type ), value_str )
+
+
+class CollectlPlugin( InstrumentPlugin ):
+ """ Run collectl along with job to capture system and/or process data
+ according to specified collectl subsystems.
+ """
+ plugin_type = "collectl"
+ formatter = CollectlFormatter()
+
+ def __init__( self, **kwargs ):
+ self.__configure_paths( kwargs )
+ self.__configure_subsystems( kwargs )
+ saved_logs_path = kwargs.get( "saved_logs_path", None )
+ if "app" in kwargs:
+ saved_logs_path = kwargs[ "app" ].config.resolve_path( saved_logs_path )
+ self.saved_logs_path = saved_logs_path
+ self.__configure_collectl_recorder_args( kwargs )
+ self.summarize_process_data = util.asbool( kwargs.get( "summarize_process_data", True ) )
+ self.log_collectl_program_output = util.asbool( kwargs.get( "log_collectl_program_output", False ) )
+ if self.summarize_process_data:
+ if subsystems.get_subsystem( "process" ) not in self.subsystems:
+ raise Exception( "Collectl plugin misconfigured - cannot summarize_process_data without process subsystem being enabled." )
+
+ process_statistics = kwargs.get( "process_statistics", None )
+ # None will let processes module use default set of statistics
+ # defined there.
+ self.process_statistics = processes.parse_process_statistics( process_statistics )
+
+ def pre_execute_instrument( self, job_directory ):
+ commands = []
+ # Capture PID of process so we can walk its ancestors when building
+ # statistics for the whole job.
+ commands.append( '''echo "$$" > '%s' ''' % self.__pid_file( job_directory ) )
+ # Run collectl in record mode to capture process and system level
+ # statistics according to supplied subsystems.
+ commands.append( self.__collectl_record_command( job_directory ) )
+ return commands
+
+ def post_execute_instrument( self, job_directory ):
+ commands = []
+ # collectl dies when job script completes, perhaps capture pid of
+ # collectl above and check if it is still alive to allow tracking if
+ # collectl ran successfully through the whole job.
+ return commands
+
+ def job_properties( self, job_id, job_directory ):
+ pid = open( self.__pid_file( job_directory ), "r" ).read().strip()
+ contents = os.listdir( job_directory )
+ try:
+ rel_path = filter( self._is_instrumented_collectl_log, contents )[ 0 ]
+ path = os.path.join( job_directory, rel_path )
+ except IndexError:
+ message = "Failed to find collectl log in directory %s, files were %s" % ( job_directory, contents )
+ raise Exception( message )
+
+ properties = dict(
+ pid=int( pid ),
+ )
+
+ if self.saved_logs_path:
+ destination_rel_dir = os.path.join( *directory_hash.directory_hash_id( job_id ) )
+ destination_rel_path = os.path.join( destination_rel_dir, rel_path )
+ destination_path = os.path.join( self.saved_logs_path, destination_rel_path )
+ destination_dir = os.path.dirname( destination_path )
+ if not os.path.isdir( destination_dir ):
+ os.makedirs( destination_dir )
+ shutil.copyfile( path, destination_path )
+ properties[ "raw_log_path" ] = destination_rel_path
+
+ if self.summarize_process_data:
+ # Run collectl in playback and generate statistics of interest
+ summary_statistics = self.__summarize_process_data( pid, path )
+ for statistic, value in summary_statistics:
+ properties[ "process_%s" % "_".join( statistic ) ] = value
+
+ return properties
+
+ def __configure_paths( self, kwargs ):
+ # 95% of time I would expect collectl to just be installed with apt or
+ # yum, but if it is manually installed on not on path, allow
+ # configuration of explicit path - and allow path to be different
+ # between galaxy job handler (local_collectl_path) and compute node
+ # (remote_collectl_path).
+ collectl_path = kwargs.get( "collectl_path", "collectl" )
+ self.remote_collectl_path = kwargs.get( "remote_collectl_path", collectl_path )
+ self.local_collectl_path = kwargs.get( "local_collectl_path", collectl_path )
+
+ def __configure_subsystems( self, kwargs ):
+ raw_subsystems_str = kwargs.get( "subsystems", DEFAULT_SUBSYSTEMS )
+ raw_subsystems = util.listify( raw_subsystems_str, do_strip=True )
+ self.subsystems = map( subsystems.get_subsystem, raw_subsystems )
+
+ def __configure_collectl_recorder_args( self, kwargs ):
+ collectl_recorder_args = kwargs.copy()
+
+ # Allow deployer to configure separate system and process intervals,
+ # but if they specify just one - use it for both. Thinking here is this
+ # plugin's most useful feature is the process level information so
+ # this is likely what the deployer is attempting to configure.
+ if "interval" in kwargs and "interval2" not in kwargs:
+ collectl_recorder_args[ "interval2" ] = kwargs[ "interval"]
+
+ if "flush" not in kwargs:
+ collectl_recorder_args[ "flush" ] = DEFAULT_FLUSH_INTERVAL
+
+ procfilt_on = kwargs.get( "procfilt_on", DEFAULT_PROCFILT_ON ).lower()
+ # Calculate explicit arguments, rest can just be passed through from
+ # constructor arguments.
+ explicit_args = dict(
+ collectl_path=self.remote_collectl_path,
+ procfilt=procfilt_argument( procfilt_on ),
+ subsystems=self.subsystems,
+ )
+ collectl_recorder_args.update( explicit_args )
+ self.collectl_recorder_args = collectl_recorder_args
+
+ def __summarize_process_data( self, pid, collectl_log_path ):
+ playback_cli_args = dict(
+ collectl_path=self.local_collectl_path,
+ playback_path=collectl_log_path,
+ sep="9"
+ )
+ if not os.stat( collectl_log_path ).st_size:
+ log.debug( EMPTY_COLLECTL_FILE_MESSAGE )
+ return [ ]
+
+ playback_cli = cli.CollectlCli( **playback_cli_args )
+ return processes.generate_process_statistics( playback_cli, pid, self.process_statistics )
+
+ def __collectl_recorder_cli( self, job_directory ):
+ cli_args = self.collectl_recorder_args.copy()
+ cli_args[ "destination_path" ] = self._instrument_file_path( job_directory, "log" )
+ return cli.CollectlCli( **cli_args )
+
+ def __collectl_record_command( self, job_directory ):
+ collectl_cli = self.__collectl_recorder_cli( job_directory )
+ if self.log_collectl_program_output:
+ redirect_to = self._instrument_file_path( job_directory, "program_output" )
+ else:
+ redirect_to = "/dev/null"
+ return "%s > %s 2>&1 &" % (
+ collectl_cli.build_command_line(),
+ redirect_to,
+ )
+
+ def __pid_file( self, job_directory ):
+ return self._instrument_file_path( job_directory, "pid" )
+
+ def _is_instrumented_collectl_log( self, filename ):
+ prefix = self._instrument_file_name( "log" )
+ return filename.startswith( prefix ) and filename.endswith( ".raw.gz" )
+
+
+def procfilt_argument( procfilt_on ):
+ if procfilt_on == "username":
+ return "U$USER"
+ elif procfilt_on == "uid":
+ return "u$UID"
+ else:
+ # Ensure it is empty of None
+ if procfilt_on or procfilt_on.lower() != "none":
+ raise Exception( "Invalid procfilt_on argument encountered")
+ return ""
+
+
+__all__ = [ CollectlPlugin ]
https://bitbucket.org/galaxy/galaxy-central/commits/b24137a3a980/
Changeset: b24137a3a980
User: jmchilton
Date: 2014-04-23 04:06:03
Summary: Merge pull request #352.
Affected #: 27 files
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 job_conf.xml.sample_advanced
--- a/job_conf.xml.sample_advanced
+++ b/job_conf.xml.sample_advanced
@@ -62,6 +62,11 @@
<!-- Warning: Local slot count doesn't tie up additional worker threads, to prevent over
allocating machine define a second local runner with different name and fewer workers
to run this destination. -->
+ <job_metrics />
+ <!-- Above element demonstrates embedded job metrics definition - see
+ job_metrics_conf.xml.sample for full documentation on possible nested
+ elements. This empty block will simply disable job metrics for the
+ corresponding destination. --></destination><destination id="pbs" runner="pbs" tags="mycluster"/><destination id="pbs_longjobs" runner="pbs" tags="mycluster,longjobs">
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 job_metrics_conf.xml.sample
--- /dev/null
+++ b/job_metrics_conf.xml.sample
@@ -0,0 +1,124 @@
+<?xml version="1.0"?>
+<!-- If job_metrics.xml exists, this file will define the default job metric
+ plugin used for all jobs. Individual job_conf.xml destinations can
+ disable metric collection by setting metrics="off" on that destination.
+ The metrics attribute on destination definition elements can also be
+ a path - in which case that XML metrics file will be loaded and used for
+ that destination. Finally, the destination element may contain a job_metrics
+ child element (with all options defined below) to define job metrics in an
+ embedded manner directly in the job_conf.xml file.
+-->
+<job_metrics>
+ <!-- Each element in this file corresponds to a job instrumentation plugin
+ used to generate metrics in lib/galaxy/jobs/metrics/instrumenters. -->
+
+ <!-- Core plugin captures Galaxy slots, start and end of job (in seconds
+ since epoch) and computes runtime in seconds. -->
+ <core />
+
+ <!-- Uncomment to dump processor count for each job - linux only. -->
+ <!-- <cpuinfo /> -->
+ <!-- Uncomment to dump information about all processors for for each
+ job - this is likely too much data. Linux only. -->
+ <!-- <cpuinfo verbose="true" /> -->
+
+ <!-- Uncomment to dump system memory information for each job - linux
+ only. -->
+ <!-- <meminfo /> -->
+
+ <!-- Uncomment to record operating system each job is executed on - linux
+ only. -->
+ <!-- <uname /> -->
+
+ <!-- Uncomment following to enable plugin dumping complete environment
+ for each job, potentially useful for debuging -->
+ <!-- <env /> -->
+ <!-- env plugin can also record more targetted, obviously useful variables
+ as well. -->
+ <!-- <env variables="HOSTNAME,SLURM_CPUS_ON_NODE,SLURM_JOBID" /> -->
+
+ <!-- <collectl /> -->
+ <!-- Collectl (http://collectl.sourceforge.net/) is a powerful monitoring
+ utility capable of gathering numerous system and process level
+ statistics of running applications. The Galaxy collectl job metrics
+ plugin by default will grab a variety of process level metrics
+ aggregated across all processes corresponding to a job, this behavior
+ is highly customiziable - both using the attributes documented below
+ or simply hacking up the code in lib/galaxy/jobs/metrics.
+
+ Warning: In order to use this plugin collectl must be available on the
+ compute server the job runs on and on the local Galaxy server as well
+ (unless in this latter case summarize_process_data is set to False).
+
+ Attributes (the follow describes attributes that can be used with
+ the collectl job metrics element above to modify its behavior).
+
+ 'summarize_process_data': Boolean indicating whether to run collectl
+ in playback mode after jobs complete and gather process level
+ statistics for the job run. These statistics can be customized
+ with the 'process_statistics' attribute. (defaults to True)
+
+ 'saved_logs_path': If set (it is off by default), all collectl logs
+ will be saved to the specified path after jobs complete. These
+ logs can later be replayed using collectl offline to generate
+ full time-series data corresponding to a job run.
+
+ 'subsystems': Comma separated list of collectl subystems to collect
+ data for. Plugin doesn't currently expose all of them or offer
+ summary data for any of them except 'process' but extensions
+ would be welcome. May seem pointless to include subsystems
+ beside process since they won't be processed online by Galaxy -
+ but if 'saved_logs_path' these files can be played back at anytime.
+
+ Available subsystems - 'process', 'cpu', 'memory', 'network',
+ 'disk', 'network'. (Default 'process').
+
+ Warning: If you override this - be sure to include 'process'
+ unless 'summarize_process_data' is set to false.
+
+ 'process_statistics': If 'summarize_process_data' this attribute can be
+ specified as a comma separated list to override the statistics
+ that are gathered. Each statistics is of the for X_Y where X
+ if one of 'min', 'max', 'count', 'avg', or 'sum' and Y is a
+ value from 'S', 'VmSize', 'VmLck', 'VmRSS', 'VmData', 'VmStk',
+ 'VmExe', 'VmLib', 'CPU', 'SysT', 'UsrT', 'PCT', 'AccumT' 'WKB',
+ 'RKBC', 'WKBC', 'RSYS', 'WSYS', 'CNCL', 'MajF', 'MinF'. Consult
+ lib/galaxy/jobs/metrics/collectl/processes.py for more details
+ on what each of these resource types means.
+
+ Defaults to 'max_VmSize,avg_VmSize,max_VmRSS,avg_VmRSS,sum_SysT,sum_UsrT,max_PCT avg_PCT,max_AccumT,sum_RSYS,sum_WSYS'
+ as variety of statistics roughly describing CPU and memory
+ usage of the program and VERY ROUGHLY describing I/O consumption.
+
+ 'procfilt_on': By default Galaxy will tell collectl to only collect
+ 'process' level data for the current user (as identified)
+ by 'username' (default) - this can be disabled by settting this
+ to 'none' - the plugin will still only aggregate process level
+ statistics for the jobs process tree - but the additional
+ information can still be used offline with 'saved_logs_path'
+ if set. Obsecurely, this can also be set 'uid' to identify
+ the current user to filter on by UID instead of username -
+ this may needed on some clusters(?).
+
+ 'interval': The time (in seconds) between data collection points.
+ Collectl uses a variety of different defaults for different
+ subsystems if this is not set, but process information (likely
+ the most pertinent for Galaxy jobs will collect data every
+ 60 seconds).
+
+ 'flush': Interval (in seconds I think) between when collectl will
+ flush its buffer to disk. Galaxy overrides this to disable
+ flushing by default if not set.
+
+ 'local_collectl_path', 'remote_collectl_path', 'collectl_path':
+ By default, jobs will just assume collectl is on the PATH, but
+ it can be overridden with 'local_collectl_path' and
+ 'remote_collectl_path' (or simply 'collectl_path' if it is not
+ on the path but installed in the same location both locally and
+ remotely).
+
+ There are more and more increasingly obsecure options including -
+ log_collectl_program_output, interval2, and interval3. Consult
+ source code for more details.
+ -->
+</job_metrics>
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/app.py
--- a/lib/galaxy/app.py
+++ b/lib/galaxy/app.py
@@ -15,7 +15,7 @@
from galaxy.sample_tracking import external_service_types
from galaxy.openid.providers import OpenIDProviders
from galaxy.tools.data_manager.manager import DataManagers
-
+from galaxy.jobs import metrics as job_metrics
from galaxy.web.base import pluginframework
import logging
@@ -60,6 +60,10 @@
self._configure_tool_data_tables( from_shed_config=False )
+ # Initialize job metrics manager, needs to be in place before
+ # config so per-destination modifications can be made.
+ self.job_metrics = job_metrics.JobMetrics( self.config.job_metrics_config_file, app=self )
+
# Initialize the job management configuration
self.job_config = jobs.JobConfiguration(self)
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -137,6 +137,7 @@
self.template_path = resolve_path( kwargs.get( "template_path", "templates" ), self.root )
self.template_cache = resolve_path( kwargs.get( "template_cache_path", "database/compiled_templates" ), self.root )
self.dependency_resolvers_config_file = resolve_path( kwargs.get( 'dependency_resolvers_config_file', 'dependency_resolvers_conf.xml' ), self.root )
+ self.job_metrics_config_file = resolve_path( kwargs.get( 'job_metrics_config_file', 'job_metrics_conf.xml' ), self.root )
self.job_config_file = resolve_path( kwargs.get( 'job_config_file', 'job_conf.xml' ), self.root )
self.local_job_queue_workers = int( kwargs.get( "local_job_queue_workers", "5" ) )
self.cluster_job_queue_workers = int( kwargs.get( "cluster_job_queue_workers", "3" ) )
@@ -455,6 +456,11 @@
admin_users = [ x.strip() for x in self.get( "admin_users", "" ).split( "," ) ]
return ( user is not None and user.email in admin_users )
+ def resolve_path( self, path ):
+ """ Resolve a path relative to Galaxy's root.
+ """
+ return resolve_path( path, self.root )
+
def get_database_engine_options( kwargs, model_prefix='' ):
"""
Allow options for the SQLAlchemy database engine to be passed by using
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -162,8 +162,21 @@
# Parse destinations
destinations = root.find('destinations')
+ job_metrics = self.app.job_metrics
for destination in self.__findall_with_required(destinations, 'destination', ('id', 'runner')):
id = destination.get('id')
+ destination_metrics = destination.get( "metrics", None )
+ if destination_metrics:
+ if not util.asbool( destination_metrics ):
+ # disable
+ job_metrics.set_destination_instrumenter( id, None )
+ else:
+ metrics_conf_path = self.app.config.resolve_path( destination_metrics )
+ job_metrics.set_destination_conf_file( id, metrics_conf_path )
+ else:
+ metrics_elements = self.__findall_with_required( destination, 'job_metrics', () )
+ if metrics_elements:
+ job_metrics.set_destination_conf_element( id, metrics_elements[ 0 ] )
job_destination = JobDestination(**dict(destination.items()))
job_destination['params'] = self.__get_params(destination)
self.destinations[id] = (job_destination,)
@@ -1068,8 +1081,10 @@
# Finally set the job state. This should only happen *after* all
# dataset creation, and will allow us to eliminate force_history_refresh.
job.state = final_job_state
+ if not job.tasks:
+ # If job was composed of tasks, don't attempt to recollect statisitcs
+ self._collect_metrics( job )
self.sa_session.flush()
-
log.debug( 'job %d ended' % self.job_id )
delete_files = self.app.config.cleanup_job == 'always' or ( job.state == job.states.OK and self.app.config.cleanup_job == 'onsuccess' )
self.cleanup( delete_files=delete_files )
@@ -1094,6 +1109,16 @@
except:
log.exception( "Unable to cleanup job %d" % self.job_id )
+ def _collect_metrics( self, has_metrics ):
+ job = has_metrics.get_job()
+ per_plugin_properties = self.app.job_metrics.collect_properties( job.destination_id, self.job_id, self.working_directory )
+ if per_plugin_properties:
+ log.info( "Collecting job metrics for %s" % has_metrics )
+ for plugin, properties in per_plugin_properties.iteritems():
+ for metric_name, metric_value in properties.iteritems():
+ if metric_value is not None:
+ has_metrics.add_metric( plugin, metric_name, metric_value )
+
def get_output_sizes( self ):
sizes = []
output_paths = self.get_output_fnames()
@@ -1508,6 +1533,7 @@
task.stdout = util.shrink_string_by_size( stdout, DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True )
if len( stderr ) > DATABASE_MAX_STRING_SIZE:
log.error( "stderr for task %d is greater than %s, only a portion will be logged to database" % ( task.id, DATABASE_MAX_STRING_SIZE_PRETTY ) )
+ self._collect_metrics( task )
task.stderr = util.shrink_string_by_size( stderr, DATABASE_MAX_STRING_SIZE, join_by="\n..\n", left_larger=True, beginning_on_size_error=True )
task.exit_code = tool_exit_code
task.command_line = self.command_line
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/__init__.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/__init__.py
@@ -0,0 +1,134 @@
+import collections
+import os
+
+from xml.etree import ElementTree
+
+from galaxy.util.submodules import submodules
+from galaxy import util
+
+from ..metrics import formatting
+
+import logging
+log = logging.getLogger( __name__ )
+
+
+DEFAULT_FORMATTER = formatting.JobMetricFormatter()
+
+
+class JobMetrics( object ):
+
+ def __init__( self, conf_file=None, **kwargs ):
+ """
+ """
+ self.plugin_classes = self.__plugins_dict()
+ self.default_job_instrumenter = JobInstrumenter.from_file( self.plugin_classes, conf_file, **kwargs )
+ self.job_instrumenters = collections.defaultdict( lambda: self.default_job_instrumenter )
+
+ def format( self, plugin, key, value ):
+ if plugin in self.plugin_classes:
+ plugin_class = self.plugin_classes[ plugin ]
+ formatter = plugin_class.formatter
+ else:
+ formatter = DEFAULT_FORMATTER
+ return formatter.format( key, value )
+
+ def set_destination_conf_file( self, destination_id, conf_file ):
+ instrumenter = JobInstrumenter.from_file( self.plugin_classes, conf_file )
+ self.set_destination_instrumenter( destination_id, instrumenter )
+
+ def set_destination_conf_element( self, destination_id, element ):
+ instrumenter = JobInstrumenter( self.plugin_classes, element )
+ self.set_destination_instrumenter( destination_id, instrumenter )
+
+ def set_destination_instrumenter( self, destination_id, job_instrumenter=None ):
+ if job_instrumenter is None:
+ job_instrumenter = NULL_JOB_INSTRUMENTER
+ self.job_instrumenters[ destination_id ] = job_instrumenter
+
+ def collect_properties( self, destination_id, job_id, job_directory ):
+ return self.job_instrumenters[ destination_id ].collect_properties( job_id, job_directory )
+
+ def __plugins_dict( self ):
+ plugin_dict = {}
+ for plugin_module in self.__plugin_modules():
+ for clazz in plugin_module.__all__:
+ plugin_type = getattr( clazz, 'plugin_type', None )
+ if plugin_type:
+ plugin_dict[ plugin_type ] = clazz
+ return plugin_dict
+
+ def __plugin_modules( self ):
+ import galaxy.jobs.metrics.instrumenters
+ return submodules( galaxy.jobs.metrics.instrumenters )
+
+
+class NullJobInstrumenter( object ):
+
+ def pre_execute_commands( self, job_directory ):
+ return None
+
+ def post_execute_commands( self, job_directory ):
+ return None
+
+ def collect_properties( self, job_id, job_directory ):
+ return {}
+
+NULL_JOB_INSTRUMENTER = NullJobInstrumenter()
+
+
+class JobInstrumenter( object ):
+
+ def __init__( self, plugin_classes, metrics_element, **kwargs ):
+ self.extra_kwargs = kwargs
+ self.plugin_classes = plugin_classes
+ self.plugins = self.__plugins_for_element( metrics_element )
+
+ def pre_execute_commands( self, job_directory ):
+ commands = []
+ for plugin in self.plugins:
+ try:
+ plugin_commands = plugin.pre_execute_instrument( job_directory )
+ if plugin_commands:
+ commands.extend( util.listify( plugin_commands ) )
+ except Exception:
+ log.exception( "Failed to generate pre-execute commands for plugin %s" % plugin )
+ return "\n".join( [ c for c in commands if c ] )
+
+ def post_execute_commands( self, job_directory ):
+ commands = []
+ for plugin in self.plugins:
+ try:
+ plugin_commands = plugin.post_execute_instrument( job_directory )
+ if plugin_commands:
+ commands.extend( util.listify( plugin_commands ) )
+ except Exception:
+ log.exception( "Failed to generate post-execute commands for plugin %s" % plugin )
+ return "\n".join( [ c for c in commands if c ] )
+
+ def collect_properties( self, job_id, job_directory ):
+ per_plugin_properites = {}
+ for plugin in self.plugins:
+ try:
+ properties = plugin.job_properties( job_id, job_directory )
+ if properties:
+ per_plugin_properites[ plugin.plugin_type ] = properties
+ except Exception:
+ log.exception( "Failed to collect job properties for plugin %s" % plugin )
+ return per_plugin_properites
+
+ def __plugins_for_element( self, plugins_element ):
+ plugins = []
+ for plugin_element in plugins_element.getchildren():
+ plugin_type = plugin_element.tag
+ plugin_kwds = dict( plugin_element.items() )
+ plugin_kwds.update( self.extra_kwargs )
+ plugin = self.plugin_classes[ plugin_type ]( **plugin_kwds )
+ plugins.append( plugin )
+ return plugins
+
+ @staticmethod
+ def from_file( plugin_classes, conf_file, **kwargs ):
+ if not conf_file or not os.path.exists( conf_file ):
+ return NULL_JOB_INSTRUMENTER
+ plugins_element = ElementTree.parse( conf_file ).getroot()
+ return JobInstrumenter( plugin_classes, plugins_element, **kwargs )
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/collectl/__init__.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/collectl/__init__.py
@@ -0,0 +1,5 @@
+""" This module contains helper functions and data structures for interacting
+with collectl and collectl generated data. More information on collectl can be
+found at: http://collectl.sourceforge.net/.
+
+"""
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/collectl/cli.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/collectl/cli.py
@@ -0,0 +1,128 @@
+from string import Template
+import subprocess
+
+import logging
+log = logging.getLogger( __name__ )
+
+COMMAND_LINE_TEMPLATE = Template(
+ "$collectl_path $destination_arg $mode_arg $subsystems_arg $interval_arg $procfilt_arg $flush_arg $sep_arg"
+)
+MODE_RECORD = "record"
+MODE_PLAYBACK = "playback"
+
+
+class CollectlCli( object ):
+ """ Abstraction over (some of) the command-line arguments of collectl.
+ Ideally this will be useful for building up command line arguments for
+ remote execution as well as runnning directly on local host.
+
+ This is meant to be a fairly generic utility - for interfacing with
+ collectl CLI - logic more directly related to the Galaxy job metric plugin
+ plugin should be placed in other modules.
+
+ Keyword Arguments:
+ collectl_path: Path to collectl executable (defaults to collectl - i.e.
+ search the PATH).
+
+ playback_path (defaults to None): If this is None collectl will run in
+ record mode, else it will playback specified file.
+
+ Playback Mode Options:
+
+ sep : Separator used in playback mode (set to 9 to produce tsv)
+ (defaults to None).
+
+ Record Mode Options (some of these may work in playback mode also):
+
+ destination_path: Location of path files to write to (defaults to None
+ and collectl will just use cwd). Really this is just to prefix -
+ collectl will append hostname and datetime to file.
+ interval: Setup polling interval (secs) for most subsystems (defaults
+ to None and when unspecified collectl will use default of 1 second).
+ interval2: Setup polling interval (secs) for process information
+ (defaults to None and when unspecified collectl will use default to
+ 60 seconds).
+ interval3: Setup polling interval (secs) for environment information
+ (defaults to None and when unspecified collectl will use default to
+ 300 seconds).
+ procfilt: Optional argument to procfilt. (defaults to None).
+ flush : Optional flush interval (defaults to None).
+ """
+
+ def __init__( self, **kwargs ):
+ command_args = {}
+ command_args[ "collectl_path" ] = kwargs.get( "collectl_path", "collectl" )
+ playback_path = kwargs.get( "playback_path", None )
+ self.mode = MODE_RECORD if not playback_path else MODE_PLAYBACK
+ if self.mode == MODE_RECORD:
+ mode_arg = ""
+ elif self.mode == MODE_PLAYBACK:
+ mode_arg = "-P -p '%s'" % playback_path
+ else:
+ raise Exception( "Invalid mode supplied to CollectlCli - %s" % self.mode )
+ command_args[ "mode_arg" ] = mode_arg
+ command_args[ "interval_arg" ] = self.__interval_arg( kwargs )
+ destination = kwargs.get( "destination_path", None )
+ if destination:
+ destination_arg = "-f '%s'" % destination
+ else:
+ destination_arg = ""
+ command_args[ "destination_arg" ] = destination_arg
+ procfilt = kwargs.get( "procfilt", None )
+ command_args[ "procfilt_arg" ] = "" if not procfilt else "--procfilt %s" % procfilt
+ command_args[ "subsystems_arg" ] = self.__subsystems_arg( kwargs.get( "subsystems", [] ) )
+ flush = kwargs.get( "flush", None )
+ command_args[ "flush_arg"] = "--flush %s" % flush if flush else ""
+ sep = kwargs.get( "sep", None )
+ command_args[ "sep_arg" ] = "--sep=%s" % sep if sep else ""
+
+ self.command_args = command_args
+
+ def __subsystems_arg( self, subsystems ):
+ if subsystems:
+ return "-s%s" % "".join( [ s.command_line_arg for s in subsystems ] )
+ else:
+ return ""
+
+ def __interval_arg( self, kwargs ):
+ if self.mode != MODE_RECORD:
+ return ""
+
+ interval = kwargs.get( "interval", None )
+ if not interval:
+ return ""
+
+ self.__validate_interval_arg( interval )
+ interval_arg = "-i %s" % interval
+ interval2 = kwargs.get( "interval2", None )
+ if not interval2:
+ return interval_arg
+ self.__validate_interval_arg( interval2, multiple_of=int( interval ) )
+ interval_arg = "%s:%s" % ( interval_arg, interval2 )
+
+ interval3 = kwargs.get( "interval3", None )
+ if not interval3:
+ return interval_arg
+ self.__validate_interval_arg( interval3, multiple_of=int( interval ) )
+ interval_arg = "%s:%s" % ( interval_arg, interval3 )
+ return interval_arg
+
+ def __validate_interval_arg( self, value, multiple_of=None ):
+ if value and not str(value).isdigit():
+ raise Exception( "Invalid interval argument supplied, must be integer %s" % value )
+ if multiple_of:
+ if int( value ) % multiple_of != 0:
+ raise Exception( "Invalid interval argument supplied, must multiple of %s" % multiple_of )
+
+ def build_command_line( self ):
+ return COMMAND_LINE_TEMPLATE.substitute( **self.command_args )
+
+ def run( self, stdout=subprocess.PIPE, stderr=subprocess.PIPE ):
+ command_line = self.build_command_line()
+ log.info( "Executing %s" % command_line )
+ proc = subprocess.Popen( command_line, shell=True, stdout=stdout, stderr=stderr )
+ return_code = proc.wait()
+ if return_code:
+ raise Exception( "Problem running collectl command." )
+
+__all__ = [ CollectlCli ]
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/collectl/processes.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/collectl/processes.py
@@ -0,0 +1,252 @@
+""" Modules will run collectl in playback mode and collect various process
+statistics for a given pid's process and process ancestors.
+"""
+import collections
+import csv
+import tempfile
+
+from galaxy import util
+
+from ..collectl import stats
+
+import logging
+log = logging.getLogger( __name__ )
+
+# Collectl process information cheat sheet:
+#
+# Record process information for current user.
+# % collectl -sZ -f./__instrument_collectl -i 10:10 --procfilt U$USER
+#
+# TSV Replay of processing information in plottable mode...
+#
+# % collectl -sZ -P --sep=9 -p __instrument_collectl-jlaptop13-20140322-120919.raw.gz
+#
+# Has following columns:
+# Date Time PID User PR PPID THRD S VmSize VmLck VmRSS VmData VmStk VmExe VmLib CPU SysT UsrT PCT AccumT RKB WKB RKBC WKBC RSYS WSYS CNCL MajF MinF Command
+#
+
+# Process data dumped one row per process per interval.
+#http://collectl.sourceforge.net/Data-detail.html
+PROCESS_COLUMNS = [
+ "#Date", # Date of interval - e.g. 20140322
+ "Time", # Time of interval - 12:18:58
+ "PID", # Process pid.
+ "User", # Process user.
+ "PR", # Priority of process.
+ "PPID", # Parent PID of process.
+ "THRD", # Thread???
+ "S", # Process state - S - Sleeping, D - Uninterruptable Sleep, R - Running, Z - Zombie or T - Stopped/Traced
+ ## Memory options - http://ewx.livejournal.com/579283.html
+ "VmSize",
+ "VmLck",
+ "VmRSS",
+ "VmData",
+ "VmStk",
+ "VmExe",
+ "VmLib",
+ "CPU", # CPU number of process
+ "SysT", # Amount of system time consumed during interval
+ "UsrT", # Amount user time consumed during interval
+ "PCT", # Percentage of current interval consumed by task
+ "AccumT", # Total accumulated System and User time since the process began execution
+ # kilobytes read/written - requires I/O level monitoring to be enabled in kernel.
+ "RKB", # kilobytes read by process - requires I/O monitoring in kernel
+ "WKB",
+ "RKBC",
+ "WKBC",
+ "RSYS", # Number of read system calls
+ "WSYS", # Number of write system calls
+ "CNCL",
+ "MajF", # Number of major page faults
+ "MinF", # Number of minor page faults
+ "Command", # Command executed
+]
+
+# Types of statistics this module can summarize
+STATISTIC_TYPES = [ "max", "min", "sum", "count", "avg" ]
+
+COLUMN_INDICES = dict( [ ( col, i ) for i, col in enumerate( PROCESS_COLUMNS ) ] )
+PID_INDEX = COLUMN_INDICES[ "PID" ]
+PARENT_PID_INDEX = COLUMN_INDICES[ "PPID" ]
+
+DEFAULT_STATISTICS = [
+ ("max", "VmSize"),
+ ("avg", "VmSize"),
+ ("max", "VmRSS"),
+ ("avg", "VmRSS"),
+ ("sum", "SysT"),
+ ("sum", "UsrT"),
+ ("max", "PCT"),
+ ("avg", "PCT"),
+ ("max", "AccumT"),
+ ("sum", "RSYS"),
+ ("sum", "WSYS"),
+]
+
+
+def parse_process_statistics( statistics ):
+ """ Turn string or list of strings into list of tuples in format ( stat,
+ resource ) where stat is a value from STATISTIC_TYPES and resource is a
+ value from PROCESS_COLUMNS.
+ """
+ if statistics is None:
+ statistics = DEFAULT_STATISTICS
+
+ statistics = util.listify( statistics )
+ statistics = map( __tuplize_statistic, statistics )
+ # Check for validity...
+ for statistic in statistics:
+ if statistic[ 0 ] not in STATISTIC_TYPES:
+ raise Exception( "Unknown statistic type encountered %s" % statistic[ 0 ] )
+ if statistic[ 1 ] not in PROCESS_COLUMNS:
+ raise Exception( "Unknown process column encountered %s" % statistic[ 1 ] )
+ return statistics
+
+
+def generate_process_statistics( collectl_playback_cli, pid, statistics=DEFAULT_STATISTICS ):
+ """ Playback collectl file and generate summary statistics.
+ """
+ with tempfile.NamedTemporaryFile( ) as tmp_tsv:
+ collectl_playback_cli.run( stdout=tmp_tsv )
+ with open( tmp_tsv.name, "r" ) as tsv_file:
+ return __read_process_statistics( tsv_file, pid, statistics )
+
+
+def __read_process_statistics( tsv_file, pid, statistics ):
+ process_summarizer = CollectlProcessSummarizer( pid, statistics )
+ current_interval = None
+
+ for row in csv.reader( tsv_file, dialect="excel-tab" ):
+ if current_interval is None:
+ for header, expected_header in zip( row, PROCESS_COLUMNS ):
+ if header.lower() != expected_header.lower():
+ raise Exception( "Unknown header value encountered while processing collectl playback - %s" % header )
+
+ # First row, check contains correct header.
+ current_interval = CollectlProcessInterval()
+ continue
+
+ if current_interval.row_is_in( row ):
+ current_interval.add_row( row )
+ else:
+ process_summarizer.handle_interval( current_interval )
+ current_interval = CollectlProcessInterval()
+
+ # Do we have unsummarized rows...
+ if current_interval and current_interval.rows:
+ process_summarizer.handle_interval( current_interval )
+
+ return process_summarizer.get_statistics()
+
+
+class CollectlProcessSummarizer( object ):
+
+ def __init__( self, pid, statistics ):
+ self.pid = pid
+ self.statistics = statistics
+ self.columns_of_interest = set( [ s[ 1 ] for s in statistics ] )
+ self.tree_statistics = collections.defaultdict( stats.StatisticsTracker )
+ self.process_accum_statistics = collections.defaultdict( stats.StatisticsTracker )
+ self.interval_count = 0
+
+ def handle_interval( self, interval ):
+ self.interval_count += 1
+ rows = self.__rows_for_process( interval.rows, self.pid )
+ for column_name in self.columns_of_interest:
+ column_index = COLUMN_INDICES[ column_name ]
+
+ if column_name == "AccumT":
+ # Should not sum this across pids each interval, sum max at end...
+ for r in rows:
+ pid_seconds = self.__time_to_seconds( r[ column_index ] )
+ self.process_accum_statistics[ r[ PID_INDEX ] ].track( pid_seconds )
+ else:
+ # All other stastics should be summed across whole process tree
+ # at each interval I guess.
+ if column_name in [ "SysT", "UsrT", "PCT" ]:
+ to_num = float
+ else:
+ to_num = long
+
+ interval_stat = sum( to_num( r[ column_index ] ) for r in rows )
+ self.tree_statistics[ column_name ].track( interval_stat )
+
+ def get_statistics( self ):
+ if self.interval_count == 0:
+ return []
+
+ computed_statistics = []
+ for statistic in self.statistics:
+ statistic_type, column = statistic
+ if column == "AccumT":
+ # Only thing that makes sense is sum
+ if statistic_type != "max":
+ log.warn( "Only statistic max makes sense for AccumT" )
+ continue
+
+ value = sum( [ v.max for v in self.process_accum_statistics.itervalues() ] )
+ else:
+ statistics_tracker = self.tree_statistics[ column ]
+ value = getattr( statistics_tracker, statistic_type )
+
+ computed_statistic = ( statistic, value )
+ computed_statistics.append( computed_statistic )
+
+ return computed_statistics
+
+ def __rows_for_process( self, rows, pid ):
+ process_rows = []
+ pids = self.__all_child_pids( rows, pid )
+ for row in rows:
+ if row[ PID_INDEX ] in pids:
+ process_rows.append( row )
+ return process_rows
+
+ def __all_child_pids( self, rows, pid ):
+ pids_in_process_tree = set( [ str( self.pid ) ] )
+ added = True
+ while added:
+ added = False
+ for row in rows:
+ pid = row[ PID_INDEX ]
+ parent_pid = row[ PARENT_PID_INDEX ]
+ if parent_pid in pids_in_process_tree and pid not in pids_in_process_tree:
+ pids_in_process_tree.add( pid )
+ added = True
+ return pids_in_process_tree
+
+ def __time_to_seconds( self, minutes_str ):
+ parts = minutes_str.split( ":" )
+ seconds = 0.0
+ for i, val in enumerate( parts ):
+ seconds += float(val) * ( 60 ** ( len( parts ) - ( i + 1 ) ) )
+ return seconds
+
+
+class CollectlProcessInterval( object ):
+ """ Represent all rows in collectl playback file for given time slice with
+ ability to filter out just rows corresponding to the process tree
+ corresponding to a given pid.
+ """
+
+ def __init__( self ):
+ self.rows = []
+
+ def row_is_in( self, row ):
+ if not self.rows: # No rows, this row defines interval.
+ return True
+ first_row = self.rows[ 0 ]
+ return first_row[ 0 ] == row[ 0 ] and first_row[ 1 ] == row[ 1 ]
+
+ def add_row( self, row ):
+ self.rows.append( row )
+
+
+def __tuplize_statistic( statistic ):
+ if not isinstance( statistic, tuple ):
+ statistic_split = statistic.split( "_", 1 )
+ statistic = ( statistic_split[ 0 ].lower(), statistic_split[ 1 ] )
+ return statistic
+
+
+__all__ = [ generate_process_statistics ]
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/collectl/stats.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/collectl/stats.py
@@ -0,0 +1,27 @@
+""" Primitive module for tracking running statistics without storing values in
+memory.
+"""
+
+
+class StatisticsTracker( object ):
+
+ def __init__( self ):
+ self.min = None
+ self.max = None
+ self.count = 0
+ self.sum = 0
+
+ def track( self, value ):
+ if self.min is None or value < self.min:
+ self.min = value
+ if self.max is None or value > self.max:
+ self.max = value
+ self.count += 1
+ self.sum += value
+
+ @property
+ def avg( self ):
+ if self.count > 0:
+ return self.sum / self.count
+ else:
+ return None
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/collectl/subsystems.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/collectl/subsystems.py
@@ -0,0 +1,72 @@
+from abc import ABCMeta
+from abc import abstractmethod
+
+
+class CollectlSubsystem( object ):
+ """ Class providing an abstraction of collectl subsytems.
+ """
+ __metaclass__ = ABCMeta
+
+ @property
+ @abstractmethod
+ def command_line_arg( self ):
+ """ Return single letter command-line argument used by collectl CLI.
+ """
+
+ @property
+ @abstractmethod
+ def name( self, job_directory ):
+ """ High-level name for subsystem as consumed by this module.
+ """
+
+
+class ProcessesSubsystem( CollectlSubsystem ):
+ command_line_arg = "Z"
+ name = "process"
+
+
+class CpuSubsystem( CollectlSubsystem ):
+ command_line_arg = "C"
+ name = "cpu"
+
+
+class DiskSubsystem( CollectlSubsystem ):
+ command_line_arg = "D"
+ name = "disk"
+
+
+class NetworkSubsystem( CollectlSubsystem ):
+ command_line_arg = "N"
+ name = "network"
+
+
+class EnvironmentSubsystem( CollectlSubsystem ):
+ command_line_arg = "E"
+ name = "environment"
+
+
+class MemorySubsystem( CollectlSubsystem ):
+ command_line_arg = "M"
+ name = "memory"
+
+
+SUBSYSTEMS = [
+ ProcessesSubsystem(),
+ CpuSubsystem(),
+ DiskSubsystem(),
+ NetworkSubsystem(),
+ EnvironmentSubsystem(),
+ MemorySubsystem(),
+]
+SUBSYSTEM_DICT = dict( [ (s.name, s) for s in SUBSYSTEMS ] )
+
+
+def get_subsystem( name ):
+ """
+
+ >>> get_subsystem( "process" ).command_line_arg == "Z"
+ True
+ """
+ return SUBSYSTEM_DICT[ name ]
+
+__all__ = [ get_subsystem ]
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/formatting.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/formatting.py
@@ -0,0 +1,18 @@
+
+
+class JobMetricFormatter( object ):
+ """ Format job metric key-value pairs for human consumption in Web UI. """
+
+ def format( self, key, value ):
+ return ( str( key ), str( value ) )
+
+
+## Formatting utilities
+
+def seconds_to_str( value ):
+ if value < 60:
+ return "%s seconds" % value
+ elif value < 3600:
+ return "%s minutes" % ( value / 60 )
+ else:
+ return "%s days and %s minutes" % ( value / 3600, ( value % 3600 ) / 60 )
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/instrumenters/__init__.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/__init__.py
@@ -0,0 +1,53 @@
+from abc import ABCMeta
+from abc import abstractmethod
+
+import os.path
+
+from ...metrics import formatting
+
+
+INSTRUMENT_FILE_PREFIX = "__instrument"
+
+
+class InstrumentPlugin( object ):
+ """ A plugin describing how to instrument Galaxy jobs and retrieve metrics
+ from this instrumentation.
+ """
+ __metaclass__ = ABCMeta
+ formatter = formatting.JobMetricFormatter()
+
+ @property
+ @abstractmethod
+ def plugin_type( self ):
+ """ Short string providing labelling this plugin """
+
+ def pre_execute_instrument( self, job_directory ):
+ """ Optionally return one or more commands to instrument job. These
+ commands will be executed on the compute server prior to the job
+ running.
+ """
+ return None
+
+ def post_execute_instrument( self, job_directory ):
+ """ Optionally return one or more commands to instrument job. These
+ commands will be executed on the compute server after the tool defined
+ command is ran.
+ """
+ return None
+
+ @abstractmethod
+ def job_properties( self, job_id, job_directory ):
+ """ Collect properties for this plugin from specified job directory.
+ This method will run on the Galaxy server and can assume files created
+ in job_directory with pre_execute_instrument and
+ post_execute_instrument are available.
+ """
+
+ def _instrument_file_name( self, name ):
+ """ Provide a common pattern for naming files used by instrumentation
+ plugins - to ease their staging out of remote job directories.
+ """
+ return "%s_%s_%s" % ( INSTRUMENT_FILE_PREFIX, self.plugin_type, name )
+
+ def _instrument_file_path( self, job_directory, name ):
+ return os.path.join( job_directory, self._instrument_file_name( name ) )
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/instrumenters/collectl.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/collectl.py
@@ -0,0 +1,214 @@
+import os
+import shutil
+
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+from ..collectl import subsystems
+from ..collectl import cli
+from ..collectl import processes
+
+from galaxy import util
+from galaxy.util import directory_hash
+
+import logging
+log = logging.getLogger( __name__ )
+
+DEFAULT_PROCFILT_ON = "username" # By default, only grab statistics for user
+ # processes (as identifiers by username).
+DEFAULT_SUBSYSTEMS = "process"
+DEFAULT_FLUSH_INTERVAL = "0" # Set to zero to flush every collection.
+
+FORMATTED_RESOURCE_TITLES = {
+ "PCT": "Percent CPU Usage",
+ "RSYS": "Disk Reads",
+ "WSYS": "Disk Writes",
+}
+
+EMPTY_COLLECTL_FILE_MESSAGE = "Skipping process summary due to empty file... job probably did not run long enough for collectl to gather data."
+
+
+class CollectlFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ if key == "pid":
+ return ( "Process ID", int( value ) )
+ elif key == "raw_log_path":
+ return ( "Relative Path of Full Collectl Log", value )
+ elif key == "process_max_AccumT":
+ return ( "Job Runtime (System+User)", formatting.seconds_to_str( float( value ) ) )
+ else:
+ _, stat_type, resource_type = key.split( "_", 2 )
+ if resource_type.startswith( "Vm"):
+ value_str = "%s KB" % int( value )
+ elif resource_type in [ "RSYS", "WSYS" ] and stat_type in [ "count", "max", "sum" ]:
+ value_str = "%d (# system calls)" % int( value )
+ else:
+ value_str = str( value )
+ resource_title = FORMATTED_RESOURCE_TITLES.get( resource_type, resource_type )
+ return ( "%s (%s)" % ( resource_title, stat_type ), value_str )
+
+
+class CollectlPlugin( InstrumentPlugin ):
+ """ Run collectl along with job to capture system and/or process data
+ according to specified collectl subsystems.
+ """
+ plugin_type = "collectl"
+ formatter = CollectlFormatter()
+
+ def __init__( self, **kwargs ):
+ self.__configure_paths( kwargs )
+ self.__configure_subsystems( kwargs )
+ saved_logs_path = kwargs.get( "saved_logs_path", None )
+ if "app" in kwargs:
+ saved_logs_path = kwargs[ "app" ].config.resolve_path( saved_logs_path )
+ self.saved_logs_path = saved_logs_path
+ self.__configure_collectl_recorder_args( kwargs )
+ self.summarize_process_data = util.asbool( kwargs.get( "summarize_process_data", True ) )
+ self.log_collectl_program_output = util.asbool( kwargs.get( "log_collectl_program_output", False ) )
+ if self.summarize_process_data:
+ if subsystems.get_subsystem( "process" ) not in self.subsystems:
+ raise Exception( "Collectl plugin misconfigured - cannot summarize_process_data without process subsystem being enabled." )
+
+ process_statistics = kwargs.get( "process_statistics", None )
+ # None will let processes module use default set of statistics
+ # defined there.
+ self.process_statistics = processes.parse_process_statistics( process_statistics )
+
+ def pre_execute_instrument( self, job_directory ):
+ commands = []
+ # Capture PID of process so we can walk its ancestors when building
+ # statistics for the whole job.
+ commands.append( '''echo "$$" > '%s' ''' % self.__pid_file( job_directory ) )
+ # Run collectl in record mode to capture process and system level
+ # statistics according to supplied subsystems.
+ commands.append( self.__collectl_record_command( job_directory ) )
+ return commands
+
+ def post_execute_instrument( self, job_directory ):
+ commands = []
+ # collectl dies when job script completes, perhaps capture pid of
+ # collectl above and check if it is still alive to allow tracking if
+ # collectl ran successfully through the whole job.
+ return commands
+
+ def job_properties( self, job_id, job_directory ):
+ pid = open( self.__pid_file( job_directory ), "r" ).read().strip()
+ contents = os.listdir( job_directory )
+ try:
+ rel_path = filter( self._is_instrumented_collectl_log, contents )[ 0 ]
+ path = os.path.join( job_directory, rel_path )
+ except IndexError:
+ message = "Failed to find collectl log in directory %s, files were %s" % ( job_directory, contents )
+ raise Exception( message )
+
+ properties = dict(
+ pid=int( pid ),
+ )
+
+ if self.saved_logs_path:
+ destination_rel_dir = os.path.join( *directory_hash.directory_hash_id( job_id ) )
+ destination_rel_path = os.path.join( destination_rel_dir, rel_path )
+ destination_path = os.path.join( self.saved_logs_path, destination_rel_path )
+ destination_dir = os.path.dirname( destination_path )
+ if not os.path.isdir( destination_dir ):
+ os.makedirs( destination_dir )
+ shutil.copyfile( path, destination_path )
+ properties[ "raw_log_path" ] = destination_rel_path
+
+ if self.summarize_process_data:
+ # Run collectl in playback and generate statistics of interest
+ summary_statistics = self.__summarize_process_data( pid, path )
+ for statistic, value in summary_statistics:
+ properties[ "process_%s" % "_".join( statistic ) ] = value
+
+ return properties
+
+ def __configure_paths( self, kwargs ):
+ # 95% of time I would expect collectl to just be installed with apt or
+ # yum, but if it is manually installed on not on path, allow
+ # configuration of explicit path - and allow path to be different
+ # between galaxy job handler (local_collectl_path) and compute node
+ # (remote_collectl_path).
+ collectl_path = kwargs.get( "collectl_path", "collectl" )
+ self.remote_collectl_path = kwargs.get( "remote_collectl_path", collectl_path )
+ self.local_collectl_path = kwargs.get( "local_collectl_path", collectl_path )
+
+ def __configure_subsystems( self, kwargs ):
+ raw_subsystems_str = kwargs.get( "subsystems", DEFAULT_SUBSYSTEMS )
+ raw_subsystems = util.listify( raw_subsystems_str, do_strip=True )
+ self.subsystems = map( subsystems.get_subsystem, raw_subsystems )
+
+ def __configure_collectl_recorder_args( self, kwargs ):
+ collectl_recorder_args = kwargs.copy()
+
+ # Allow deployer to configure separate system and process intervals,
+ # but if they specify just one - use it for both. Thinking here is this
+ # plugin's most useful feature is the process level information so
+ # this is likely what the deployer is attempting to configure.
+ if "interval" in kwargs and "interval2" not in kwargs:
+ collectl_recorder_args[ "interval2" ] = kwargs[ "interval"]
+
+ if "flush" not in kwargs:
+ collectl_recorder_args[ "flush" ] = DEFAULT_FLUSH_INTERVAL
+
+ procfilt_on = kwargs.get( "procfilt_on", DEFAULT_PROCFILT_ON ).lower()
+ # Calculate explicit arguments, rest can just be passed through from
+ # constructor arguments.
+ explicit_args = dict(
+ collectl_path=self.remote_collectl_path,
+ procfilt=procfilt_argument( procfilt_on ),
+ subsystems=self.subsystems,
+ )
+ collectl_recorder_args.update( explicit_args )
+ self.collectl_recorder_args = collectl_recorder_args
+
+ def __summarize_process_data( self, pid, collectl_log_path ):
+ playback_cli_args = dict(
+ collectl_path=self.local_collectl_path,
+ playback_path=collectl_log_path,
+ sep="9"
+ )
+ if not os.stat( collectl_log_path ).st_size:
+ log.debug( EMPTY_COLLECTL_FILE_MESSAGE )
+ return [ ]
+
+ playback_cli = cli.CollectlCli( **playback_cli_args )
+ return processes.generate_process_statistics( playback_cli, pid, self.process_statistics )
+
+ def __collectl_recorder_cli( self, job_directory ):
+ cli_args = self.collectl_recorder_args.copy()
+ cli_args[ "destination_path" ] = self._instrument_file_path( job_directory, "log" )
+ return cli.CollectlCli( **cli_args )
+
+ def __collectl_record_command( self, job_directory ):
+ collectl_cli = self.__collectl_recorder_cli( job_directory )
+ if self.log_collectl_program_output:
+ redirect_to = self._instrument_file_path( job_directory, "program_output" )
+ else:
+ redirect_to = "/dev/null"
+ return "%s > %s 2>&1 &" % (
+ collectl_cli.build_command_line(),
+ redirect_to,
+ )
+
+ def __pid_file( self, job_directory ):
+ return self._instrument_file_path( job_directory, "pid" )
+
+ def _is_instrumented_collectl_log( self, filename ):
+ prefix = self._instrument_file_name( "log" )
+ return filename.startswith( prefix ) and filename.endswith( ".raw.gz" )
+
+
+def procfilt_argument( procfilt_on ):
+ if procfilt_on == "username":
+ return "U$USER"
+ elif procfilt_on == "uid":
+ return "u$UID"
+ else:
+ # Ensure it is empty of None
+ if procfilt_on or procfilt_on.lower() != "none":
+ raise Exception( "Invalid procfilt_on argument encountered")
+ return ""
+
+
+__all__ = [ CollectlPlugin ]
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/instrumenters/core.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/core.py
@@ -0,0 +1,85 @@
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+import time
+
+import logging
+log = logging.getLogger( __name__ )
+
+GALAXY_SLOTS_KEY = "galaxy_slots"
+START_EPOCH_KEY = "start_epoch"
+END_EPOCH_KEY = "end_epoch"
+RUNTIME_SECONDS_KEY = "runtime_seconds"
+
+
+class CorePluginFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ value = int( value )
+ if key == GALAXY_SLOTS_KEY:
+ return ( "Cores Allocated", "%d" % value )
+ elif key == RUNTIME_SECONDS_KEY:
+ return ( "Job Runtime (Wall Clock)", formatting.seconds_to_str( value ) )
+ else:
+ # TODO: Use localized version of this from universe_wsgi.ini
+ title = "Job Start Time" if key == START_EPOCH_KEY else "Job End Time"
+ return (title, time.strftime( '%Y-%m-%d %H:%M:%S', time.localtime( value ) ) )
+
+
+class CorePlugin( InstrumentPlugin ):
+ """ Simple plugin that collects data without external dependencies. In
+ particular it currently collects value set for Galaxy slots.
+ """
+ plugin_type = "core"
+ formatter = CorePluginFormatter()
+
+ def __init__( self, **kwargs ):
+ pass
+
+ def pre_execute_instrument( self, job_directory ):
+ commands = []
+ commands.append( self.__record_galaxy_slots_command( job_directory ) )
+ commands.append( self.__record_seconds_since_epoch_to_file( job_directory, "start" ) )
+ return commands
+
+ def post_execute_instrument( self, job_directory ):
+ commands = []
+ commands.append( self.__record_seconds_since_epoch_to_file( job_directory, "end" ) )
+ return commands
+
+ def job_properties( self, job_id, job_directory ):
+ galaxy_slots_file = self.__galaxy_slots_file( job_directory )
+
+ properties = {}
+ properties[ GALAXY_SLOTS_KEY ] = self.__read_integer( galaxy_slots_file )
+ start = self.__read_seconds_since_epoch( job_directory, "start" )
+ end = self.__read_seconds_since_epoch( job_directory, "end" )
+ if start is not None and end is not None:
+ properties[ START_EPOCH_KEY ] = start
+ properties[ END_EPOCH_KEY ] = end
+ properties[ RUNTIME_SECONDS_KEY ] = end - start
+ return properties
+
+ def __record_galaxy_slots_command( self, job_directory ):
+ galaxy_slots_file = self.__galaxy_slots_file( job_directory )
+ return '''echo "$GALAXY_SLOTS" > '%s' ''' % galaxy_slots_file
+
+ def __record_seconds_since_epoch_to_file( self, job_directory, name ):
+ path = self._instrument_file_path( job_directory, "epoch_%s" % name )
+ return 'date +"%s" > ' + path
+
+ def __read_seconds_since_epoch( self, job_directory, name ):
+ path = self._instrument_file_path( job_directory, "epoch_%s" % name )
+ return self.__read_integer( path )
+
+ def __galaxy_slots_file( self, job_directory ):
+ return self._instrument_file_path( job_directory, "galaxy_slots" )
+
+ def __read_integer( self, path ):
+ value = None
+ try:
+ value = int( open( path, "r" ).read() )
+ except Exception:
+ pass
+ return value
+
+__all__ = [ CorePlugin ]
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/instrumenters/cpuinfo.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/cpuinfo.py
@@ -0,0 +1,62 @@
+import re
+
+from galaxy import util
+
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+
+import logging
+log = logging.getLogger( __name__ )
+
+PROCESSOR_LINE = re.compile(r"processor\s*\:\s*(\d+)")
+
+
+class CpuInfoFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ if key == "processor_count":
+ return "Processor Count", "%s" % int( value )
+ else:
+ return key, value
+
+
+class CpuInfoPlugin( InstrumentPlugin ):
+ """ Gather information about processor configuration from /proc/cpuinfo.
+ Linux only.
+ """
+ plugin_type = "cpuinfo"
+ formatter = CpuInfoFormatter()
+
+ def __init__( self, **kwargs ):
+ self.verbose = util.asbool( kwargs.get( "verbose", False ) )
+
+ def pre_execute_instrument( self, job_directory ):
+ return "cat /proc/cpuinfo > '%s'" % self.__instrument_cpuinfo_path( job_directory )
+
+ def job_properties( self, job_id, job_directory ):
+ properties = {}
+ processor_count = 0
+ with open( self.__instrument_cpuinfo_path( job_directory ) ) as f:
+ current_processor = None
+ for line in f:
+ line = line.strip().lower()
+ if not line: # Skip empty lines
+ continue
+
+ processor_line_match = PROCESSOR_LINE.match( line )
+ if processor_line_match:
+ processor_count += 1
+ current_processor = processor_line_match.group( 1 )
+ elif current_processor and self.verbose:
+ # If verbose, dump information about each processor
+ # into database...
+ key, value = line.split( ":", 1 )
+ key = "processor_%s_%s" % ( current_processor, key.strip() )
+ value = value
+ properties[ "processor_count" ] = processor_count
+ return properties
+
+ def __instrument_cpuinfo_path( self, job_directory ):
+ return self._instrument_file_path( job_directory, "cpuinfo" )
+
+__all__ = [ CpuInfoPlugin ]
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/instrumenters/env.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/env.py
@@ -0,0 +1,54 @@
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+
+import logging
+log = logging.getLogger( __name__ )
+
+
+class EnvFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ return ( "%s (runtime environment variable)" % key, value )
+
+
+class EnvPlugin( InstrumentPlugin ):
+ """ Instrumentation plugin capable of recording all or specific environment
+ variables for a job at runtime.
+ """
+ plugin_type = "env"
+ formatter = EnvFormatter()
+
+ def __init__( self, **kwargs ):
+ variables_str = kwargs.get( "variables", None )
+ if variables_str:
+ variables = [ v.strip() for v in variables_str.split(",") ]
+ else:
+ variables = None
+ self.variables = variables
+
+ def pre_execute_instrument( self, job_directory ):
+ """ Use env to dump all environment variables to a file.
+ """
+ return "env > '%s'" % self.__env_file( job_directory )
+
+ def post_execute_instrument( self, job_directory ):
+ return None
+
+ def job_properties( self, job_id, job_directory ):
+ """ Recover environment variables dumped out on compute server and filter
+ out specific variables if needed.
+ """
+ variables = self.variables
+
+ properties = {}
+ for line in open( self.__env_file( job_directory ) ).readlines():
+ var, value = line.split( "=", 1 )
+ if not variables or var in variables:
+ properties[ var ] = value
+
+ return properties
+
+ def __env_file( self, job_directory ):
+ return self._instrument_file_path( job_directory, "vars" )
+
+__all__ = [ EnvPlugin ]
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/instrumenters/meminfo.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/meminfo.py
@@ -0,0 +1,59 @@
+import re
+
+from galaxy import util
+
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+
+MEMINFO_LINE = re.compile(r"(\w+)\s*\:\s*(\d+) kB")
+
+# Important (non-verbose) meminfo property titles.
+MEMINFO_TITLES = {
+ "memtotal": "Total System Memory",
+ "swaptotal": "Total System Swap"
+}
+
+
+class MemInfoFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ title = MEMINFO_TITLES.get( key, key )
+ return title, util.nice_size( value * 1000 ) # kB = *1000, KB = *1024 - wikipedia
+
+
+class MemInfoPlugin( InstrumentPlugin ):
+ """ Gather information about processor configuration from /proc/cpuinfo.
+ Linux only.
+ """
+ plugin_type = "meminfo"
+ formatter = MemInfoFormatter()
+
+ def __init__( self, **kwargs ):
+ self.verbose = util.asbool( kwargs.get( "verbose", False ) )
+
+ def pre_execute_instrument( self, job_directory ):
+ return "cat /proc/meminfo > '%s'" % self.__instrument_meminfo_path( job_directory )
+
+ def job_properties( self, job_id, job_directory ):
+ properties = {}
+ with open( self.__instrument_meminfo_path( job_directory ) ) as f:
+ for line in f:
+ line = line.strip()
+ if not line: # Skip empty lines
+ continue
+ line_match = MEMINFO_LINE.match( line )
+ if not line_match:
+ continue
+ key = line_match.group( 1 ).lower()
+ # By default just grab important meminfo properties with titles
+ # defined for formatter. Grab everything in verbose mode for
+ # an arbitrary snapshot of memory at beginning of run.
+ if key in MEMINFO_TITLES or self.verbose:
+ value = long( line_match.group( 2 ) )
+ properties[ key ] = value
+ return properties
+
+ def __instrument_meminfo_path( self, job_directory ):
+ return self._instrument_file_path( job_directory, "meminfo" )
+
+__all__ = [ MemInfoPlugin ]
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/metrics/instrumenters/uname.py
--- /dev/null
+++ b/lib/galaxy/jobs/metrics/instrumenters/uname.py
@@ -0,0 +1,34 @@
+from ..instrumenters import InstrumentPlugin
+from ...metrics import formatting
+
+
+class UnameFormatter( formatting.JobMetricFormatter ):
+
+ def format( self, key, value ):
+ return "Operating System", value
+
+
+class UnamePlugin( InstrumentPlugin ):
+ """ Use uname to gather operating system information about remote system
+ job is running on. Linux only.
+ """
+ plugin_type = "uname"
+ formatter = UnameFormatter()
+
+ def __init__( self, **kwargs ):
+ self.uname_args = kwargs.get( "args", "-a" )
+
+ def pre_execute_instrument( self, job_directory ):
+ return "uname %s > '%s'" % ( self.uname_args, self.__instrument_uname_path( job_directory ) )
+
+ def job_properties( self, job_id, job_directory ):
+ properties = {}
+ with open( self.__instrument_uname_path( job_directory ) ) as f:
+ properties[ "uname" ] = f.read()
+ return properties
+
+ def __instrument_uname_path( self, job_directory ):
+ return self._instrument_file_path( job_directory, "uname" )
+
+
+__all__ = [ UnamePlugin ]
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -251,7 +251,10 @@
log.debug( 'execution of external set_meta for job %d finished' % job_wrapper.job_id )
def get_job_file(self, job_wrapper, **kwds):
+ job_metrics = job_wrapper.app.job_metrics
+ job_instrumenter = job_metrics.job_instrumenters[ job_wrapper.job_destination.id ]
options = dict(
+ job_instrumenter=job_instrumenter,
galaxy_lib=job_wrapper.galaxy_lib_dir,
env_setup_commands=job_wrapper.get_env_setup_clause(),
working_directory=os.path.abspath( job_wrapper.working_directory ),
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/runners/util/job_script/DEFAULT_JOB_FILE_TEMPLATE.sh
--- a/lib/galaxy/jobs/runners/util/job_script/DEFAULT_JOB_FILE_TEMPLATE.sh
+++ b/lib/galaxy/jobs/runners/util/job_script/DEFAULT_JOB_FILE_TEMPLATE.sh
@@ -12,6 +12,8 @@
export PYTHONPATH
fi
$env_setup_commands
+$instrument_pre_commands
cd $working_directory
$command
echo $? > $exit_code_path
+$instrument_post_commands
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/jobs/runners/util/job_script/__init__.py
--- a/lib/galaxy/jobs/runners/util/job_script/__init__.py
+++ b/lib/galaxy/jobs/runners/util/job_script/__init__.py
@@ -18,6 +18,8 @@
'headers': '',
'env_setup_commands': '',
'slots_statement': SLOTS_STATEMENT_CLUSTER_DEFAULT,
+ 'instrument_pre_commands': '',
+ 'instrument_post_commands': '',
}
@@ -47,6 +49,13 @@
"""
if any([param not in kwds for param in REQUIRED_TEMPLATE_PARAMS]):
raise Exception("Failed to create job_script, a required parameter is missing.")
+ job_instrumenter = kwds.get("job_instrumenter", None)
+ if job_instrumenter:
+ del kwds[ "job_instrumenter" ]
+ working_directory = kwds["working_directory"]
+ kwds["instrument_pre_commands"] = job_instrumenter.pre_execute_commands(working_directory) or ''
+ kwds["instrument_post_commands"] = job_instrumenter.post_execute_commands(working_directory) or ''
+
template_params = OPTIONAL_TEMPLATE_PARAMS.copy()
template_params.update(**kwds)
if not isinstance(template, Template):
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -17,6 +17,7 @@
import json
import socket
import time
+import numbers
from uuid import UUID, uuid4
from string import Template
from itertools import ifilter
@@ -87,6 +88,26 @@
return name
+class HasJobMetrics:
+
+ def _init_metrics( self ):
+ self.text_metrics = []
+ self.numeric_metrics = []
+
+ def add_metric( self, plugin, metric_name, metric_value ):
+ if isinstance( metric_value, numbers.Number ):
+ metric = self._numeric_metric( plugin, metric_name, metric_value )
+ self.numeric_metrics.append( metric )
+ else:
+ metric = self._text_metric( plugin, metric_name, metric_value )
+ self.text_metrics.append( metric )
+
+ @property
+ def metrics( self ):
+ # TODO: Make iterable, concatenate with chain
+ return self.text_metrics + self.numeric_metrics
+
+
class User( object, Dictifiable ):
use_pbkdf2 = True
"""
@@ -226,7 +247,31 @@
return Template( in_string ).safe_substitute( environment )
-class Job( object, Dictifiable ):
+class BaseJobMetric( object ):
+
+ def __init__( self, plugin, metric_name, metric_value ):
+ self.plugin = plugin
+ self.metric_name = metric_name
+ self.metric_value = metric_value
+
+
+class JobMetricText( BaseJobMetric ):
+ pass
+
+
+class JobMetricNumeric( BaseJobMetric ):
+ pass
+
+
+class TaskMetricText( BaseJobMetric ):
+ pass
+
+
+class TaskMetricNumeric( BaseJobMetric ):
+ pass
+
+
+class Job( object, HasJobMetrics, Dictifiable ):
dict_collection_visible_keys = [ 'id', 'state', 'exit_code', 'update_time', 'create_time' ]
dict_element_visible_keys = [ 'id', 'state', 'exit_code', 'update_time', 'create_time' ]
@@ -234,6 +279,9 @@
A job represents a request to run a tool given input datasets, tool
parameters, and output datasets.
"""
+ _numeric_metric = JobMetricNumeric
+ _text_metric = JobMetricText
+
states = Bunch( NEW = 'new',
UPLOAD = 'upload',
WAITING = 'waiting',
@@ -267,6 +315,7 @@
self.imported = False
self.handler = None
self.exit_code = None
+ self._init_metrics()
@property
def finished( self ):
@@ -370,6 +419,12 @@
self.info = info
def set_runner_name( self, job_runner_name ):
self.job_runner_name = job_runner_name
+
+ def get_job( self ):
+ # Added so job and task have same interface (.get_job() ) to get at
+ # underlying job object.
+ return self
+
def set_runner_external_id( self, job_runner_external_id ):
self.job_runner_external_id = job_runner_external_id
def set_post_job_actions( self, post_job_actions ):
@@ -472,10 +527,14 @@
return rval
-class Task( object ):
+
+class Task( object, HasJobMetrics ):
"""
A task represents a single component of a job.
"""
+ _numeric_metric = TaskMetricNumeric
+ _text_metric = TaskMetricText
+
states = Bunch( NEW = 'new',
WAITING = 'waiting',
QUEUED = 'queued',
@@ -498,6 +557,7 @@
self.stderr = ""
self.exit_code = None
self.prepare_input_files_cmd = prepare_files_cmd
+ self._init_metrics()
def get_param_values( self, app ):
"""
@@ -608,6 +668,7 @@
def set_prepare_input_files_cmd( self, prepare_input_files_cmd ):
self.prepare_input_files_cmd = prepare_input_files_cmd
+
class JobParameter( object ):
def __init__( self, name, value ):
self.name = name
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -470,6 +470,52 @@
Column( "archive_dir", TEXT )
)
+
+JOB_METRIC_MAX_LENGTH = 1023
+
+model.JobMetricText.table = Table(
+ "job_metric_text",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Unicode(JOB_METRIC_MAX_LENGTH), ),
+)
+
+model.TaskMetricText.table = Table(
+ "task_metric_text",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Unicode(JOB_METRIC_MAX_LENGTH), ),
+)
+
+
+model.JobMetricNumeric.table = Table(
+ "job_metric_numeric",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Numeric( 22, 7 ), ),
+)
+
+
+model.TaskMetricNumeric.table = Table(
+ "task_metric_numeric",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Numeric( 22, 7 ), ),
+)
+
+
model.GenomeIndexToolData.table = Table( "genome_index_tool_data", metadata,
Column( "id", Integer, primary_key=True ),
Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
@@ -1569,6 +1615,26 @@
job=relation( model.Job ), dataset=relation(
model.LibraryDatasetDatasetAssociation, lazy=False ) ) )
+simple_mapping(
+ model.JobMetricText,
+ job=relation( model.Job, backref="text_metrics" ),
+)
+
+simple_mapping(
+ model.TaskMetricText,
+ task=relation( model.Task, backref="text_metrics" ),
+)
+
+simple_mapping(
+ model.JobMetricNumeric,
+ job=relation( model.Job, backref="numeric_metrics" ),
+)
+
+simple_mapping(
+ model.TaskMetricNumeric,
+ task=relation( model.Task, backref="numeric_metrics" ),
+)
+
mapper( model.JobParameter, model.JobParameter.table )
mapper( model.JobExternalOutputMetadata, model.JobExternalOutputMetadata.table,
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 lib/galaxy/model/migrate/versions/0119_job_metrics.py
--- /dev/null
+++ b/lib/galaxy/model/migrate/versions/0119_job_metrics.py
@@ -0,0 +1,102 @@
+"""
+Migration script for job metric plugins.
+"""
+
+from sqlalchemy import *
+from sqlalchemy.orm import *
+from migrate import *
+from migrate.changeset import *
+
+import datetime
+now = datetime.datetime.utcnow
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData()
+
+TEXT_METRIC_MAX_LENGTH = 1023
+
+JobMetricText_table = Table(
+ "job_metric_text",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Unicode(TEXT_METRIC_MAX_LENGTH), ),
+)
+
+
+TaskMetricText_table = Table(
+ "task_metric_text",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Unicode(TEXT_METRIC_MAX_LENGTH), ),
+)
+
+
+JobMetricNumeric_table = Table(
+ "job_metric_numeric",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Numeric( 22, 7 ), ),
+)
+
+
+TaskMetricNumeric_table = Table(
+ "task_metric_numeric",
+ metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "task_id", Integer, ForeignKey( "task.id" ), index=True ),
+ Column( "plugin", Unicode(255), ),
+ Column( "metric_name", Unicode(255), ),
+ Column( "metric_value", Numeric( 22, 7 ), ),
+)
+
+
+TABLES = [
+ JobMetricText_table,
+ TaskMetricText_table,
+ JobMetricNumeric_table,
+ TaskMetricNumeric_table,
+]
+
+
+def upgrade( migrate_engine ):
+ metadata.bind = migrate_engine
+ print __doc__
+ metadata.reflect()
+
+ for table in TABLES:
+ __create(table)
+
+
+def downgrade(migrate_engine):
+ metadata.bind = migrate_engine
+ metadata.reflect()
+
+ for table in TABLES:
+ __drop(table)
+
+
+def __create(table):
+ try:
+ table.create()
+ except Exception as e:
+ print str(e)
+ log.debug("Creating %s table failed: %s" % (table.name, str( e ) ) )
+
+
+def __drop(table):
+ try:
+ table.drop()
+ except Exception as e:
+ print str(e)
+ log.debug("Dropping %s table failed: %s" % (table.name, str( e ) ) )
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 templates/show_params.mako
--- a/templates/show_params.mako
+++ b/templates/show_params.mako
@@ -126,6 +126,13 @@
%if job and job.command_line and trans.user_is_admin():
<tr><td>Job Command-Line:</td><td>${ job.command_line | h }</td></tr>
%endif
+ %if job and trans.user_is_admin():
+ <% job_metrics = trans.app.job_metrics %>
+ %for metric in job.metrics:
+ <% metric_title, metric_value = job_metrics.format( metric.plugin, metric.metric_name, metric.metric_value ) %>
+ <tr><td>${ metric_title | h }</td><td>${ metric_value | h }</td></tr>
+ %endfor
+ %endif
</table><br />
diff -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 -r b24137a3a980cf913ba1b5ae023623fb416b54e6 test/unit/test_galaxy_mapping.py
--- a/test/unit/test_galaxy_mapping.py
+++ b/test/unit/test_galaxy_mapping.py
@@ -210,6 +210,46 @@
assert hist1.name == "History 2b"
# gvk TODO need to ad test for GalaxySessions, but not yet sure what they should look like.
+ def test_jobs( self ):
+ model = self.model
+ u = model.User( email="jobtest(a)foo.bar.baz", password="password" )
+ job = model.Job()
+ job.user = u
+ job.tool_id = "cat1"
+
+ self.persist( u, job )
+
+ loaded_job = model.session.query( model.Job ).filter( model.Job.user == u ).first()
+ assert loaded_job.tool_id == "cat1"
+
+ def test_job_metrics( self ):
+ model = self.model
+ u = model.User( email="jobtest(a)foo.bar.baz", password="password" )
+ job = model.Job()
+ job.user = u
+ job.tool_id = "cat1"
+
+ job.add_metric( "gx", "galaxy_slots", 5 )
+ job.add_metric( "system", "system_name", "localhost" )
+
+ self.persist( u, job )
+
+ task = model.Task( job=job, working_directory="/tmp", prepare_files_cmd="split.sh" )
+ task.add_metric( "gx", "galaxy_slots", 5 )
+ task.add_metric( "system", "system_name", "localhost" )
+ self.persist( task )
+
+ def test_tasks( self ):
+ model = self.model
+ u = model.User( email="jobtest(a)foo.bar.baz", password="password" )
+ job = model.Job()
+ task = model.Task( job=job, working_directory="/tmp", prepare_files_cmd="split.sh" )
+ job.user = u
+ self.persist( u, job, task )
+
+ loaded_task = model.session.query( model.Task ).filter( model.Task.job == job ).first()
+ assert loaded_task.prepare_input_files_cmd == "split.sh"
+
def test_history_contents( self ):
model = self.model
u = model.User( email="contents(a)foo.bar.baz", password="password" )
https://bitbucket.org/galaxy/galaxy-central/commits/e58259b27d22/
Changeset: e58259b27d22
User: jmchilton
Date: 2014-04-23 04:25:04
Summary: Job metrics - ensure all text values are unicode before persisting.
Gets rid of the following warning: "SAWarning: Unicode type received non-unicode bind param value.".
Affected #: 1 file
diff -r b24137a3a980cf913ba1b5ae023623fb416b54e6 -r e58259b27d229b5bdfe3e70772cef94804dda03c lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -95,10 +95,18 @@
self.numeric_metrics = []
def add_metric( self, plugin, metric_name, metric_value ):
+ if isinstance( plugin, str ):
+ plugin = unicode( plugin, 'utf-8' )
+
+ if isinstance( metric_name, str ):
+ metric_name = unicode( metric_name, 'utf-8' )
+
if isinstance( metric_value, numbers.Number ):
metric = self._numeric_metric( plugin, metric_name, metric_value )
self.numeric_metrics.append( metric )
else:
+ if isinstance( metric_value, str ):
+ metric_value = unicode( metric_value, 'utf-8' )
metric = self._text_metric( plugin, metric_name, metric_value )
self.text_metrics.append( metric )
https://bitbucket.org/galaxy/galaxy-central/commits/26575805a77b/
Changeset: 26575805a77b
User: jmchilton
Date: 2014-04-23 04:40:08
Summary: Fix test_job_configuration.py for job metrics PR.
Still need to go in and actually write some tests for the nested parsing that can happen.
Affected #: 1 file
diff -r e58259b27d229b5bdfe3e70772cef94804dda03c -r 26575805a77b61e893a6601401499c970f156a0c test/unit/jobs/test_job_configuration.py
--- a/test/unit/jobs/test_job_configuration.py
+++ b/test/unit/jobs/test_job_configuration.py
@@ -22,7 +22,7 @@
use_tasked_jobs=False,
)
self.__write_config_from( SIMPLE_JOB_CONF )
- self.app = bunch.Bunch( config=self.config )
+ self.app = bunch.Bunch( config=self.config, job_metrics=MockJobMetrics() )
self.__job_configuration = None
def tearDown( self ):
@@ -111,6 +111,8 @@
assert limits.concurrent_jobs[ "longjobs" ] == 1
assert limits.walltime_delta == datetime.timedelta( 0, 0, 0, 0, 0, 24 )
+ # TODO: Add job metrics parsing test.
+
@property
def job_config( self ):
if not self.__job_configuration:
@@ -126,3 +128,12 @@
def __write_config( self, contents ):
with open( os.path.join( self.temp_directory, "job_conf.xml" ), "w" ) as f:
f.write( contents )
+
+
+class MockJobMetrics( object ):
+
+ def __init__( self ):
+ pass
+
+ def set_destination_conf_element( self, id, element ):
+ pass
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: Unit tests for job conf parsing.
by commits-noreply@bitbucket.org 23 Apr '14
by commits-noreply@bitbucket.org 23 Apr '14
23 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/bc3fd881b07f/
Changeset: bc3fd881b07f
User: jmchilton
Date: 2014-04-23 03:50:18
Summary: Unit tests for job conf parsing.
Update advanced job sample to fix "bugs" discovered during testing limit parsing.
Affected #: 2 files
diff -r 68b49fdc63e2445ee2124e0185373d964bc64bc8 -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 job_conf.xml.sample_advanced
--- a/job_conf.xml.sample_advanced
+++ b/job_conf.xml.sample_advanced
@@ -159,8 +159,8 @@
<limits><!-- Certain limits can be defined. --><limit type="registered_user_concurrent_jobs">2</limit>
- <limit type="unregistered_user_concurrent_jobs">1</limit>
- <limit type="job_walltime">24:00:00</limit>
+ <limit type="anonymous_user_concurrent_jobs">1</limit>
+ <limit type="walltime">24:00:00</limit><limit type="concurrent_jobs" id="local">1</limit><limit type="concurrent_jobs" tag="mycluster">2</limit><limit type="concurrent_jobs" tag="longjobs">1</limit>
diff -r 68b49fdc63e2445ee2124e0185373d964bc64bc8 -r bc3fd881b07f5904ccad6d2d187e7d8e3c506652 test/unit/jobs/test_job_configuration.py
--- /dev/null
+++ b/test/unit/jobs/test_job_configuration.py
@@ -0,0 +1,128 @@
+import datetime
+import os
+import shutil
+import tempfile
+import unittest
+
+from galaxy.util import bunch
+from galaxy.jobs import JobConfiguration
+
+# File would be slightly more readable if contents were embedded directly, but
+# there are advantages to testing the documentation/examples.
+SIMPLE_JOB_CONF = os.path.join( os.path.dirname( __file__ ), "..", "..", "..", "job_conf.xml.sample_basic" )
+ADVANCED_JOB_CONF = os.path.join( os.path.dirname( __file__ ), "..", "..", "..", "job_conf.xml.sample_advanced" )
+
+
+class JobConfXmlParserTestCase( unittest.TestCase ):
+
+ def setUp( self ):
+ self.temp_directory = tempfile.mkdtemp()
+ self.config = bunch.Bunch(
+ job_config_file=os.path.join( self.temp_directory, "job_conf.xml" ),
+ use_tasked_jobs=False,
+ )
+ self.__write_config_from( SIMPLE_JOB_CONF )
+ self.app = bunch.Bunch( config=self.config )
+ self.__job_configuration = None
+
+ def tearDown( self ):
+ shutil.rmtree( self.temp_directory )
+
+ def test_load_simple_runner( self ):
+ runner_plugin = self.job_config.runner_plugins[ 0 ]
+ assert runner_plugin[ "id" ] == "local"
+ assert runner_plugin[ "load" ] == "galaxy.jobs.runners.local:LocalJobRunner"
+ assert runner_plugin[ "workers" ] == 4
+
+ def test_tasks_disabled( self ):
+ assert len( [ r for r in self.job_config.runner_plugins if r[ "id" ] == "tasks" ] ) == 0
+
+ def test_configuration_of_tasks( self ):
+ self.config.use_tasked_jobs = True
+ self.config.local_task_queue_workers = 5
+ task_runners = [ r for r in self.job_config.runner_plugins if r[ "id" ] == "tasks" ]
+ assert len( task_runners ) == 1
+ assert task_runners[ 0 ][ "workers" ] == 5
+
+ def test_load_simple_handler( self ):
+ main_handler = self.job_config.handlers[ "main" ]
+ assert main_handler[ 0 ] == "main"
+
+ def test_if_one_handler_implicit_default( self ):
+ assert self.job_config.default_handler_id == "main"
+
+ def test_explicit_handler_default( self ):
+ self.__with_advanced_config()
+ assert self.job_config.default_handler_id == "handlers"
+
+ def test_handler_tag_parsing( self ):
+ self.__with_advanced_config()
+ assert "handler0" in self.job_config.handlers[ "handlers" ]
+ assert "handler1" in self.job_config.handlers[ "handlers" ]
+
+ def test_load_simple_destination( self ):
+ local_dest = self.job_config.destinations[ "local" ][ 0 ]
+ assert local_dest.id == "local"
+ assert local_dest.runner == "local"
+
+ def test_load_destination_params( self ):
+ self.__with_advanced_config()
+ pbs_dest = self.job_config.destinations[ "pbs_longjobs" ][ 0 ]
+ assert pbs_dest.id == "pbs_longjobs"
+ assert pbs_dest.runner == "pbs"
+ dest_params = pbs_dest.params
+ assert dest_params[ "Resource_List" ] == "walltime=72:00:00"
+
+ def test_destination_tags( self ):
+ self.__with_advanced_config()
+ longjob_dests = self.job_config.destinations[ "longjobs" ]
+ assert len( longjob_dests ) == 2
+ assert longjob_dests[ 0 ].id == "pbs_longjobs"
+ assert longjob_dests[ 1 ].id == "remote_cluster"
+
+ def test_load_tool( self ):
+ self.__with_advanced_config()
+ baz_tool = self.job_config.tools[ "baz" ][ 0 ]
+ assert baz_tool.id == "baz"
+ assert baz_tool.handler == "special_handlers"
+ assert baz_tool.destination == "bigmem"
+
+ def test_load_tool_params( self ):
+ self.__with_advanced_config()
+ foo_tool = self.job_config.tools[ "foo" ][ 0 ]
+ assert foo_tool.params[ "source" ] == "trackster"
+
+ def test_default_limits( self ):
+ limits = self.job_config.limits
+ assert limits.registered_user_concurrent_jobs is None
+ assert limits.anonymous_user_concurrent_jobs is None
+ assert limits.walltime is None
+ assert limits.walltime_delta is None
+ assert limits.output_size is None
+ assert limits.concurrent_jobs == {}
+
+ def test_limit_overrides( self ):
+ self.__with_advanced_config()
+ limits = self.job_config.limits
+ assert limits.registered_user_concurrent_jobs == 2
+ assert limits.anonymous_user_concurrent_jobs == 1
+ assert limits.concurrent_jobs[ "local" ] == 1
+ assert limits.concurrent_jobs[ "mycluster" ] == 2
+ assert limits.concurrent_jobs[ "longjobs" ] == 1
+ assert limits.walltime_delta == datetime.timedelta( 0, 0, 0, 0, 0, 24 )
+
+ @property
+ def job_config( self ):
+ if not self.__job_configuration:
+ self.__job_configuration = JobConfiguration( self.app )
+ return self.__job_configuration
+
+ def __with_advanced_config( self ):
+ self.__write_config_from( ADVANCED_JOB_CONF )
+
+ def __write_config_from( self, path ):
+ self.__write_config( open( path, "r" ).read() )
+
+ def __write_config( self, contents ):
+ with open( os.path.join( self.temp_directory, "job_conf.xml" ), "w" ) as f:
+ f.write( contents )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: History/HDA UI: pass allow_user_dataset_purge from Galaxy.config to history-panel and hda-edit and respect by not rendering purge controls if false
by commits-noreply@bitbucket.org 22 Apr '14
by commits-noreply@bitbucket.org 22 Apr '14
22 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/68b49fdc63e2/
Changeset: 68b49fdc63e2
User: carlfeberhard
Date: 2014-04-22 19:16:50
Summary: History/HDA UI: pass allow_user_dataset_purge from Galaxy.config to history-panel and hda-edit and respect by not rendering purge controls if false
Affected #: 8 files
diff -r 2342d31a1f5cf906f1aecb0689fe64da384cf940 -r 68b49fdc63e2445ee2124e0185373d964bc64bc8 static/scripts/mvc/dataset/hda-edit.js
--- a/static/scripts/mvc/dataset/hda-edit.js
+++ b/static/scripts/mvc/dataset/hda-edit.js
@@ -36,6 +36,9 @@
this._render_rerunButton
];
+ /** allow user purge of dataset files? */
+ this.purgeAllowed = attributes.purgeAllowed || false;
+
//TODO: move to HiddenUntilActivatedViewMixin
/** should the tags editor be shown or hidden initially? */
this.tagsEditorShown = attributes.tagsEditorShown || false;
@@ -291,10 +294,15 @@
};
$newRender.find( '.dataset-deleted-msg' ).append([
- br, link( _l( 'Undelete it' ), 'dataset-undelete' ), p,
- br, link( _l( 'Permanently remove it from disk' ), 'dataset-purge' ), p
+ br, link( _l( 'Undelete it' ), 'dataset-undelete' ), p
].join( '' ));
+ if( this.purgeAllowed ){
+ $newRender.find( '.dataset-deleted-msg' ).append([
+ br, link( _l( 'Permanently remove it from disk' ), 'dataset-purge' ), p
+ ].join( '' ));
+ }
+
$newRender.find( '.dataset-hidden-msg' ).append([
br, link( _l( 'Unhide it' ), 'dataset-unhide' ), p
].join( '' ));
diff -r 2342d31a1f5cf906f1aecb0689fe64da384cf940 -r 68b49fdc63e2445ee2124e0185373d964bc64bc8 static/scripts/mvc/history/history-panel.js
--- a/static/scripts/mvc/history/history-panel.js
+++ b/static/scripts/mvc/history/history-panel.js
@@ -50,6 +50,9 @@
/** editor for annotations - sub-view */
this.annotationEditor = null;
+ /** allow user purge of dataset files? */
+ this.purgeAllowed = attributes.purgeAllowed || false;
+
// states/modes the panel can be in
/** is the panel currently showing the dataset selection controls? */
this.selecting = attributes.selecting || false;
@@ -197,41 +200,40 @@
* ajax calls made for multiple datasets are queued
*/
_setUpDatasetActionsPopup : function( $where ){
- var panel = this;
- ( new PopupMenu( $where.find( '.history-dataset-action-popup-btn' ), [
- {
- html: _l( 'Hide datasets' ), func: function(){
- var action = hdaModel.HistoryDatasetAssociation.prototype.hide;
- panel.getSelectedHdaCollection().ajaxQueue( action );
+ var panel = this,
+ actions = [
+ { html: _l( 'Hide datasets' ), func: function(){
+ var action = hdaModel.HistoryDatasetAssociation.prototype.hide;
+ panel.getSelectedHdaCollection().ajaxQueue( action );
+ }
+ },
+ { html: _l( 'Unhide datasets' ), func: function(){
+ var action = hdaModel.HistoryDatasetAssociation.prototype.unhide;
+ panel.getSelectedHdaCollection().ajaxQueue( action );
+ }
+ },
+ { html: _l( 'Delete datasets' ), func: function(){
+ var action = hdaModel.HistoryDatasetAssociation.prototype['delete'];
+ panel.getSelectedHdaCollection().ajaxQueue( action );
+ }
+ },
+ { html: _l( 'Undelete datasets' ), func: function(){
+ var action = hdaModel.HistoryDatasetAssociation.prototype.undelete;
+ panel.getSelectedHdaCollection().ajaxQueue( action );
+ }
}
- },
- {
- html: _l( 'Unhide datasets' ), func: function(){
- var action = hdaModel.HistoryDatasetAssociation.prototype.unhide;
- panel.getSelectedHdaCollection().ajaxQueue( action );
- }
- },
- {
- html: _l( 'Delete datasets' ), func: function(){
- var action = hdaModel.HistoryDatasetAssociation.prototype['delete'];
- panel.getSelectedHdaCollection().ajaxQueue( action );
- }
- },
- {
- html: _l( 'Undelete datasets' ), func: function(){
- var action = hdaModel.HistoryDatasetAssociation.prototype.undelete;
- panel.getSelectedHdaCollection().ajaxQueue( action );
- }
- },
- {
+ ];
+ if( panel.purgeAllowed ){
+ actions.push({
html: _l( 'Permanently delete datasets' ), func: function(){
if( confirm( _l( 'This will permanently remove the data in your datasets. Are you sure?' ) ) ){
var action = hdaModel.HistoryDatasetAssociation.prototype.purge;
panel.getSelectedHdaCollection().ajaxQueue( action );
}
}
- }
- ]));
+ });
+ }
+ return new PopupMenu( $where.find( '.history-dataset-action-popup-btn' ), actions );
},
// ------------------------------------------------------------------------ hda sub-views
@@ -265,6 +267,7 @@
expanded : this.storage.get( 'expandedHdas' )[ hdaId ],
//draggable : true,
selectable : this.selecting,
+ purgeAllowed : this.purgeAllowed,
hasUser : this.model.ownedByCurrUser(),
logger : this.logger,
tagsEditorShown : ( this.tagsEditor && !this.tagsEditor.hidden ),
diff -r 2342d31a1f5cf906f1aecb0689fe64da384cf940 -r 68b49fdc63e2445ee2124e0185373d964bc64bc8 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 @@
-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()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes}});var InputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes;this.multiple=a.multiple},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},_inputFilled:function(){return !(this.connectors.length<1||this.multiple)},attachable: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){if(f[b]=="input"||issubtype(f[b],this.datatypes[c])){return true}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},redraw:function(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};if(!this.handle1||!this.handle2){return}var h=n(this.handle1.element)+5;var g=i(this.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*f;this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canvas.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},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(c){var b=this;nodeView=b.nodeView;this.tool_state=c.tool_state;this.form_html=c.form_html;this.tool_errors=c.tool_errors;this.annotation=c.annotation;var d=$.parseJSON(c.post_job_actions);this.post_job_actions=d?d:{};b.nodeView.renderToolErrors();var f=nodeView.$("div.inputs");var a=nodeView.newInputsDiv();$.each(c.data_inputs,function(h,g){b.nodeView.replaceDataInput(g,a)});f.replaceWith(a);f.find("div.input-data-row > .terminal").each(function(){this.terminal.destroy()});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)},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(c){var d=new InputTerminalView({node:this.node,input:c});var f=d.el;var b=new DataInputView({terminalElement:f,input:c,nodeView:this,});var a=b.$el;var f=b.terminalElement;this.$(".inputs").append(a.prepend(f))},replaceDataInput:function(c,f){var g=new InputTerminalView({node:this.node,input:c});var d=g.el;this.$("div[name='"+c.name+"']").each(function(){$(this).find(".input-terminal").each(function(){var i=this.terminal.connectors[0];if(i){var h=d.terminal;if(i.handle1&&!h.attachable(i.handle1)){i.destroy()}else{h.connectors[0]=i;i.handle2=h}}});$(this).remove()});var b=new DataInputView({terminalElement:d,input:c,nodeView:this,skipResize:true,});var a=b.$el;f.append(a.prepend(d))},addDataOutput:function(a){var c=new OutputTerminalView({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(b.terminalElement))}});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 d=this.nodeView.node;if(a.extensions.indexOf("input")<0){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(d.type=="tool"){var f=new OutputCalloutView({label:b,output:a,node:d,});this.$el.append(f.el);this.$el.hover(function(){f.hoverImage()},function(){f.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 InputTerminalView=Backbone.View.extend({className:"terminal input-terminal",initialize:function(d){var h=d.node;var b=d.input;var c=b.name;var g=b.extensions;var a=b.multiple;var f=this.el.terminal=new InputTerminal({element:this.el,datatypes:g,multiple:a});f.node=h;f.name=c;h.input_terminals[c]=f},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 OutputTerminalView=Backbone.View.extend({className:"terminal output-terminal",initialize:function(c){var i=c.node;var a=c.output;var b=a.name;var h=a.extensions;var g=this.el;var f=g;var d=g.terminal=new OutputTerminal({element:g,datatypes:h});d.node=i;d.name=b;i.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()}});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
+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()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes}});var InputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.update(a.input)},update:function(a){this.datatypes=a.extensions;this.multiple=a.multiple},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},_inputFilled:function(){return !(this.connectors.length<1||this.multiple)},attachable: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){if(f[b]=="input"||issubtype(f[b],this.datatypes[c])){return true}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},redraw:function(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};if(!this.handle1||!this.handle2){return}var h=n(this.handle1.element)+5;var g=i(this.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*f;this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canvas.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},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;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;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={}},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(d,a){var f=true;if(!a){a=this.$(".inputs");f=false}var h=this.terminalViews[d.name];if(!h){h=new InputTerminalView({node:this.node,input:d})}else{var g=h.el.terminal;g.update(d);_.each(g.connectors,function(j){if(j.handle1&&!g.attachable(j.handle1)){j.destroy()}})}this.terminalViews[d.name]=h;var i=h.el;var c=new DataInputView({terminalElement:i,input:d,nodeView:this,skipResize:f});var b=c.$el;var i=c.terminalElement;a.append(b.prepend(i));return h},addDataOutput:function(a){var c=new OutputTerminalView({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(b.terminalElement))}});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 d=this.nodeView.node;if(a.extensions.indexOf("input")<0){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(d.type=="tool"){var f=new OutputCalloutView({label:b,output:a,node:d,});this.$el.append(f.el);this.$el.hover(function(){f.hoverImage()},function(){f.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 InputTerminalView=Backbone.View.extend({className:"terminal input-terminal",initialize:function(c){var f=c.node;var a=c.input;var b=a.name;var d=this.el.terminal=new InputTerminal({element:this.el,input:a});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 OutputTerminalView=Backbone.View.extend({className:"terminal output-terminal",initialize:function(c){var i=c.node;var a=c.output;var b=a.name;var h=a.extensions;var g=this.el;var f=g;var d=g.terminal=new OutputTerminal({element:g,datatypes:h});d.node=i;d.name=b;i.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()}});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 2342d31a1f5cf906f1aecb0689fe64da384cf940 -r 68b49fdc63e2445ee2124e0185373d964bc64bc8 static/scripts/packed/mvc/dataset/hda-edit.js
--- a/static/scripts/packed/mvc/dataset/hda-edit.js
+++ b/static/scripts/packed/mvc/dataset/hda-edit.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/tags","mvc/annotations","utils/localization"],function(g,b,a,e,f){var h=b.HDABaseView.extend({initialize:function(i){b.HDABaseView.prototype.initialize.call(this,i);this.hasUser=i.hasUser;this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton,this._render_rerunButton];this.tagsEditorShown=i.tagsEditorShown||false;this.annotationEditorShown=i.annotationEditorShown||false},_render_titleButtons:function(){return b.HDABaseView.prototype._render_titleButtons.call(this).concat([this._render_editButton(),this._render_deleteButton()])},_render_editButton:function(){if((this.model.get("state")===g.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===g.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var k=this.model.get("purged"),i=this.model.get("deleted"),j={title:f("Edit attributes"),href:this.urls.edit,target:this.linkTarget,classes:"dataset-edit"};if(i||k){j.disabled=true;if(k){j.title=f("Cannot edit attributes of datasets removed from disk")}else{if(i){j.title=f("Undelete dataset to edit attributes")}}}else{if(this.model.get("state")===g.HistoryDatasetAssociation.STATES.UPLOAD){j.disabled=true;j.title=f("This dataset must finish uploading before it can be edited")}else{if(this.model.get("state")===g.HistoryDatasetAssociation.STATES.NEW){j.disabled=true;j.title=f("This dataset is not yet editable")}}}j.faIcon="fa-pencil";return faIconButton(j)},_render_deleteButton:function(){if((this.model.get("state")===g.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var i=this,j={title:f("Delete"),classes:"dataset-delete",onclick:function(){i.$el.find(".icon-btn.dataset-delete").trigger("mouseout");i.model["delete"]()}};if(this.model.get("deleted")||this.model.get("purged")){j={title:f("Dataset is already deleted"),disabled:true}}j.faIcon="fa-times";return faIconButton(j)},_render_errButton:function(){if(this.model.get("state")!==g.HistoryDatasetAssociation.STATES.ERROR){return null}return faIconButton({title:f("View or report this error"),href:this.urls.report_error,classes:"dataset-report-error-btn",target:this.linkTarget,faIcon:"fa-bug"})},_render_rerunButton:function(){return faIconButton({title:f("Run this job again"),href:this.urls.rerun,classes:"dataset-rerun-btn",target:this.linkTarget,faIcon:"fa-refresh"})},_render_visualizationsButton:function(){var i=this.model.get("visualizations");if((!this.hasUser)||(!this.model.hasData())||(_.isEmpty(i))){return null}if(_.isObject(i[0])){return this._render_visualizationsFrameworkButton(i)}if(!this.urls.visualization){return null}var k=this.model.get("dbkey"),o=this.urls.visualization,l={},p={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(k){p.dbkey=k}var j=faIconButton({title:f("Visualize"),classes:"dataset-visualize-btn",faIcon:"fa-bar-chart-o"});function m(q){if(q==="trackster"){return c(o,p,k)}return function(){Galaxy.frame.add({title:"Visualization",type:"url",content:o+"/"+q+"?"+$.param(p)})}}function n(q){return q.charAt(0).toUpperCase()+q.slice(1)}if(i.length===1){j.attr("data-original-title",f("Visualize in")+" "+f(n(i[0])));j.click(m(i[0]))}else{_.each(i,function(q){l[f(n(q))]=m(q)});make_popupmenu(j,l)}return j},_render_visualizationsFrameworkButton:function(i){if(!(this.model.hasData())||!(i&&!_.isEmpty(i))){return null}var k=faIconButton({title:f("Visualize"),classes:"dataset-visualize-btn",faIcon:"fa-bar-chart-o"});if(i.length===1){var j=i[0];k.attr("data-original-title",f("Visualize in")+" "+j.html);k.attr("href",j.href)}else{var l=[];_.each(i,function(m){m.func=function(n){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Visualization",type:"url",content:m.href});n.preventDefault();return false}return true};l.push(m);return false});PopupMenu.create(k,l)}return k},_buildNewRender:function(){var i=b.HDABaseView.prototype._buildNewRender.call(this);var j="<br />",l=".",k=function(m,n){return['<a href="javascript:void(0)" class="',n,'">',m,"</a>"].join("")};i.find(".dataset-deleted-msg").append([j,k(f("Undelete it"),"dataset-undelete"),l,j,k(f("Permanently remove it from disk"),"dataset-purge"),l].join(""));i.find(".dataset-hidden-msg").append([j,k(f("Unhide it"),"dataset-unhide"),l].join(""));return i},_render_body_failed_metadata:function(){var j=$("<a/>").attr({href:this.urls.edit,target:this.linkTarget}).text(f("set it manually or retry auto-detection")),i=$("<span/>").text(". "+f("You may be able to")+" ").append(j),k=b.HDABaseView.prototype._render_body_failed_metadata.call(this);k.find(".warningmessagesmall strong").append(i);return k},_render_body_error:function(){var i=b.HDABaseView.prototype._render_body_error.call(this);i.find(".dataset-actions .left").prepend(this._render_errButton());return i},_render_body_ok:function(){var i=b.HDABaseView.prototype._render_body_ok.call(this);if(this.model.isDeletedOrPurged()){return i}this.makeDbkeyEditLink(i);if(this.hasUser){i.find(".dataset-actions .left").append(this._render_visualizationsButton());this._renderTags(i);this._renderAnnotation(i)}return i},_renderTags:function(i){var j=this;this.tagsEditor=new a.TagsEditor({model:this.model,el:i.find(".tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){j.tagsEditorShown=true},onhide:function(){j.tagsEditorShown=false},$activator:faIconButton({title:f("Edit dataset tags"),classes:"dataset-tag-btn",faIcon:"fa-tags"}).appendTo(i.find(".dataset-actions .right"))});if(this.tagsEditorShown){this.tagsEditor.toggle(true)}},_renderAnnotation:function(i){var j=this;this.annotationEditor=new e.AnnotationEditor({model:this.model,el:i.find(".annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){j.annotationEditorShown=true},onhide:function(){j.annotationEditorShown=false},$activator:faIconButton({title:f("Edit dataset annotation"),classes:"dataset-annotate-btn",faIcon:"fa-comment"}).appendTo(i.find(".dataset-actions .right"))});if(this.annotationEditorShown){this.annotationEditor.toggle(true)}},makeDbkeyEditLink:function(j){if(this.model.get("metadata_dbkey")==="?"&&!this.model.isDeletedOrPurged()){var i=$('<a class="value">?</a>').attr("href",this.urls.edit).attr("target",this.linkTarget);j.find(".dataset-dbkey .value").replaceWith(i)}},events:_.extend(_.clone(b.HDABaseView.prototype.events),{"click .dataset-undelete":function(i){this.model.undelete();return false},"click .dataset-unhide":function(i){this.model.unhide();return false},"click .dataset-purge":"confirmPurge"}),confirmPurge:function d(i){this.model.purge();return false},toString:function(){var i=(this.model)?(this.model+""):("(no model)");return"HDAView("+i+")"}});function c(i,k,j){return function(){var l={};if(j){l["f-dbkey"]=j}$.ajax({url:i+"/list_tracks?"+$.param(l),dataType:"html",error:function(){alert(("Could not add this dataset to browser")+".")},success:function(m){var n=window.parent;n.Galaxy.modal.show({title:"View Data in a New or Saved Visualization",buttons:{Cancel:function(){n.Galaxy.modal.hide()},"View in saved visualization":function(){n.Galaxy.modal.show({title:"Add Data to Saved Visualization",body:m,buttons:{Cancel:function(){n.Galaxy.modal.hide()},"Add to visualization":function(){$(n.document).find("input[name=id]:checked").each(function(){n.Galaxy.modal.hide();var o=$(this).val();k.id=o;n.Galaxy.frame.add({title:"Trackster",type:"url",content:i+"/trackster?"+$.param(k)})})}}})},"View in new visualization":function(){n.Galaxy.modal.hide();var o=i+"/trackster?"+$.param(k);n.Galaxy.frame.add({title:"Trackster",type:"url",content:o})}}})}});return false}}return{HDAEditView:h}});
\ No newline at end of file
+define(["mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/tags","mvc/annotations","utils/localization"],function(g,b,a,e,f){var h=b.HDABaseView.extend({initialize:function(i){b.HDABaseView.prototype.initialize.call(this,i);this.hasUser=i.hasUser;this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton,this._render_rerunButton];this.purgeAllowed=i.purgeAllowed||false;this.tagsEditorShown=i.tagsEditorShown||false;this.annotationEditorShown=i.annotationEditorShown||false},_render_titleButtons:function(){return b.HDABaseView.prototype._render_titleButtons.call(this).concat([this._render_editButton(),this._render_deleteButton()])},_render_editButton:function(){if((this.model.get("state")===g.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===g.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var k=this.model.get("purged"),i=this.model.get("deleted"),j={title:f("Edit attributes"),href:this.urls.edit,target:this.linkTarget,classes:"dataset-edit"};if(i||k){j.disabled=true;if(k){j.title=f("Cannot edit attributes of datasets removed from disk")}else{if(i){j.title=f("Undelete dataset to edit attributes")}}}else{if(this.model.get("state")===g.HistoryDatasetAssociation.STATES.UPLOAD){j.disabled=true;j.title=f("This dataset must finish uploading before it can be edited")}else{if(this.model.get("state")===g.HistoryDatasetAssociation.STATES.NEW){j.disabled=true;j.title=f("This dataset is not yet editable")}}}j.faIcon="fa-pencil";return faIconButton(j)},_render_deleteButton:function(){if((this.model.get("state")===g.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var i=this,j={title:f("Delete"),classes:"dataset-delete",onclick:function(){i.$el.find(".icon-btn.dataset-delete").trigger("mouseout");i.model["delete"]()}};if(this.model.get("deleted")||this.model.get("purged")){j={title:f("Dataset is already deleted"),disabled:true}}j.faIcon="fa-times";return faIconButton(j)},_render_errButton:function(){if(this.model.get("state")!==g.HistoryDatasetAssociation.STATES.ERROR){return null}return faIconButton({title:f("View or report this error"),href:this.urls.report_error,classes:"dataset-report-error-btn",target:this.linkTarget,faIcon:"fa-bug"})},_render_rerunButton:function(){return faIconButton({title:f("Run this job again"),href:this.urls.rerun,classes:"dataset-rerun-btn",target:this.linkTarget,faIcon:"fa-refresh"})},_render_visualizationsButton:function(){var i=this.model.get("visualizations");if((!this.hasUser)||(!this.model.hasData())||(_.isEmpty(i))){return null}if(_.isObject(i[0])){return this._render_visualizationsFrameworkButton(i)}if(!this.urls.visualization){return null}var k=this.model.get("dbkey"),o=this.urls.visualization,l={},p={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(k){p.dbkey=k}var j=faIconButton({title:f("Visualize"),classes:"dataset-visualize-btn",faIcon:"fa-bar-chart-o"});function m(q){if(q==="trackster"){return c(o,p,k)}return function(){Galaxy.frame.add({title:"Visualization",type:"url",content:o+"/"+q+"?"+$.param(p)})}}function n(q){return q.charAt(0).toUpperCase()+q.slice(1)}if(i.length===1){j.attr("data-original-title",f("Visualize in")+" "+f(n(i[0])));j.click(m(i[0]))}else{_.each(i,function(q){l[f(n(q))]=m(q)});make_popupmenu(j,l)}return j},_render_visualizationsFrameworkButton:function(i){if(!(this.model.hasData())||!(i&&!_.isEmpty(i))){return null}var k=faIconButton({title:f("Visualize"),classes:"dataset-visualize-btn",faIcon:"fa-bar-chart-o"});if(i.length===1){var j=i[0];k.attr("data-original-title",f("Visualize in")+" "+j.html);k.attr("href",j.href)}else{var l=[];_.each(i,function(m){m.func=function(n){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Visualization",type:"url",content:m.href});n.preventDefault();return false}return true};l.push(m);return false});PopupMenu.create(k,l)}return k},_buildNewRender:function(){var i=b.HDABaseView.prototype._buildNewRender.call(this);var j="<br />",l=".",k=function(m,n){return['<a href="javascript:void(0)" class="',n,'">',m,"</a>"].join("")};i.find(".dataset-deleted-msg").append([j,k(f("Undelete it"),"dataset-undelete"),l].join(""));if(this.purgeAllowed){i.find(".dataset-deleted-msg").append([j,k(f("Permanently remove it from disk"),"dataset-purge"),l].join(""))}i.find(".dataset-hidden-msg").append([j,k(f("Unhide it"),"dataset-unhide"),l].join(""));return i},_render_body_failed_metadata:function(){var j=$("<a/>").attr({href:this.urls.edit,target:this.linkTarget}).text(f("set it manually or retry auto-detection")),i=$("<span/>").text(". "+f("You may be able to")+" ").append(j),k=b.HDABaseView.prototype._render_body_failed_metadata.call(this);k.find(".warningmessagesmall strong").append(i);return k},_render_body_error:function(){var i=b.HDABaseView.prototype._render_body_error.call(this);i.find(".dataset-actions .left").prepend(this._render_errButton());return i},_render_body_ok:function(){var i=b.HDABaseView.prototype._render_body_ok.call(this);if(this.model.isDeletedOrPurged()){return i}this.makeDbkeyEditLink(i);if(this.hasUser){i.find(".dataset-actions .left").append(this._render_visualizationsButton());this._renderTags(i);this._renderAnnotation(i)}return i},_renderTags:function(i){var j=this;this.tagsEditor=new a.TagsEditor({model:this.model,el:i.find(".tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){j.tagsEditorShown=true},onhide:function(){j.tagsEditorShown=false},$activator:faIconButton({title:f("Edit dataset tags"),classes:"dataset-tag-btn",faIcon:"fa-tags"}).appendTo(i.find(".dataset-actions .right"))});if(this.tagsEditorShown){this.tagsEditor.toggle(true)}},_renderAnnotation:function(i){var j=this;this.annotationEditor=new e.AnnotationEditor({model:this.model,el:i.find(".annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){j.annotationEditorShown=true},onhide:function(){j.annotationEditorShown=false},$activator:faIconButton({title:f("Edit dataset annotation"),classes:"dataset-annotate-btn",faIcon:"fa-comment"}).appendTo(i.find(".dataset-actions .right"))});if(this.annotationEditorShown){this.annotationEditor.toggle(true)}},makeDbkeyEditLink:function(j){if(this.model.get("metadata_dbkey")==="?"&&!this.model.isDeletedOrPurged()){var i=$('<a class="value">?</a>').attr("href",this.urls.edit).attr("target",this.linkTarget);j.find(".dataset-dbkey .value").replaceWith(i)}},events:_.extend(_.clone(b.HDABaseView.prototype.events),{"click .dataset-undelete":function(i){this.model.undelete();return false},"click .dataset-unhide":function(i){this.model.unhide();return false},"click .dataset-purge":"confirmPurge"}),confirmPurge:function d(i){this.model.purge();return false},toString:function(){var i=(this.model)?(this.model+""):("(no model)");return"HDAView("+i+")"}});function c(i,k,j){return function(){var l={};if(j){l["f-dbkey"]=j}$.ajax({url:i+"/list_tracks?"+$.param(l),dataType:"html",error:function(){alert(("Could not add this dataset to browser")+".")},success:function(m){var n=window.parent;n.Galaxy.modal.show({title:"View Data in a New or Saved Visualization",buttons:{Cancel:function(){n.Galaxy.modal.hide()},"View in saved visualization":function(){n.Galaxy.modal.show({title:"Add Data to Saved Visualization",body:m,buttons:{Cancel:function(){n.Galaxy.modal.hide()},"Add to visualization":function(){$(n.document).find("input[name=id]:checked").each(function(){n.Galaxy.modal.hide();var o=$(this).val();k.id=o;n.Galaxy.frame.add({title:"Trackster",type:"url",content:i+"/trackster?"+$.param(k)})})}}})},"View in new visualization":function(){n.Galaxy.modal.hide();var o=i+"/trackster?"+$.param(k);n.Galaxy.frame.add({title:"Trackster",type:"url",content:o})}}})}});return false}}return{HDAEditView:h}});
\ No newline at end of file
diff -r 2342d31a1f5cf906f1aecb0689fe64da384cf940 -r 68b49fdc63e2445ee2124e0185373d964bc64bc8 static/scripts/packed/mvc/history/history-panel.js
--- a/static/scripts/packed/mvc/history/history-panel.js
+++ b/static/scripts/packed/mvc/history/history-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-model","mvc/dataset/hda-edit","mvc/history/readonly-history-panel","mvc/tags","mvc/annotations","utils/localization"],function(f,b,d,a,c,e){var g=d.ReadOnlyHistoryPanel.extend({HDAViewClass:b.HDAEditView,initialize:function(h){h=h||{};this.selectedHdaIds=[];this.tagsEditor=null;this.annotationEditor=null;this.selecting=h.selecting||false;this.annotationEditorShown=h.annotationEditorShown||false;this.tagsEditorShown=h.tagsEditorShown||false;d.ReadOnlyHistoryPanel.prototype.initialize.call(this,h)},_setUpModelEventHandlers:function(){d.ReadOnlyHistoryPanel.prototype._setUpModelEventHandlers.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);this.model.hdas.on("change:deleted",this._handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this._handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(h){this.model.fetch()},this)},renderModel:function(){var h=$("<div/>");h.append(g.templates.historyPanel(this.model.toJSON()));this.$emptyMessage(h).text(this.emptyMsg);if(Galaxy&&Galaxy.currUser&&Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(h);this._renderAnnotation(h)}h.find(".history-secondary-actions").prepend(this._renderSelectButton());h.find(".history-dataset-actions").toggle(this.selecting);h.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(h);this.renderHdas(h);return h},_renderTags:function(h){var i=this;this.tagsEditor=new a.TagsEditor({model:this.model,el:h.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){i.toggleHDATagEditors(true,i.fxSpeed)},onhide:function(){i.toggleHDATagEditors(false,i.fxSpeed)},$activator:faIconButton({title:e("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(h.find(".history-secondary-actions"))})},_renderAnnotation:function(h){var i=this;this.annotationEditor=new c.AnnotationEditor({model:this.model,el:h.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){i.toggleHDAAnnotationEditors(true,i.fxSpeed)},onhide:function(){i.toggleHDAAnnotationEditors(false,i.fxSpeed)},$activator:faIconButton({title:e("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(h.find(".history-secondary-actions"))})},_renderSelectButton:function(h){return faIconButton({title:e("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(h){h=h||this.$el;d.ReadOnlyHistoryPanel.prototype._setUpBehaviours.call(this,h);if(!this.model){return}this._setUpDatasetActionsPopup(h);if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var i=this;h.find(".history-name").attr("title",e("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(j){var k=i.model.get("name");if(j&&j!==k){i.$el.find(".history-name").text(j);i.model.save({name:j}).fail(function(){i.$el.find(".history-name").text(i.model.previous("name"))})}else{i.$el.find(".history-name").text(k)}}})},_setUpDatasetActionsPopup:function(h){var i=this;(new PopupMenu(h.find(".history-dataset-action-popup-btn"),[{html:e("Hide datasets"),func:function(){var j=f.HistoryDatasetAssociation.prototype.hide;i.getSelectedHdaCollection().ajaxQueue(j)}},{html:e("Unhide datasets"),func:function(){var j=f.HistoryDatasetAssociation.prototype.unhide;i.getSelectedHdaCollection().ajaxQueue(j)}},{html:e("Delete datasets"),func:function(){var j=f.HistoryDatasetAssociation.prototype["delete"];i.getSelectedHdaCollection().ajaxQueue(j)}},{html:e("Undelete datasets"),func:function(){var j=f.HistoryDatasetAssociation.prototype.undelete;i.getSelectedHdaCollection().ajaxQueue(j)}},{html:e("Permanently delete datasets"),func:function(){if(confirm(e("This will permanently remove the data in your datasets. Are you sure?"))){var j=f.HistoryDatasetAssociation.prototype.purge;i.getSelectedHdaCollection().ajaxQueue(j)}}}]))},_handleHdaDeletionChange:function(h){if(h.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[h.id])}},_handleHdaVisibleChange:function(h){if(h.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[h.id])}},_createHdaView:function(i){var h=i.get("id"),j=new this.HDAViewClass({model:i,linkTarget:this.linkTarget,expanded:this.storage.get("expandedHdas")[h],selectable:this.selecting,hasUser:this.model.ownedByCurrUser(),logger:this.logger,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});this._setUpHdaListeners(j);return j},_setUpHdaListeners:function(i){var h=this;d.ReadOnlyHistoryPanel.prototype._setUpHdaListeners.call(this,i);i.on("selected",function(j){var k=j.model.get("id");h.selectedHdaIds=_.union(h.selectedHdaIds,[k])});i.on("de-selected",function(j){var k=j.model.get("id");h.selectedHdaIds=_.without(h.selectedHdaIds,k)})},toggleHDATagEditors:function(h){var i=arguments;_.each(this.hdaViews,function(j){if(j.tagsEditor){j.tagsEditor.toggle.apply(j.tagsEditor,i)}})},toggleHDAAnnotationEditors:function(h){var i=arguments;_.each(this.hdaViews,function(j){if(j.annotationEditor){j.annotationEditor.toggle.apply(j.annotationEditor,i)}})},removeHdaView:function(i){if(!i){return}var h=this;i.$el.fadeOut(h.fxSpeed,function(){i.off();i.remove();delete h.hdaViews[i.model.id];if(_.isEmpty(h.hdaViews)){h.$emptyMessage().fadeIn(h.fxSpeed,function(){h.trigger("empty-history",h)})}})},events:_.extend(_.clone(d.ReadOnlyHistoryPanel.prototype.events),{"click .history-select-btn":"toggleSelectors","click .history-select-all-datasets-btn":"selectAllDatasets","click .history-deselect-all-datasets-btn":"deselectAllDatasets"}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},showSelectors:function(h){h=(h!==undefined)?(h):(this.fxSpeed);this.selecting=true;this.$(".history-dataset-actions").slideDown(h);_.each(this.hdaViews,function(i){i.showSelector()});this.selectedHdaIds=[]},hideSelectors:function(h){h=(h!==undefined)?(h):(this.fxSpeed);this.selecting=false;this.$(".history-dataset-actions").slideUp(h);_.each(this.hdaViews,function(i){i.hideSelector()});this.selectedHdaIds=[]},toggleSelectors:function(){if(!this.selecting){this.showSelectors()}else{this.hideSelectors()}},selectAllDatasets:function(h){_.each(this.hdaViews,function(i){i.select(h)})},deselectAllDatasets:function(h){_.each(this.hdaViews,function(i){i.deselect(h)})},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(h){return h.selected})},getSelectedHdaCollection:function(){return new f.HDACollection(_.map(this.getSelectedHdaViews(),function(h){return h.model}),{historyId:this.model.id})},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanel:g}});
\ No newline at end of file
+define(["mvc/dataset/hda-model","mvc/dataset/hda-edit","mvc/history/readonly-history-panel","mvc/tags","mvc/annotations","utils/localization"],function(f,b,d,a,c,e){var g=d.ReadOnlyHistoryPanel.extend({HDAViewClass:b.HDAEditView,initialize:function(h){h=h||{};this.selectedHdaIds=[];this.tagsEditor=null;this.annotationEditor=null;this.purgeAllowed=h.purgeAllowed||false;this.selecting=h.selecting||false;this.annotationEditorShown=h.annotationEditorShown||false;this.tagsEditorShown=h.tagsEditorShown||false;d.ReadOnlyHistoryPanel.prototype.initialize.call(this,h)},_setUpModelEventHandlers:function(){d.ReadOnlyHistoryPanel.prototype._setUpModelEventHandlers.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);this.model.hdas.on("change:deleted",this._handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this._handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(h){this.model.fetch()},this)},renderModel:function(){var h=$("<div/>");h.append(g.templates.historyPanel(this.model.toJSON()));this.$emptyMessage(h).text(this.emptyMsg);if(Galaxy&&Galaxy.currUser&&Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(h);this._renderAnnotation(h)}h.find(".history-secondary-actions").prepend(this._renderSelectButton());h.find(".history-dataset-actions").toggle(this.selecting);h.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(h);this.renderHdas(h);return h},_renderTags:function(h){var i=this;this.tagsEditor=new a.TagsEditor({model:this.model,el:h.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){i.toggleHDATagEditors(true,i.fxSpeed)},onhide:function(){i.toggleHDATagEditors(false,i.fxSpeed)},$activator:faIconButton({title:e("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(h.find(".history-secondary-actions"))})},_renderAnnotation:function(h){var i=this;this.annotationEditor=new c.AnnotationEditor({model:this.model,el:h.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){i.toggleHDAAnnotationEditors(true,i.fxSpeed)},onhide:function(){i.toggleHDAAnnotationEditors(false,i.fxSpeed)},$activator:faIconButton({title:e("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(h.find(".history-secondary-actions"))})},_renderSelectButton:function(h){return faIconButton({title:e("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(h){h=h||this.$el;d.ReadOnlyHistoryPanel.prototype._setUpBehaviours.call(this,h);if(!this.model){return}this._setUpDatasetActionsPopup(h);if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var i=this;h.find(".history-name").attr("title",e("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(j){var k=i.model.get("name");if(j&&j!==k){i.$el.find(".history-name").text(j);i.model.save({name:j}).fail(function(){i.$el.find(".history-name").text(i.model.previous("name"))})}else{i.$el.find(".history-name").text(k)}}})},_setUpDatasetActionsPopup:function(h){var i=this,j=[{html:e("Hide datasets"),func:function(){var k=f.HistoryDatasetAssociation.prototype.hide;i.getSelectedHdaCollection().ajaxQueue(k)}},{html:e("Unhide datasets"),func:function(){var k=f.HistoryDatasetAssociation.prototype.unhide;i.getSelectedHdaCollection().ajaxQueue(k)}},{html:e("Delete datasets"),func:function(){var k=f.HistoryDatasetAssociation.prototype["delete"];i.getSelectedHdaCollection().ajaxQueue(k)}},{html:e("Undelete datasets"),func:function(){var k=f.HistoryDatasetAssociation.prototype.undelete;i.getSelectedHdaCollection().ajaxQueue(k)}}];if(i.purgeAllowed){j.push({html:e("Permanently delete datasets"),func:function(){if(confirm(e("This will permanently remove the data in your datasets. Are you sure?"))){var k=f.HistoryDatasetAssociation.prototype.purge;i.getSelectedHdaCollection().ajaxQueue(k)}}})}return new PopupMenu(h.find(".history-dataset-action-popup-btn"),j)},_handleHdaDeletionChange:function(h){if(h.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[h.id])}},_handleHdaVisibleChange:function(h){if(h.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[h.id])}},_createHdaView:function(i){var h=i.get("id"),j=new this.HDAViewClass({model:i,linkTarget:this.linkTarget,expanded:this.storage.get("expandedHdas")[h],selectable:this.selecting,purgeAllowed:this.purgeAllowed,hasUser:this.model.ownedByCurrUser(),logger:this.logger,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});this._setUpHdaListeners(j);return j},_setUpHdaListeners:function(i){var h=this;d.ReadOnlyHistoryPanel.prototype._setUpHdaListeners.call(this,i);i.on("selected",function(j){var k=j.model.get("id");h.selectedHdaIds=_.union(h.selectedHdaIds,[k])});i.on("de-selected",function(j){var k=j.model.get("id");h.selectedHdaIds=_.without(h.selectedHdaIds,k)})},toggleHDATagEditors:function(h){var i=arguments;_.each(this.hdaViews,function(j){if(j.tagsEditor){j.tagsEditor.toggle.apply(j.tagsEditor,i)}})},toggleHDAAnnotationEditors:function(h){var i=arguments;_.each(this.hdaViews,function(j){if(j.annotationEditor){j.annotationEditor.toggle.apply(j.annotationEditor,i)}})},removeHdaView:function(i){if(!i){return}var h=this;i.$el.fadeOut(h.fxSpeed,function(){i.off();i.remove();delete h.hdaViews[i.model.id];if(_.isEmpty(h.hdaViews)){h.$emptyMessage().fadeIn(h.fxSpeed,function(){h.trigger("empty-history",h)})}})},events:_.extend(_.clone(d.ReadOnlyHistoryPanel.prototype.events),{"click .history-select-btn":"toggleSelectors","click .history-select-all-datasets-btn":"selectAllDatasets","click .history-deselect-all-datasets-btn":"deselectAllDatasets"}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},showSelectors:function(h){h=(h!==undefined)?(h):(this.fxSpeed);this.selecting=true;this.$(".history-dataset-actions").slideDown(h);_.each(this.hdaViews,function(i){i.showSelector()});this.selectedHdaIds=[]},hideSelectors:function(h){h=(h!==undefined)?(h):(this.fxSpeed);this.selecting=false;this.$(".history-dataset-actions").slideUp(h);_.each(this.hdaViews,function(i){i.hideSelector()});this.selectedHdaIds=[]},toggleSelectors:function(){if(!this.selecting){this.showSelectors()}else{this.hideSelectors()}},selectAllDatasets:function(h){_.each(this.hdaViews,function(i){i.select(h)})},deselectAllDatasets:function(h){_.each(this.hdaViews,function(i){i.deselect(h)})},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(h){return h.selected})},getSelectedHdaCollection:function(){return new f.HDACollection(_.map(this.getSelectedHdaViews(),function(h){return h.model}),{historyId:this.model.id})},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanel:g}});
\ No newline at end of file
diff -r 2342d31a1f5cf906f1aecb0689fe64da384cf940 -r 68b49fdc63e2445ee2124e0185373d964bc64bc8 templates/webapps/galaxy/history/view.mako
--- a/templates/webapps/galaxy/history/view.mako
+++ b/templates/webapps/galaxy/history/view.mako
@@ -173,6 +173,7 @@
window.historyPanel = new panelClass({
show_deleted : ${show_deleted_json},
show_hidden : ${show_hidden_json},
+ purgeAllowed : Galaxy.config.allow_user_dataset_purge,
el : $( "#history-" + historyJSON.id ),
model : history,
onready : function(){
diff -r 2342d31a1f5cf906f1aecb0689fe64da384cf940 -r 68b49fdc63e2445ee2124e0185373d964bc64bc8 templates/webapps/galaxy/root/history.mako
--- a/templates/webapps/galaxy/root/history.mako
+++ b/templates/webapps/galaxy/root/history.mako
@@ -25,6 +25,7 @@
window.panel = new historyPanel.CurrentHistoryPanel({
show_deleted : bootstrapped.show_deleted,
show_hidden : bootstrapped.show_hidden,
+ purgeAllowed : Galaxy.config.allow_user_dataset_purge,
el : $( "body" ),
model : new historyModel.History( bootstrapped.history, bootstrapped.hdas ),
onready : function(){
diff -r 2342d31a1f5cf906f1aecb0689fe64da384cf940 -r 68b49fdc63e2445ee2124e0185373d964bc64bc8 templates/webapps/galaxy/root/index.mako
--- a/templates/webapps/galaxy/root/index.mako
+++ b/templates/webapps/galaxy/root/index.mako
@@ -205,6 +205,7 @@
$(function(){
var currPanel = new historyPanel.CurrentHistoryPanel({
el : $( "#current-history-panel" ),
+ purgeAllowed : Galaxy.config.allow_user_dataset_purge,
linkTarget : 'galaxy_main',
onready : function loadAsCurrentHistoryPanel(){
this.connectToQuotaMeter( Galaxy.quotaMeter )
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/a0d853e0fe80/
Changeset: a0d853e0fe80
User: davebgx
Date: 2014-04-22 18:58:42
Summary: Fix library template functional tests. Revert change to user info tests that was breaking library template tests.
Affected #: 3 files
diff -r 4a697eff12901cdb715667526bc097948646da86 -r a0d853e0fe804be7b21191957998ec7cdd4d8305 test/base/twilltestcase.py
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -1698,17 +1698,16 @@
num_fields=1, num_options=0, field_name='1_field_name', strings_displayed=[],
strings_displayed_after_submit=[] ):
"""Create a new form definition."""
- strings_displayed_after_submit.extend( [ name, description, form_type ] )
self.visit_url( "%s/forms/create_form_definition" % self.url )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
- tc.fv( "1", "name", name )
- tc.fv( "1", "description", description )
- tc.fv( "1", "form_type_select_field", form_type )
+ tc.fv( "create_form_definition", "name", name )
+ tc.fv( "create_form_definition", "description", description )
+ tc.fv( "create_form_definition", "form_type_select_field", form_type )
tc.submit( "create_form_button" )
if form_type == "Sequencing Sample Form":
tc.submit( "add_layout_grid" )
- tc.fv( "1", "grid_layout0", form_layout_name )
+ tc.fv( "create_form_definition", "grid_layout0", form_layout_name )
# if not adding any fields at this time, remove the default empty field
if num_fields == 0:
tc.submit( "remove_button" )
@@ -1720,8 +1719,8 @@
field_help_contents = 'Field %i help' % index1
field_default = 'field_default_0'
field_default_contents = '%s default contents' % form_type
- tc.fv( "1", field_label, field_contents )
- tc.fv( "1", field_help_name, field_help_contents )
+ tc.fv( "edit_form_definition", field_label, field_contents )
+ tc.fv( "edit_form_definition", field_help_name, field_help_contents )
if field_type == 'SelectField':
# SelectField field_type requires a refresh_on_change
self.refresh_form( 'field_type_0', field_type )
@@ -1735,11 +1734,11 @@
for index2 in range( num_options ):
option_field_name = 'field_0_option_%i' % index2
option_field_value = 'Option%i' % index2
- tc.fv( "1", option_field_name, option_field_value )
+ tc.fv( "edit_form_definition", option_field_name, option_field_value )
else:
- tc.fv( "1", "field_type_0", field_type )
- tc.fv( "1", 'field_name_0', field_name )
- tc.fv( "1", field_default, field_default_contents )
+ tc.fv( "edit_form_definition", "field_type_0", field_type )
+ tc.fv( "edit_form_definition", 'field_name_0', field_name )
+ tc.fv( "edit_form_definition", field_default, field_default_contents )
# All done... now save
tc.submit( "save_changes_button" )
for check_str in strings_displayed_after_submit:
diff -r 4a697eff12901cdb715667526bc097948646da86 -r a0d853e0fe804be7b21191957998ec7cdd4d8305 test/functional/test_library_templates.py
--- a/test/functional/test_library_templates.py
+++ b/test/functional/test_library_templates.py
@@ -436,7 +436,7 @@
strings_displayed=[ 'SelectField', 'Option1' ] )
# Import a dataset from the current history
filename = '8.bed'
- self.new_history( name='import with SelectField' )
+ self.new_history( name='import+with+SelectField' )
self.upload_file( filename )
hda = get_latest_hda()
self.upload_library_dataset( cntrller='library_admin',
diff -r 4a697eff12901cdb715667526bc097948646da86 -r a0d853e0fe804be7b21191957998ec7cdd4d8305 test/functional/test_user_info.py
--- a/test/functional/test_user_info.py
+++ b/test/functional/test_user_info.py
@@ -38,12 +38,15 @@
"""Testing creating a new user info form and editing it"""
# Logged in as admin_user
# Create a the first form
- self.create_form( name='Student',
- description="This is Student user info form's description",
- form_type=get_user_info_form_definition(),
+ name = "Student"
+ desc = "This is Student user info form's description"
+ form_type = get_user_info_form_definition()
+ self.create_form( name=name,
+ description=desc,
+ form_type=form_type,
num_fields=0,
strings_displayed=[ 'Create a new form definition' ],
- strings_displayed_after_submit=[] )
+ strings_displayed_after_submit=[ name, desc, form_type ] )
tmp_form = get_form( 'Student' )
# Add fields to the form
field_dicts = [ dict( label='Affiliation',
@@ -73,12 +76,14 @@
assert form_one is not None, 'Problem retrieving form named "Student" from the database'
assert len( form_one.fields ) == len( tmp_form.fields ) + len( field_dicts )
# Create the second form
- self.create_form( name='Researcher',
- description="This is Researcher user info form's description",
- form_type=get_user_info_form_definition(),
+ name = "Researcher"
+ desc = "This is Researcher user info form's description"
+ self.create_form( name=name,
+ description=desc,
+ form_type=form_type,
num_fields=0,
strings_displayed=[ 'Create a new form definition' ],
- strings_displayed_after_submit=[] )
+ strings_displayed_after_submit=[ name, desc, form_type ] )
tmp_form = get_form( 'Researcher' )
# Add fields to the form
self.edit_form( id=self.security.encode_id( tmp_form.current.id ),
https://bitbucket.org/galaxy/galaxy-central/commits/2342d31a1f5c/
Changeset: 2342d31a1f5c
User: davebgx
Date: 2014-04-22 18:59:21
Summary: Merge stable changes into default.
Affected #: 0 files
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/b6c06c9db59b/
Changeset: b6c06c9db59b
User: jmchilton
Date: 2014-04-22 18:13:27
Summary: Workflow Editor: Rework InputTerminal to implement/use an update method (w/tests).
This will enable replacing terminals instead of recreating them and reconnecting in subsequent changesets.
Affected #: 2 files
diff -r 008c26226230353a2440e6c7208de58575cfebff -r b6c06c9db59b3197a4f1c55327679ae6a6a08263 static/scripts/galaxy.workflow_editor.canvas.js
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -38,8 +38,11 @@
var InputTerminal = Terminal.extend( {
initialize: function( attr ) {
Terminal.prototype.initialize.call( this, attr );
- this.datatypes = attr.datatypes;
- this.multiple = attr.multiple;
+ this.update( attr.input );
+ },
+ update: function( input ) {
+ this.datatypes = input.extensions;
+ this.multiple = input.multiple;
},
canAccept: function ( other ) {
if( this._inputFilled() ) {
@@ -981,13 +984,9 @@
var input = options.input;
var name = input.name;
- var types = input.extensions;
- var multiple = input.multiple;
-
- var terminal = this.el.terminal = new InputTerminal( { element: this.el, datatypes: types, multiple: multiple } );
+ var terminal = this.el.terminal = new InputTerminal( { element: this.el, input: input } );
terminal.node = node;
terminal.name = name;
-
node.input_terminals[name] = terminal;
},
diff -r 008c26226230353a2440e6c7208de58575cfebff -r b6c06c9db59b3197a4f1c55327679ae6a6a08263 test/qunit/tests/workflow_editor_tests.js
--- a/test/qunit/tests/workflow_editor_tests.js
+++ b/test/qunit/tests/workflow_editor_tests.js
@@ -63,7 +63,8 @@
setup: function() {
this.node = { };
this.element = $( "<div>" );
- this.input_terminal = new InputTerminal( { element: this.element, datatypes: [ "txt" ] } );
+ var input = { extensions: [ "txt" ], multiple: false };
+ this.input_terminal = new InputTerminal( { element: this.element, input: input } );
this.input_terminal.node = this.node;
},
test_connector: function( attr ) {
@@ -90,6 +91,16 @@
}
} );
+ test( "test update", function() {
+ deepEqual( this.input_terminal.datatypes, [ 'txt' ] );
+ equal( this.input_terminal.multiple, false );
+
+ this.input_terminal.update( { extensions: [ 'bam' ], multiple: true } );
+
+ deepEqual( this.input_terminal.datatypes, [ 'bam' ] );
+ equal( this.input_terminal.multiple, true );
+ } );
+
test( "test connect", function() {
this.node.markChanged = sinon.spy();
https://bitbucket.org/galaxy/galaxy-central/commits/47ff7cab1ab1/
Changeset: 47ff7cab1ab1
User: jmchilton
Date: 2014-04-22 18:13:27
Summary: Workflow Editor: Replace instead of recreate terminal on update.
This fixes at least one subtle bug related to multiple input data parameters (actually probably two bugs) because the old logic assumed there was only one connector per input terminal.
This should be more efficient, lead to some code duplication deletion in subsequent changesets, and really help dataset collections where terminals are much more complex (there are data inputs and collection inputs, and each can be mapped over by collections) - this helps manage complexity downstream.
Affected #: 2 files
diff -r b6c06c9db59b3197a4f1c55327679ae6a6a08263 -r 47ff7cab1ab1b92bcbb0db06d6892bdad856f005 static/scripts/galaxy.workflow_editor.canvas.js
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -257,14 +257,17 @@
// Update input rows
var old_body = nodeView.$( "div.inputs" );
var new_body = nodeView.newInputsDiv();
- $.each( data.data_inputs, function( i, input ) {
- node.nodeView.replaceDataInput( input, new_body );
+ var newTerminalViews = {};
+ _.each( data.data_inputs, function( input ) {
+ var terminalView = node.nodeView.addDataInput( input, new_body );
+ newTerminalViews[ input.name ] = terminalView;
});
+ // Cleanup any leftover terminals
+ _.each( _.difference( _.values( nodeView.terminalViews ), _.values( newTerminalViews ) ), function( unusedView ) {
+ unusedView.el.terminal.destroy();
+ } );
+ nodeView.terminalViews = newTerminalViews;
old_body.replaceWith( new_body );
- // Cleanup any leftover terminals
- old_body.find( "div.input-data-row > .terminal" ).each( function() {
- this.terminal.destroy();
- });
// If active, reactivate with new form_html
this.markChanged();
this.redraw();
@@ -754,6 +757,7 @@
this.tool_body = this.$el.find( ".toolFormBody" );
this.tool_body.find( "div" ).remove();
this.newInputsDiv().appendTo( this.tool_body );
+ this.terminalViews = {};
},
render : function() {
@@ -786,6 +790,7 @@
node: this.node,
input: input
} );
+ this.terminalViews[ input.name ] = terminalView;
var terminalElement = terminalView.el;
var inputView = new DataInputView( {
"terminalElement": terminalElement,
@@ -798,29 +803,23 @@
},
replaceDataInput: function( input, new_body ) {
- var terminalView = new InputTerminalView( {
- node: this.node,
- input: input
- } );
+ var terminalView = this.terminalViews[ input.name ];
+ if( ! terminalView ) {
+ terminalView = new InputTerminalView( {
+ node: this.node,
+ input: input
+ } );
+ } else {
+ var terminal = terminalView.el.terminal;
+ terminal.update( input );
+ _.each( terminal.connectors, function( connector ) {
+ if( connector.handle1 && ! terminal.attachable( connector.handle1 ) ) {
+ connector.destroy();
+ }
+ } );
+ }
+ this.terminalViews[ input.name ] = terminalView;
var t = terminalView.el;
-
- // If already connected save old connection
- this.$( "div[name='" + input.name + "']" ).each( function() {
- $(this).find( ".input-terminal" ).each( function() {
- var c = this.terminal.connectors[0];
- if ( c ) {
- var terminal = t.terminal;
- if( c.handle1 && ! terminal.attachable( c.handle1 ) ) {
- // connection no longer valid, destroy it
- c.destroy();
- } else {
- terminal.connectors[0] = c;
- c.handle2 = terminal;
- }
- }
- });
- $(this).remove();
- });
var inputView = new DataInputView( {
"terminalElement": t,
"input": input,
@@ -831,7 +830,7 @@
// Append to new body
new_body.append( ib.prepend( t ) );
-
+ return terminalView;
},
addDataOutput: function( output ) {
diff -r b6c06c9db59b3197a4f1c55327679ae6a6a08263 -r 47ff7cab1ab1b92bcbb0db06d6892bdad856f005 test/qunit/tests/workflow_editor_tests.js
--- a/test/qunit/tests/workflow_editor_tests.js
+++ b/test/qunit/tests/workflow_editor_tests.js
@@ -385,11 +385,14 @@
var old_input_terminal = node.input_terminals.input1;
old_input_terminal.connectors.push( connector );
+ // Update node, make sure connector still the same...
test.update_field_data_with_new_input();
-
var new_input_terminal = node.input_terminals.input1;
- equal( old_input_terminal, old_input_terminal );
- notEqual( old_input_terminal, new_input_terminal );
+ equal( connector, new_input_terminal.connectors[ 0 ] );
+
+ // Update a second time, make sure connector still the same...
+ test.update_field_data_with_new_input();
+ new_input_terminal = node.input_terminals.input1;
equal( connector, new_input_terminal.connectors[ 0 ] );
} );
} );
https://bitbucket.org/galaxy/galaxy-central/commits/4a697eff1290/
Changeset: 4a697eff1290
User: jmchilton
Date: 2014-04-22 18:13:27
Summary: Workflow Editor: Merge addDataInput and replaceDataInput into common method.
Reduces some code duplication here and reduces even more code duplication downstream in dataset collections work.
Affected #: 2 files
diff -r 47ff7cab1ab1b92bcbb0db06d6892bdad856f005 -r 4a697eff12901cdb715667526bc097948646da86 static/scripts/galaxy.workflow_editor.canvas.js
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -785,24 +785,14 @@
this.tool_body.append( $( "<div class='rule'></div>" ) );
},
- addDataInput: function( input ) {
- var terminalView = new InputTerminalView( {
- node: this.node,
- input: input
- } );
- this.terminalViews[ input.name ] = terminalView;
- var terminalElement = terminalView.el;
- var inputView = new DataInputView( {
- "terminalElement": terminalElement,
- "input": input,
- "nodeView": this,
- } );
- var ib = inputView.$el;
- var terminalElement = inputView.terminalElement;
- this.$( ".inputs" ).append( ib.prepend( terminalElement ) );
- },
-
- replaceDataInput: function( input, new_body ) {
+ addDataInput: function( input, body ) {
+ var skipResize = true;
+ if( ! body ) {
+ body = this.$( ".inputs" );
+ // initial addition to node - resize input to help calculate node
+ // width.
+ skipResize = false;
+ }
var terminalView = this.terminalViews[ input.name ];
if( ! terminalView ) {
terminalView = new InputTerminalView( {
@@ -819,17 +809,17 @@
} );
}
this.terminalViews[ input.name ] = terminalView;
- var t = terminalView.el;
+ var terminalElement = terminalView.el;
var inputView = new DataInputView( {
- "terminalElement": t,
- "input": input,
- "nodeView": this,
- "skipResize": true,
+ terminalElement: terminalElement,
+ input: input,
+ nodeView: this,
+ skipResize: skipResize
} );
var ib = inputView.$el;
- // Append to new body
- new_body.append( ib.prepend( t ) );
+ var terminalElement = inputView.terminalElement;
+ body.append( ib.prepend( terminalElement ) );
return terminalView;
},
diff -r 47ff7cab1ab1b92bcbb0db06d6892bdad856f005 -r 4a697eff12901cdb715667526bc097948646da86 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 @@
test( "replacing terminal on data input update preserves connections", function() {
var connector = this.connectAttachedTerminal( "txt", "txt" );
var newElement = $("<div class='inputs'></div>");
- this.view.replaceDataInput( { name: "TestName", extensions: ["txt"] }, newElement );
+ this.view.addDataInput( { name: "TestName", extensions: ["txt"] }, newElement );
var terminal = newElement.find(".input-terminal")[ 0 ].terminal;
ok( connector.handle2 === terminal );
} );
@@ -487,7 +487,7 @@
var newElement = $("<div class='inputs'></div>");
var connector_destroy_spy = sinon.spy( connector, "destroy" );
// Replacing input with same name - but now of type bam should destroy connection.
- this.view.replaceDataInput( { name: "TestName", extensions: ["bam"] }, newElement );
+ this.view.addDataInput( { name: "TestName", extensions: ["bam"] }, newElement );
var terminal = newElement.find(".input-terminal")[ 0 ].terminal;
ok( connector_destroy_spy.called );
} );
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: martenson: rename biostar menu link
by commits-noreply@bitbucket.org 22 Apr '14
by commits-noreply@bitbucket.org 22 Apr '14
22 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/7a7985a007fb/
Changeset: 7a7985a007fb
Branch: stable
User: martenson
Date: 2014-04-22 16:53:35
Summary: rename biostar menu link
Affected #: 2 files
diff -r 1d06e19aa3aac0c87b21f8482b8fc73c89e277ad -r 7a7985a007fbce2bfa7bea5d8d23af1d63fecbb1 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -195,7 +195,7 @@
if (this.options.biostar_url)
{
tab_help.add({
- title : "Galaxy Q&A Site",
+ title : "Galaxy Biostar",
content : this.options.biostar_url_redirect,
target : "_blank"
});
diff -r 1d06e19aa3aac0c87b21f8482b8fc73c89e277ad -r 7a7985a007fbce2bfa7bea5d8d23af1d63fecbb1 static/scripts/packed/galaxy.menu.js
--- a/static/scripts/packed/galaxy.menu.js
+++ b/static/scripts/packed/galaxy.menu.js
@@ -1,1 +1,1 @@
-define(["galaxy.masthead"],function(b){var a=Backbone.Model.extend({options:null,masthead:null,initialize:function(c){this.options=c.config;this.masthead=c.masthead;this.create()},create:function(){var e=new b.GalaxyMastheadTab({id:"analysis",title:"Analyze Data",content:"root/index",title_attribute:"Analysis home view"});this.masthead.append(e);var g={id:"workflow",title:"Workflow",content:"workflow",title_attribute:"Chain tools into workflows"};if(!this.options.user.valid){g.disabled=true}var d=new b.GalaxyMastheadTab(g);this.masthead.append(d);var i=new b.GalaxyMastheadTab({id:"shared",title:"Shared Data",content:"library/index",title_attribute:"Access published resources"});i.add({title:"Data Libraries",content:"library/index"});i.add({title:"Data Libraries Beta",content:"library/list",divider:true});i.add({title:"Published Histories",content:"history/list_published"});i.add({title:"Published Workflows",content:"workflow/list_published"});i.add({title:"Published Visualizations",content:"visualization/list_published"});i.add({title:"Published Pages",content:"page/list_published"});this.masthead.append(i);if(this.options.user.requests){var j=new b.GalaxyMastheadTab({id:"lab",title:"Lab"});j.add({title:"Sequencing Requests",content:"requests/index"});j.add({title:"Find Samples",content:"requests/find_samples_index"});j.add({title:"Help",content:this.options.lims_doc_url});this.masthead.append(j)}var c={id:"visualization",title:"Visualization",content:"visualization/list",title_attribute:"Visualize datasets"};if(!this.options.user.valid){c.disabled=true}var m=new b.GalaxyMastheadTab(c);if(this.options.user.valid){m.add({title:"New Track Browser",content:"visualization/trackster",target:"_frame"});m.add({title:"Saved Visualizations",content:"visualization/list",target:"_frame"})}this.masthead.append(m);if(this.options.enable_cloud_launch){var f=new b.GalaxyMastheadTab({id:"cloud",title:"Cloud",content:"cloudlaunch/index"});f.add({title:"New Cloud Cluster",content:"cloudlaunch/index"});this.masthead.append(f)}if(this.options.is_admin_user){var h=new b.GalaxyMastheadTab({id:"admin",title:"Admin",content:"admin/index",extra_class:"admin-only",title_attribute:"Administer this Galaxy"});this.masthead.append(h)}var l=new b.GalaxyMastheadTab({id:"help",title:"Help",title_attribute:"Support, contact, and community hubs"});if(this.options.biostar_url){l.add({title:"Galaxy Q&A Site",content:this.options.biostar_url_redirect,target:"_blank"});l.add({title:"Ask a question",content:"biostar/biostar_question_redirect",target:"_blank"})}l.add({title:"Support",content:this.options.support_url,target:"_blank"});l.add({title:"Search",content:this.options.search_url,target:"_blank"});l.add({title:"Mailing Lists",content:this.options.mailing_lists,target:"_blank"});l.add({title:"Videos",content:this.options.screencasts_url,target:"_blank"});l.add({title:"Wiki",content:this.options.wiki_url,target:"_blank"});l.add({title:"How to Cite Galaxy",content:this.options.citation_url,target:"_blank"});if(this.options.terms_url){l.add({title:"Terms and Conditions",content:this.options.terms_url,target:"_blank"})}this.masthead.append(l);if(!this.options.user.valid){var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedout-only",title_attribute:"Account registration or login"});k.add({title:"Login",content:"user/login",target:"galaxy_main"});if(this.options.allow_user_creation){k.add({title:"Register",content:"user/create",target:"galaxy_main"})}this.masthead.append(k)}else{var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedin-only",title_attribute:"Account preferences and saved data"});k.add({title:"Logged in as "+this.options.user.email});if(this.options.use_remote_user&&this.options.remote_user_logout_href){k.add({title:"Logout",content:this.options.remote_user_logout_href,target:"_top"})}else{k.add({title:"Preferences",content:"user?cntrller=user",target:"galaxy_main"});k.add({title:"Custom Builds",content:"user/dbkeys",target:"galaxy_main"});k.add({title:"Logout",content:"user/logout",target:"_top",divider:true})}k.add({title:"Saved Histories",content:"history/list",target:"galaxy_main"});k.add({title:"Saved Datasets",content:"dataset/list",target:"galaxy_main"});k.add({title:"Saved Pages",content:"page/list",target:"_top"});k.add({title:"API Keys",content:"user/api_keys?cntrller=user",target:"galaxy_main"});if(this.options.use_remote_user){k.add({title:"Public Name",content:"user/edit_username?cntrller=user",target:"galaxy_main"})}this.masthead.append(k)}if(this.options.active_view){this.masthead.highlight(this.options.active_view)}}});return{GalaxyMenu:a}});
\ No newline at end of file
+define(["galaxy.masthead"],function(b){var a=Backbone.Model.extend({options:null,masthead:null,initialize:function(c){this.options=c.config;this.masthead=c.masthead;this.create()},create:function(){var e=new b.GalaxyMastheadTab({id:"analysis",title:"Analyze Data",content:"root/index",title_attribute:"Analysis home view"});this.masthead.append(e);var g={id:"workflow",title:"Workflow",content:"workflow",title_attribute:"Chain tools into workflows"};if(!this.options.user.valid){g.disabled=true}var d=new b.GalaxyMastheadTab(g);this.masthead.append(d);var i=new b.GalaxyMastheadTab({id:"shared",title:"Shared Data",content:"library/index",title_attribute:"Access published resources"});i.add({title:"Data Libraries",content:"library/index"});i.add({title:"Data Libraries Beta",content:"library/list",divider:true});i.add({title:"Published Histories",content:"history/list_published"});i.add({title:"Published Workflows",content:"workflow/list_published"});i.add({title:"Published Visualizations",content:"visualization/list_published"});i.add({title:"Published Pages",content:"page/list_published"});this.masthead.append(i);if(this.options.user.requests){var j=new b.GalaxyMastheadTab({id:"lab",title:"Lab"});j.add({title:"Sequencing Requests",content:"requests/index"});j.add({title:"Find Samples",content:"requests/find_samples_index"});j.add({title:"Help",content:this.options.lims_doc_url});this.masthead.append(j)}var c={id:"visualization",title:"Visualization",content:"visualization/list",title_attribute:"Visualize datasets"};if(!this.options.user.valid){c.disabled=true}var m=new b.GalaxyMastheadTab(c);if(this.options.user.valid){m.add({title:"New Track Browser",content:"visualization/trackster",target:"_frame"});m.add({title:"Saved Visualizations",content:"visualization/list",target:"_frame"})}this.masthead.append(m);if(this.options.enable_cloud_launch){var f=new b.GalaxyMastheadTab({id:"cloud",title:"Cloud",content:"cloudlaunch/index"});f.add({title:"New Cloud Cluster",content:"cloudlaunch/index"});this.masthead.append(f)}if(this.options.is_admin_user){var h=new b.GalaxyMastheadTab({id:"admin",title:"Admin",content:"admin/index",extra_class:"admin-only",title_attribute:"Administer this Galaxy"});this.masthead.append(h)}var l=new b.GalaxyMastheadTab({id:"help",title:"Help",title_attribute:"Support, contact, and community hubs"});if(this.options.biostar_url){l.add({title:"Galaxy Biostar",content:this.options.biostar_url_redirect,target:"_blank"});l.add({title:"Ask a question",content:"biostar/biostar_question_redirect",target:"_blank"})}l.add({title:"Support",content:this.options.support_url,target:"_blank"});l.add({title:"Search",content:this.options.search_url,target:"_blank"});l.add({title:"Mailing Lists",content:this.options.mailing_lists,target:"_blank"});l.add({title:"Videos",content:this.options.screencasts_url,target:"_blank"});l.add({title:"Wiki",content:this.options.wiki_url,target:"_blank"});l.add({title:"How to Cite Galaxy",content:this.options.citation_url,target:"_blank"});if(this.options.terms_url){l.add({title:"Terms and Conditions",content:this.options.terms_url,target:"_blank"})}this.masthead.append(l);if(!this.options.user.valid){var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedout-only",title_attribute:"Account registration or login"});k.add({title:"Login",content:"user/login",target:"galaxy_main"});if(this.options.allow_user_creation){k.add({title:"Register",content:"user/create",target:"galaxy_main"})}this.masthead.append(k)}else{var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedin-only",title_attribute:"Account preferences and saved data"});k.add({title:"Logged in as "+this.options.user.email});if(this.options.use_remote_user&&this.options.remote_user_logout_href){k.add({title:"Logout",content:this.options.remote_user_logout_href,target:"_top"})}else{k.add({title:"Preferences",content:"user?cntrller=user",target:"galaxy_main"});k.add({title:"Custom Builds",content:"user/dbkeys",target:"galaxy_main"});k.add({title:"Logout",content:"user/logout",target:"_top",divider:true})}k.add({title:"Saved Histories",content:"history/list",target:"galaxy_main"});k.add({title:"Saved Datasets",content:"dataset/list",target:"galaxy_main"});k.add({title:"Saved Pages",content:"page/list",target:"_top"});k.add({title:"API Keys",content:"user/api_keys?cntrller=user",target:"galaxy_main"});if(this.options.use_remote_user){k.add({title:"Public Name",content:"user/edit_username?cntrller=user",target:"galaxy_main"})}this.masthead.append(k)}if(this.options.active_view){this.masthead.highlight(this.options.active_view)}}});return{GalaxyMenu:a}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: UI: rework zh and ja locale data
by commits-noreply@bitbucket.org 22 Apr '14
by commits-noreply@bitbucket.org 22 Apr '14
22 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/008c26226230/
Changeset: 008c26226230
User: carlfeberhard
Date: 2014-04-22 17:58:22
Summary: UI: rework zh and ja locale data
Affected #: 21 files
diff -r e7ec387cb3b3b8e6aaceade0c8bdd3da0077ac57 -r 008c26226230353a2440e6c7208de58575cfebff static/scripts/mvc/annotations.js
--- a/static/scripts/mvc/annotations.js
+++ b/static/scripts/mvc/annotations.js
@@ -1,6 +1,7 @@
define([
- "mvc/base-mvc"
-], function( baseMVC ){
+ "mvc/base-mvc",
+ "utils/localization"
+], function( baseMVC, _l ){
// =============================================================================
/** A view on any model that has a 'annotation' attribute
*/
diff -r e7ec387cb3b3b8e6aaceade0c8bdd3da0077ac57 -r 008c26226230353a2440e6c7208de58575cfebff static/scripts/mvc/dataset/hda-base.js
--- a/static/scripts/mvc/dataset/hda-base.js
+++ b/static/scripts/mvc/dataset/hda-base.js
@@ -239,8 +239,8 @@
var menuId = 'dataset-' + this.model.get( 'id' ) + '-popup',
html = [
'<div popupmenu="' + menuId + '">',
- '<a href="' + urls.download + '">', _l( 'Download Dataset' ), '</a>',
- '<a>' + _l( 'Additional Files' ) + '</a>',
+ '<a href="' + urls.download + '">', _l( 'Download dataset' ), '</a>',
+ '<a>' + _l( 'Additional files' ) + '</a>',
_.map( meta_files, function( meta_file ){
return [
@@ -647,13 +647,13 @@
// purged and deleted
'<% if( hda.purged ){ %>',
'<div class="dataset-purged-msg warningmessagesmall"><strong>',
- _l( 'This dataset has been deleted and removed from disk.' ),
+ _l( 'This dataset has been deleted and removed from disk' ) + '.',
'</strong></div>',
// deleted not purged
'<% } else { %>',
'<div class="dataset-deleted-msg warningmessagesmall"><strong>',
- _l( 'This dataset has been deleted.' ),
+ _l( 'This dataset has been deleted' ) + '.',
'</strong></div>',
'<% } %>',
'<% } %>',
@@ -661,7 +661,7 @@
// hidden
'<% if( !hda.visible ){ %>',
'<div class="dataset-hidden-msg warningmessagesmall"><strong>',
- _l( 'This dataset has been hidden.' ),
+ _l( 'This dataset has been hidden' ) + '.',
'</strong></div>',
'<% } %>',
'</div>',
diff -r e7ec387cb3b3b8e6aaceade0c8bdd3da0077ac57 -r 008c26226230353a2440e6c7208de58575cfebff static/scripts/mvc/dataset/hda-edit.js
--- a/static/scripts/mvc/dataset/hda-edit.js
+++ b/static/scripts/mvc/dataset/hda-edit.js
@@ -223,7 +223,7 @@
// No need for popup menu because there's a single visualization.
if( visualizations.length === 1 ){
- $icon.attr( 'data-original-title', _l( 'Visualize in ' ) + _l( titleCase( visualizations[0] ) ) );
+ $icon.attr( 'data-original-title', _l( 'Visualize in' ) + ' ' + _l( titleCase( visualizations[0] ) ) );
$icon.click( create_viz_action( visualizations[0] ) );
// >1: Populate menu dict with visualization fns, make the popupmenu
@@ -253,7 +253,7 @@
// No need for popup menu because there's a single visualization.
if( visualizations.length === 1 ) {
var onlyVisualization = visualizations[0];
- $icon.attr( 'data-original-title', _l( 'Visualize in ' ) + onlyVisualization.html );
+ $icon.attr( 'data-original-title', _l( 'Visualize in' ) + ' ' + onlyVisualization.html );
$icon.attr( 'href', onlyVisualization.href );
// >1: Populate menu dict with visualization fns, make the popupmenu
@@ -284,13 +284,20 @@
_buildNewRender : function(){
var $newRender = hdaBase.HDABaseView.prototype._buildNewRender.call( this );
- //TODO: this won't localize easily
- $newRender.find( '.dataset-deleted-msg' ).append(
- _l( ' Click <a href="javascript:void(0);" class="dataset-undelete">here</a> to undelete it' +
- ' or <a href="javascript:void(0);" class="dataset-purge">here</a> to immediately remove it from disk' ));
+//TODO: this won't localize easily
+ var br = '<br />', p = '.',
+ link = function( t, c ){
+ return [ '<a href="javascript:void(0)" class="', c, '">', t, '</a>' ].join( '' );
+ };
- $newRender.find( '.dataset-hidden-msg' ).append(
- _l( ' Click <a href="javascript:void(0);" class="dataset-unhide">here</a> to unhide it' ));
+ $newRender.find( '.dataset-deleted-msg' ).append([
+ br, link( _l( 'Undelete it' ), 'dataset-undelete' ), p,
+ br, link( _l( 'Permanently remove it from disk' ), 'dataset-purge' ), p
+ ].join( '' ));
+
+ $newRender.find( '.dataset-hidden-msg' ).append([
+ br, link( _l( 'Unhide it' ), 'dataset-unhide' ), p
+ ].join( '' ));
return $newRender;
},
diff -r e7ec387cb3b3b8e6aaceade0c8bdd3da0077ac57 -r 008c26226230353a2440e6c7208de58575cfebff static/scripts/mvc/history/current-history-panel.js
--- a/static/scripts/mvc/history/current-history-panel.js
+++ b/static/scripts/mvc/history/current-history-panel.js
@@ -221,7 +221,7 @@
$emptyMsg.empty();
$emptyMsg.html([
- _l( 'This history is empty. ' ), _l( 'You can ' ),
+ _l( 'This history is empty' ), '. ', _l( 'You can ' ),
'<a class="uploader-link" href="javascript:void(0)">',
_l( 'load your own data' ),
'</a>',
diff -r e7ec387cb3b3b8e6aaceade0c8bdd3da0077ac57 -r 008c26226230353a2440e6c7208de58575cfebff static/scripts/mvc/history/history-panel.js
--- a/static/scripts/mvc/history/history-panel.js
+++ b/static/scripts/mvc/history/history-panel.js
@@ -137,7 +137,7 @@
panel.toggleHDAAnnotationEditors( false, panel.fxSpeed );
},
$activator : faIconButton({
- title : _l( 'Edit history Annotation' ),
+ title : _l( 'Edit history annotation' ),
classes : 'history-annotate-btn',
faIcon : 'fa-comment'
}).appendTo( $where.find( '.history-secondary-actions' ) )
diff -r e7ec387cb3b3b8e6aaceade0c8bdd3da0077ac57 -r 008c26226230353a2440e6c7208de58575cfebff static/scripts/mvc/history/readonly-history-panel.js
--- a/static/scripts/mvc/history/readonly-history-panel.js
+++ b/static/scripts/mvc/history/readonly-history-panel.js
@@ -249,7 +249,7 @@
/** Modify an error message to be fancy and wear a monocle. */
_bePolite : function( msg ){
msg = msg || _l( 'An error occurred while getting updates from the server' );
- return msg + '. ' + _l( 'Please contact a Galaxy administrator if the problem persists.' );
+ return msg + '. ' + _l( 'Please contact a Galaxy administrator if the problem persists' ) + '.';
},
// ------------------------------------------------------------------------ loading history/hda models
@@ -284,6 +284,7 @@
return panel._loadHistoryFromXHR( xhr, attributes )
.fail( function( xhr, where, history ){
// throw an error up for the error handler
+//TODO: difficult to localize - use template
panel.trigger( 'error', panel, xhr, attributes, _l( 'An error was encountered while ' + where ),
{ historyId: historyId, history: history || {} });
})
@@ -1002,8 +1003,8 @@
'</div>',
'<div class="quota-message errormessage">',
- _l( 'You are over your disk quota.' ),
- _l( 'Tool execution is on hold until your disk usage drops below your allocated quota.' ),
+ _l( 'You are over your disk quota' ), '. ',
+ _l( 'Tool execution is on hold until your disk usage drops below your allocated quota' ), '.',
'</div>',
'<div class="tags-display"></div>',
@@ -1024,7 +1025,7 @@
// where the datasets/hdas are added
'<div class="datasets-list"></div>',
'<div class="empty-history-message infomessagesmall">',
- _l( 'Your history is empty. Click \'Get Data\' on the left pane to start' ),
+ _l( 'This history is empty' ),
'</div>'
].join( '' );
diff -r e7ec387cb3b3b8e6aaceade0c8bdd3da0077ac57 -r 008c26226230353a2440e6c7208de58575cfebff static/scripts/mvc/tags.js
--- a/static/scripts/mvc/tags.js
+++ b/static/scripts/mvc/tags.js
@@ -1,6 +1,7 @@
define([
- "mvc/base-mvc"
-], function( baseMVC ){
+ "mvc/base-mvc",
+ "utils/localization"
+], function( baseMVC, _l ){
// =============================================================================
/** A view on any model that has a 'tags' attribute (a list of tag strings)
* Incorporates the select2 jQuery plugin for tags display/editing:
diff -r e7ec387cb3b3b8e6aaceade0c8bdd3da0077ac57 -r 008c26226230353a2440e6c7208de58575cfebff static/scripts/nls/ja/locale.js
--- a/static/scripts/nls/ja/locale.js
+++ b/static/scripts/nls/ja/locale.js
@@ -1,389 +1,217 @@
/** ja localization */
define({
-// templates/history/options.mako:24
-"Are you sure you want to delete the current history?" :
-"現在のヒストリーを消すことに同意しますか?",
+// ---------------------------------------------------------------------------- histories
+// ---- history-model
+// ---- readonly-history-panel
+"This history is empty" :
+"ヒストリーは空です",
+"No matching datasets found" :
+"一致するデータセットが見つかりませんでした",
+//"An error occurred while getting updates from the server" :
+//false,
+//"Please contact a Galaxy administrator if the problem persists" :
+//false,
+//TODO:
+//"An error was encountered while <% where %>" :
+//false,
+"Search datasets" :
+"データセットを検索する",
+"You are currently viewing a deleted history!" :
+"消去したヒストリーをみています。",
+"You are over your disk quota" :
+"あなたはディスククォータを超えている",
+//"Tool execution is on hold until your disk usage drops below your allocated quota" :
+//false,
+"All" :
+"一式",
+"None" :
+"なし",
+"For all selected" :
+"各項目を",
-// templates/root/history.mako:38
-"collapse all" :
-"すべてをおりたたむ",
+// ---- history-panel
+//"Edit history tags" :
+//false,
+//"Edit history Annotation" :
+//false,
+"Click to rename history" :
+"ヒストリーの名前を変更するにはクリック",
+// multi operations
+"Operations on multiple datasets" :
+"複数のデータセットに対する操作",
+//"Hide datasets" :
+//false,
+//"Unhide datasets" :
+//false,
+//"Delete datasets" :
+//false,
+//"Undelete datasets" :
+//false,
+"Permanently delete datasets" :
+"永久にデータセットを削除",
+"This will permanently remove the data in your datasets. Are you sure?" :
+"これは永久にあなたのデータセット内のデータを削除します。本当に?",
-// templates/dataset/edit_attributes.mako:2
-"History Item Attributes" :
-"ヒストリーアイテム変数",
+// ---- annotated-history-panel
+"Dataset" :
+"データセット",
+//"Annotation" :
+//false,
-// templates/dataset/edit_attributes.mako:19
-"Edit Attributes" :
+// ---- current-history-panel
+"This history is empty. Click 'Get Data' on the left tool menu to start" :
+"ヒストリーは空です。解析をはじめるには、左パネルの 'データ取得' をクリック",
+"No matching datasets found" :
+"一致するデータセットが見つかりませんでした",
+"You must be logged in to create histories" :
+"ヒストリーを作成するためにはログインする必要があります",
+//TODO:
+//"You can <% loadYourOwn %> or <% externalSource %>" :
+//false,
+//"load your own data" :
+//false,
+//"get data from an external source" :
+//false,
+
+// these aren't in zh/ginga.po and the template doesn't localize
+//"Include Deleted Datasets" :
+//false,
+//"Include Hidden Datasets" :
+//false,
+
+
+// ---------------------------------------------------------------------------- datasets
+// ---- hda-model
+//"Unable to purge dataset" :
+//false,
+
+// ---- hda-base
+// display button
+//"Cannot display datasets removed from disk" :
+//false,
+//"This dataset must finish uploading before it can be viewed" :
+//false,
+//"This dataset is not yet viewable" :
+//false,
+"View data" :
+"データを表示",
+// download button
+"Download" :
+"ダウンロード",
+"Download dataset" :
+"データセットをダウンロード",
+//"Additional files" :
+//false,
+// info/show_params
+"View details" :
+"細部を表示",
+
+// dataset states
+// state: new
+//"This is a new dataset and not all of its data are available yet" :
+//false,
+// state: noPermission
+//"You do not have permission to view this dataset" :
+//false,
+// state: discarded
+//"The job creating this dataset was cancelled before completion" :
+//false,
+// state: queued
+"This job is waiting to run" :
+"ジョブは実行待ちです",
+// state: upload
+//"This dataset is currently uploading" :
+//false,
+// state: setting_metadata
+//"Metadata is being auto-detected" :
+//false,
+// state: running
+"This job is currently running" :
+"ジョブは実行中です",
+// state: paused
+//"This job is paused. Use the \"Resume Paused Jobs\" in the history menu to resume" :
+//false,
+// state: error
+"An error occurred with this dataset" :
+"このジョブの実行中に発生したエラー",
+// state: empty
+"No data" :
+"データ無し",
+// state: failed_metadata
+//"An error occurred setting the metadata for this dataset" :
+//false,
+
+// ajax error prefix
+//"There was an error getting the data for this dataset" :
+//false,
+
+// purged'd/del'd msg
+"This dataset has been deleted and removed from disk" :
+"このデータセットは、永続的にディスクから削除されました",
+"This dataset has been deleted" :
+"このデータセットは削除されました",
+"This dataset has been hidden" :
+"このデータセットは、非表示にされた",
+
+"format" :
+"フォーマット",
+"database" :
+"データベース",
+
+// ---- hda-edit
+"Edit attributes" :
"変数を編集する",
+//"Cannot edit attributes of datasets removed from disk" :
+//false,
+//"Undelete dataset to edit attributes" :
+//false,
+//"This dataset must finish uploading before it can be edited" :
+//false,
+//"This dataset is not yet editable" :
+//false,
-// templates/dataset/edit_attributes.mako:64
-"This will inspect the dataset and attempt to correct the above column values if they are not accurate." :
-"これはデータセットを調査して上記のカラムの値を修正することを試みます。",
-
-// templates/dataset/edit_attributes.mako:68
-"Required metadata values are missing. Some of these values may not be editable by the user. Selecting \"Auto-detect\" will attempt to fix these values." :
-"必要なメタデータの値が不明です。それらのいくつかの値はユーザによって編集可能にはなっていません。「自動判定」を選択するとそれらの値をただしくできるかもしれません。",
-
-// templates/dataset/edit_attributes.mako:78
-"Convert to new format" :
-"新しいフォーマットに変換する",
-
-// templates/dataset/edit_attributes.mako:84
-"Convert to" :
-"変換する",
-
-// templates/dataset/edit_attributes.mako:95
-"This will create a new dataset with the contents of this dataset converted to a new format." :
-"新しいフォーマットに変換したデータセットを新規作成します。",
-
-// templates/dataset/edit_attributes.mako:111
-"Change data type" :
-"データタイプを変更する",
-
-// templates/dataset/edit_attributes.mako:117
-"New Type" :
-"新しいタイプ",
-
-// templates/dataset/edit_attributes.mako:124
-"This will change the datatype of the existing dataset but <i>not</i> modify its contents. Use this if Galaxy has incorrectly guessed the type of your dataset." :
-"これは既存のデータセットのデータタイプを変更します。しかしデータセットの中身は変更しません。データセットのタイプの誤判定があったときに使用します。",
-
-// templates/dataset/edit_attributes.mako:137
-"Copy History Item" :
-"ヒストリーアイテムをコピーする",
-
-// templates/history/list.mako:3
-"Your saved histories" :
-"保存したヒストリー",
-
-// templates/history/list.mako:19
-"Stored Histories" :
-"格納してあるヒストリー",
-
-// templates/history/list.mako:21 templates/root/history.mako:239
-"hide deleted" :
-"削除したヒストリーを隠す",
-
-// templates/history/list.mako:23
-"show deleted" :
-"削除したヒストリーを表示する",
-
-// templates/history/list.mako:27
-"Name" :
-"名前",
-
-// templates/history/list.mako:27
-"Size" :
-"サイズ",
-
-// templates/history/list.mako:27
-"Last modified" :
-"最終更新日",
-
-// templates/history/list.mako:27
-"Actions" :
-"操作",
-
-// templates/history/list.mako:45
-"rename" :
-"名称変更する",
-
-// templates/history/list.mako:46
-"switch to" :
-"変更する",
-
-// templates/history/list.mako:47
-"delete" :
-"削除する",
-
-// templates/history/list.mako:49
-"undelete" :
-"削除から戻す",
-
-// templates/history/list.mako:55
-"Action" :
-"操作",
-
-// templates/history/list.mako:56 templates/history/options.mako:21
-"Share" :
-"共有",
-
-// templates/history/list.mako:56 templates/history/options.mako:15
-"Rename" :
-"名称変更する",
-
-// templates/history/list.mako:56 templates/history/options.mako:24
"Delete" :
"削除する",
+//"Dataset is already deleted" :
+//false,
-// templates/history/list.mako:58
-"Undelete" :
-"削除から戻す",
+"View or report this error" :
+"このエラーを届け出る",
-// templates/history/list.mako:65
-"You have no stored histories" :
-"保管してあるヒストリーはありません",
+"Run this job again" :
+"もう一度このジョブを実行する",
-// templates/history/options.mako:5
-"History Options" :
-"ヒストリーオプション",
+"Visualize" :
+"可視化する",
+//"Visualize in" :
+//false,
-// templates/history/options.mako:9
-"You must be " :
-"あなたは",
+"Undelete it" :
+"復元する",
+"Permanently remove it from disk" :
+"永久にディスクから削除",
+"Unhide it" :
+"非表示解除する",
-// templates/history/options.mako:9
-"logged in" :
-"ログイン",
+//"You may be able to" :
+//false,
+//"set it manually or retry auto-detection" :
+//false,
-// templates/history/options.mako:9
-" to store or switch histories." :
-"しないとヒストリーの保管や変更ができません。",
+//"Edit dataset tags" :
+//false,
+//"Edit dataset annotation" :
+//false,
-// templates/history/options.mako:15
-// python-format
-" current history (stored as \"%s\")" :
-" 現在のヒストリー(\"%s\" として保管されています)",
-// templates/history/options.mako:16
-"List" :
-"リストする",
+// ---------------------------------------------------------------------------- misc. MVC
+//"Tags" :
+//false,
+//"Annotation" :
+//false,
+//"Edit annotation" :
+//false,
-// templates/history/options.mako:16
-" previously stored histories" :
-" 以前に保管したヒストリー",
-// templates/history/options.mako:18
-"Create" :
-"作成する",
-
-// templates/history/options.mako:18
-" a new empty history" :
-" 新規ヒストリー",
-
-// templates/history/options.mako:20
-"Construct workflow" :
-"ワークフローを構築する",
-
-// templates/history/options.mako:20
-" from the current history" :
-" 現在のヒストリーから",
-
-// templates/history/options.mako:21 templates/history/options.mako:24
-" current history" :
-" 現在のヒストリー",
-
-// templates/history/options.mako:23
-"Show deleted" :
-"削除したヒストリーを表示する",
-
-// templates/history/options.mako:23
-" datasets in history" :
-" ヒストリーのデータセット",
-
-// templates/history/rename.mako:3 templates/history/rename.mako:6
-"Rename History" :
-"ヒストリーの名称変更をする",
-
-
-"Rename Histories" :
-"名称変更する",
-
-"Perform Action" :
-"操作を実行する",
-
-"Submit" :
-"登録する",
-
-
-
-// templates/history/rename.mako:10
-"Current Name" :
-"現在の名称",
-
-// templates/history/rename.mako:10
-"New Name" :
-"新しい名称",
-
-// templates/history/share.mako:3
-"Share histories" :
-"ヒストリーを共有する",
-
-// templates/history/share.mako:6
-"Share Histories" :
-"ヒストリーを共有する",
-
-// templates/history/share.mako:9
-"History Name:" :
-"ヒストリー名",
-
-// templates/history/share.mako:9
-"Number of Datasets:" :
-"データセット数",
-
-// templates/history/share.mako:9
-"Share Link" :
-"共有リンク",
-
-// templates/history/share.mako:15
-"This history contains no data." :
-"このヒストリーにはデータがありません。",
-
-// templates/history/share.mako:21
-"copy link to share" :
-"共有リンクをコピーする",
-
-// templates/history/share.mako:24
-"Email of User to share with:" :
-"共有したいユーザのEメール:",
-//"つぎのヒストリーを共有するユーザのEメールアドレス:"
-
-// templates/root/history.mako:7
-"Galaxy History" :
-"Galaxy ヒストリー",
-
-// templates/root/history.mako:237
-"refresh" :
-"リフレッシュ",
-
-// templates/root/history.mako:245
-"You are currently viewing a deleted history!" :
-"消去したヒストリーをみています。",
-
-// templates/root/history.mako:289
-"Your history is empty. Click 'Get Data' on the left pane to start" :
-"ヒストリーは空です。解析をはじめるには、左パネルの 'データ取得' をクリック",
-
-// templates/root/history_common.mako:41
-"Job is waiting to run" :
-"ジョブは実行待ちです",
-
-// templates/root/history_common.mako:43
-"Job is currently running" :
-"ジョブは実行中です",
-
-// templates/root/history_common.mako:46
-"An error occurred running this job: " :
-"このジョブの実行中に発生したエラー: ",
-
-// templates/root/history_common.mako:47
-"report this error" :
-"このエラーを報告する",
-
-// templates/root/history_common.mako:54
-"No data: " :
-"データ無し: ",
-
-// templates/root/history_common.mako:58
-"format: " :
-"フォーマット: ",
-
-// templates/root/history_common.mako:59
-"database: " :
-"データベース: ",
-
-// templates/root/history_common.mako:66 templates/root/masthead.mako:20
-"Info: " :
-"情報: ",
-
-// templates/root/history_common.mako:85
-// python-format
-"Error: unknown dataset state \"%s\"." :
-"エラー: 不明なデータ状態 \"%s\"。",
-
-// templates/root/index.mako:32
-"Options" :
-"オプション",
-
-// templates/root/index.mako:34
-"History" :
-"ヒストリー",
-
-// templates/root/masthead.mako:20
-"report bugs" :
-"バグを報告する",
-
-// templates/root/masthead.mako:21
-"wiki" :
-"wiki",
-
-// templates/root/masthead.mako:22
-"screencasts" :
-"スクリーンキャスト",
-
-// templates/root/masthead.mako:23
-"blog" :
-"ブログ",
-
-// templates/root/masthead.mako:31
-// python-format
-"Logged in as %s: " :
-"%s としてログイン中: ",
-
-// templates/root/masthead.mako:31
-"manage" :
-"管理",
-
-// templates/root/masthead.mako:32
-"logout" :
-"ログアウト",
-
-// templates/root/masthead.mako:34
-"Account: " :
-"アカウント: ",
-
-// templates/root/masthead.mako:34
-"create" :
-"作成",
-
-// templates/root/masthead.mako:35
-"login" :
-"ログイン",
-
-// templates/root/tool_menu.mako:52
-"Galaxy Tools" :
-"Galaxy ツール群",
-
-// templates/root/tool_menu.mako:129
-"Workflow" :
-"ワークフロー",
-
-// templates/root/tool_menu.mako:134
-"Manage" :
-"管理",
-
-// templates/root/tool_menu.mako:134
-"workflows" :
-"ワークフロー",
-
-// templates/user/index.mako:2 templates/user/index.mako:4
-"Account settings" :
-"アカウント設定",
-
-// templates/user/index.mako:7
-// python-format
-"You are currently logged in as %s." :
-"%s としてログイン中。",
-
-// templates/user/index.mako:9
-"Change your password" :
-"パスワード変更",
-
-// templates/user/index.mako:10
-"Update your email address" :
-"メールアドレス変更",
-
-// templates/user/index.mako:11
-"Logout" :
-"ログアウト",
-
-// templates/user/index.mako:16
-"Login" :
-"ログイン",
-
-// templates/user/index.mako:17
-"Create new account" :
-"新規アカウントを作成する"
-
-})
+});
diff -r e7ec387cb3b3b8e6aaceade0c8bdd3da0077ac57 -r 008c26226230353a2440e6c7208de58575cfebff static/scripts/nls/locale.js
--- a/static/scripts/nls/locale.js
+++ b/static/scripts/nls/locale.js
@@ -1,514 +1,219 @@
/** en/main localization hash - for use with requirejs' i18n plugin */
define({
root : {
-// ---------------------------------------------------------------------------- localized
-"history" :
+
+// ---------------------------------------------------------------------------- histories
+// ---- history-model
+// ---- readonly-history-panel
+"This history is empty" :
+false,
+"No matching datasets found" :
+false,
+"An error occurred while getting updates from the server" :
+false,
+"Please contact a Galaxy administrator if the problem persists" :
+false,
+//TODO:
+//"An error was encountered while <% where %>" :
+//false,
+"Search datasets" :
+false,
+"You are currently viewing a deleted history!" :
+false,
+"You are over your disk quota" :
+false,
+"Tool execution is on hold until your disk usage drops below your allocated quota" :
+false,
+"All" :
+false,
+"None" :
+false,
+"For all selected" :
false,
-// templates/history/options.mako:24
-"Are you sure you want to delete the current history?" :
+// ---- history-panel
+"Edit history tags" :
+false,
+"Edit history Annotation" :
+false,
+"Click to rename history" :
+false,
+// multi operations
+"Operations on multiple datasets" :
+false,
+"Hide datasets" :
+false,
+"Unhide datasets" :
+false,
+"Delete datasets" :
+false,
+"Undelete datasets" :
+false,
+"Permanently delete datasets" :
+false,
+"This will permanently remove the data in your datasets. Are you sure?" :
false,
-// templates/root/history.mako:38
-"collapse all" :
+// ---- annotated-history-panel
+"Dataset" :
+false,
+"Annotation" :
false,
-// templates/dataset/edit_attributes.mako:2
-"History Item Attributes" :
+// ---- current-history-panel
+"This history is empty. Click 'Get Data' on the left tool menu to start" :
+false,
+"No matching datasets found" :
+false,
+"You must be logged in to create histories" :
+false,
+//TODO:
+//"You can <% loadYourOwn %> or <% externalSource %>" :
+//false,
+//"load your own data" :
+//false,
+//"get data from an external source" :
+//false,
+
+// these aren't in zh/ginga.po and the template doesn't localize
+//"Include Deleted Datasets" :
+//false,
+//"Include Hidden Datasets" :
+//false,
+
+
+// ---------------------------------------------------------------------------- datasets
+// ---- hda-model
+"Unable to purge dataset" :
false,
-// templates/dataset/edit_attributes.mako:19
-"Edit Attributes" :
+// ---- hda-base
+// display button
+"Cannot display datasets removed from disk" :
+false,
+"This dataset must finish uploading before it can be viewed" :
+false,
+"This dataset is not yet viewable" :
+false,
+"View data" :
+false,
+// download button
+"Download" :
+false,
+"Download dataset" :
+false,
+"Additional files" :
+false,
+// info/show_params
+"View details" :
false,
-// templates/dataset/edit_attributes.mako:64
-"This will inspect the dataset and attempt to correct the above column values if they are not accurate." :
+// dataset states
+// state: new
+"This is a new dataset and not all of its data are available yet" :
+false,
+// state: noPermission
+"You do not have permission to view this dataset" :
+false,
+// state: discarded
+"The job creating this dataset was cancelled before completion" :
+false,
+// state: queued
+"This job is waiting to run" :
+false,
+// state: upload
+"This dataset is currently uploading" :
+false,
+// state: setting_metadata
+"Metadata is being auto-detected" :
+false,
+// state: running
+"This job is currently running" :
+false,
+// state: paused
+"This job is paused. Use the \"Resume Paused Jobs\" in the history menu to resume" :
+false,
+// state: error
+"An error occurred with this dataset" :
+false,
+// state: empty
+"No data" :
+false,
+// state: failed_metadata
+"An error occurred setting the metadata for this dataset" :
false,
-// templates/dataset/edit_attributes.mako:68
-"Required metadata values are missing. Some of these values may not be editable by the user. Selecting \"Auto-detect\" will attempt to fix these values." :
-false,
-
-// templates/dataset/edit_attributes.mako:78
-"Convert to new format" :
-false,
-
-// templates/dataset/edit_attributes.mako:84
-"Convert to" :
-false,
-
-// templates/dataset/edit_attributes.mako:95
-"This will create a new dataset with the contents of this dataset converted to a new format." :
-false,
-
-// templates/dataset/edit_attributes.mako:111
-"Change data type" :
-false,
-
-// templates/dataset/edit_attributes.mako:117
-"New Type" :
-false,
-
-// templates/dataset/edit_attributes.mako:124
-"This will change the datatype of the existing dataset but <i>not</i> modify its contents. Use this if Galaxy has incorrectly guessed the type of your dataset." :
-false,
-
-// templates/dataset/edit_attributes.mako:137
-"Copy History Item" :
-false,
-
-// templates/history/list.mako:3
-"Your saved histories" :
-false,
-
-// templates/history/list.mako:19
-"Stored Histories" :
-false,
-
-// templates/history/list.mako:21 templates/root/history.mako:239
-"hide deleted" :
-false,
-
-// templates/history/list.mako:23
-"show deleted" :
-false,
-
-// templates/history/list.mako:27
-"Name" :
-false,
-
-// templates/history/list.mako:27
-"Size" :
-false,
-
-// templates/history/list.mako:27
-"Last modified" :
-false,
-
-// templates/history/list.mako:27
-"Actions" :
-false,
-
-// templates/history/list.mako:45
-"rename" :
-false,
-
-// templates/history/list.mako:46
-"switch to" :
-false,
-
-// templates/history/list.mako:47
-"delete" :
-false,
-
-// templates/history/list.mako:49
-"undelete" :
-false,
-
-// templates/history/list.mako:55
-"Action" :
-false,
-
-// templates/history/list.mako:56 templates/history/options.mako:21
-"Share" :
-false,
-
-// templates/history/list.mako:56 templates/history/options.mako:15
-"Rename" :
-false,
-
-// templates/history/list.mako:56 templates/history/options.mako:24
-"Delete" :
-false,
-
-// templates/history/list.mako:58
-"Undelete" :
-false,
-
-// templates/history/list.mako:65
-"You have no stored histories" :
-false,
-
-// templates/history/options.mako:5
-"History Options" :
-false,
-
-// templates/history/options.mako:9
-"You must be " :
-false,
-
-// templates/history/options.mako:9
-"logged in" :
-false,
-
-// templates/history/options.mako:9
-" to store or switch histories." :
-false,
-
-// templates/history/options.mako:15
-// python-format
-" current history (stored as \"%s\")" :
-false,
-
-// templates/history/options.mako:16
-"List" :
-false,
-
-// templates/history/options.mako:16
-" previously stored histories" :
-false,
-
-// templates/history/options.mako:18
-"Create" :
-false,
-
-// templates/history/options.mako:18
-" a new empty history" :
-false,
-
-// templates/history/options.mako:20
-"Construct workflow" :
-false,
-
-// templates/history/options.mako:20
-" from the current history" :
-false,
-
-// templates/history/options.mako:21 templates/history/options.mako:24
-" current history" :
-false,
-
-// templates/history/options.mako:23
-"Show deleted" :
-false,
-
-// templates/history/options.mako:23
-" datasets in history" :
-false,
-
-// templates/history/rename.mako:3 templates/history/rename.mako:6
-"Rename History" :
-false,
-
-
-"Rename Histories" :
-false,
-
-"Perform Action" :
-false,
-
-"Submit" :
-false,
-
-// templates/history/rename.mako:10
-"Current Name" :
-false,
-
-// templates/history/rename.mako:10
-"New Name" :
-false,
-
-// templates/history/share.mako:3
-"Share histories" :
-false,
-
-// templates/history/share.mako:6
-"Share Histories" :
-false,
-
-// templates/history/share.mako:9
-"History Name:" :
-false,
-
-// templates/history/share.mako:9
-"Number of Datasets:" :
-false,
-
-// templates/history/share.mako:9
-"Share Link" :
-false,
-
-// templates/history/share.mako:15
-"This history contains no data." :
-false,
-
-// templates/history/share.mako:21
-"copy link to share" :
-false,
-
-// templates/history/share.mako:24
-"Email of User to share with:" :
-false,
-
-// templates/root/history.mako:7
-"Galaxy History" :
-false,
-
-// templates/root/history.mako:237
-"refresh" :
-false,
-
-// templates/root/history.mako:245
-"You are currently viewing a deleted history!" :
-false,
-
-// templates/root/history.mako:289
-"Your history is empty. Click 'Get Data' on the left pane to start" :
-false,
-
-// templates/root/history_common.mako:41
-"Job is waiting to run" :
-false,
-
-// templates/root/history_common.mako:43
-"Job is currently running" :
-false,
-
-// templates/root/history_common.mako:46
-"An error occurred running this job: " :
-false,
-
-// templates/root/history_common.mako:47
-"report this error" :
-false,
-
-// templates/root/history_common.mako:54
-"No data: " :
-false,
-
-// templates/root/history_common.mako:58
-"format: " :
-false,
-
-// templates/root/history_common.mako:59
-"database: " :
-false,
-
-// templates/root/history_common.mako:66 templates/root/masthead.mako:20
-"Info: " :
-false,
-
-// templates/root/history_common.mako:85
-// python-format
-"Error: unknown dataset state \"%s\"." :
-false,
-
-// templates/root/index.mako:32
-"Options" :
-false,
-
-// templates/root/index.mako:34
-"History" :
-false,
-
-// templates/root/masthead.mako:20
-"report bugs" :
-false,
-
-// templates/root/masthead.mako:21
-"wiki" :
-false,
-
-// templates/root/masthead.mako:22
-"screencasts" :
-false,
-
-// templates/root/masthead.mako:23
-"blog" :
-false,
-
-// templates/root/masthead.mako:31
-// python-format
-"Logged in as %s: " :
-false,
-
-// templates/root/masthead.mako:31
-"manage" :
-false,
-
-// templates/root/masthead.mako:32
-"logout" :
-false,
-
-// templates/root/masthead.mako:34
-"Account: " :
-false,
-
-// templates/root/masthead.mako:34
-"create" :
-false,
-
-// templates/root/masthead.mako:35
-"login" :
-false,
-
-// templates/root/tool_menu.mako:52
-"Galaxy Tools" :
-false,
-
-// templates/root/tool_menu.mako:129
-"Workflow" :
-false,
-
-// templates/root/tool_menu.mako:134
-"Manage" :
-false,
-
-// templates/root/tool_menu.mako:134
-"workflows" :
-false,
-
-// templates/user/index.mako:2 templates/user/index.mako:4
-"Account settings" :
-false,
-
-// templates/user/index.mako:7
-// python-format
-"You are currently logged in as %s." :
-false,
-
-// templates/user/index.mako:9
-"Change your password" :
-false,
-
-// templates/user/index.mako:10
-"Update your email address" :
-false,
-
-// templates/user/index.mako:11
-"Logout" :
-false,
-
-// templates/user/index.mako:16
-"Login" :
-false,
-
-// templates/user/index.mako:17
-"Create new account" :
-false,
-
-
-// ---------------------------------------------------------------------------- need to be localized
-"anonymous user" :
-false,
-
-"Using " :
-false,
-
+// ajax error prefix
"There was an error getting the data for this dataset" :
false,
-"This dataset has been deleted and removed from disk." :
+// purged'd/del'd msg
+"This dataset has been deleted and removed from disk" :
false,
-
-"This dataset has been deleted." :
+"This dataset has been deleted" :
false,
-
-"This dataset has been hidden." :
+"This dataset has been hidden" :
false,
"format" :
false,
-
"database" :
false,
-"This history is empty" :
+// ---- hda-edit
+"Edit attributes" :
+false,
+"Cannot edit attributes of datasets removed from disk" :
+false,
+"Undelete dataset to edit attributes" :
+false,
+"This dataset must finish uploading before it can be edited" :
+false,
+"This dataset is not yet editable" :
false,
-"No matching datasets found" :
+"Delete" :
+false,
+"Dataset is already deleted" :
false,
-"You are over your disk quota." :
-false,
-
-"Tool execution is on hold until your disk usage drops below your allocated quota." :
-false,
-
-"All" :
-false,
-
-"None" :
-false,
-
-"For all selected" :
-false,
-
-"This history is empty. Click 'Get Data' on the left tool menu to start" :
-false,
-
-"Include Deleted Datasets" :
-false,
-
-"Include Hidden Datasets" :
-false,
-
-"Edit history tags" :
-false,
-
-"Edit history Annotation" :
-false,
-
-"Operations on multiple datasets" :
-false,
-
-"Search datasets" :
-false,
-
-"clear search (esc)" :
-false,
-
-"loading..." :
-false,
-
-"Hide datasets" :
-false,
-
-"Unhide datasets" :
-false,
-
-"Delete datasets" :
-false,
-
-"Undelete datasets" :
-false,
-
-"Permanently delete datasets" :
-false,
-
-"Click to rename history" :
-false,
-
-"View data" :
-false,
-
-"Edit attributes" :
-false,
-
-"View details" :
+"View or report this error" :
false,
"Run this job again" :
false,
+"Visualize" :
+false,
+"Visualize in" :
+false,
+
+"Undelete it" :
+false,
+"Permanently remove it from disk" :
+false,
+"Unhide it" :
+false,
+
+"You may be able to" :
+false,
+"set it manually or retry auto-detection" :
+false,
+
"Edit dataset tags" :
false,
-
"Edit dataset annotation" :
false,
-" Click <a href=\"javascript:void(0);\" class=\"dataset-undelete\">here</a> to undelete it or <a href=\"javascript:void(0);\" class=\"dataset-purge\">here</a> to immediately remove it from disk" :
+
+// ---------------------------------------------------------------------------- misc. MVC
+"Tags" :
false,
-
-" Click <a href=\"javascript:void(0);\" class=\"dataset-unhide\">here</a> to unhide it" :
+"Annotation" :
false,
-
-"Download" :
+"Edit annotation" :
false,
-"Visualize" :
-false
-
// ----------------------------------------------------------------------------
},
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: greg: More quickly stop the process of checking for migrated tools when starting Galaxy if there is no internet connection to the tool shed.
by commits-noreply@bitbucket.org 22 Apr '14
by commits-noreply@bitbucket.org 22 Apr '14
22 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e7ec387cb3b3/
Changeset: e7ec387cb3b3
User: greg
Date: 2014-04-22 17:45:10
Summary: More quickly stop the process of checking for migrated tools when starting Galaxy if there is no internet connection to the tool shed.
Affected #: 1 file
diff -r db35188ff312ac07b608bb51eef668fc8691dde8 -r e7ec387cb3b3b8e6aaceade0c8bdd3da0077ac57 lib/tool_shed/util/common_util.py
--- a/lib/tool_shed/util/common_util.py
+++ b/lib/tool_shed/util/common_util.py
@@ -45,30 +45,37 @@
repository_name,
REPOSITORY_OWNER,
changeset_revision )
- # Accumulate all tool dependencies defined for repository dependencies for display to the user.
- for rd_key, rd_tups in repository_dependencies_dict.items():
- if rd_key in [ 'root_key', 'description' ]:
- continue
- for rd_tup in rd_tups:
- tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
- parse_repository_dependency_tuple( rd_tup )
+ if tool_shed_accessible:
+ # Accumulate all tool dependencies defined for repository dependencies for display to the user.
+ for rd_key, rd_tups in repository_dependencies_dict.items():
+ if rd_key in [ 'root_key', 'description' ]:
+ continue
+ for rd_tup in rd_tups:
+ tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
+ parse_repository_dependency_tuple( rd_tup )
+ tool_shed_accessible, tool_dependencies = get_tool_dependencies( app,
+ tool_shed_url,
+ name,
+ owner,
+ changeset_revision )
+ all_tool_dependencies = accumulate_tool_dependencies( tool_shed_accessible, tool_dependencies, all_tool_dependencies )
tool_shed_accessible, tool_dependencies = get_tool_dependencies( app,
tool_shed_url,
- name,
- owner,
+ repository_name,
+ REPOSITORY_OWNER,
changeset_revision )
all_tool_dependencies = accumulate_tool_dependencies( tool_shed_accessible, tool_dependencies, all_tool_dependencies )
- tool_shed_accessible, tool_dependencies = get_tool_dependencies( app, tool_shed_url, repository_name, REPOSITORY_OWNER, changeset_revision )
- all_tool_dependencies = accumulate_tool_dependencies( tool_shed_accessible, tool_dependencies, all_tool_dependencies )
- for tool_elem in elem.findall( 'tool' ):
- tool_config_file_name = tool_elem.get( 'file' )
- if tool_config_file_name:
- # We currently do nothing with repository dependencies except install them (we do not display repositories that will be
- # installed to the user). However, we'll store them in the following dictionary in case we choose to display them in the
- # future.
- dependencies_dict = dict( tool_dependencies=all_tool_dependencies,
- repository_dependencies=repository_dependencies )
- migrated_tool_configs_dict[ tool_config_file_name ] = dependencies_dict
+ for tool_elem in elem.findall( 'tool' ):
+ tool_config_file_name = tool_elem.get( 'file' )
+ if tool_config_file_name:
+ # We currently do nothing with repository dependencies except install them (we do not display repositories that will be
+ # installed to the user). However, we'll store them in the following dictionary in case we choose to display them in the
+ # future.
+ dependencies_dict = dict( tool_dependencies=all_tool_dependencies,
+ repository_dependencies=repository_dependencies )
+ migrated_tool_configs_dict[ tool_config_file_name ] = dependencies_dict
+ else:
+ break
if tool_shed_accessible:
# Parse the proprietary tool_panel_configs (the default is tool_conf.xml) and generate the list of missing tool config file names.
for tool_panel_config in tool_panel_configs:
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: martenson: rename biostar menu link
by commits-noreply@bitbucket.org 22 Apr '14
by commits-noreply@bitbucket.org 22 Apr '14
22 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/db35188ff312/
Changeset: db35188ff312
User: martenson
Date: 2014-04-22 16:53:35
Summary: rename biostar menu link
Affected #: 2 files
diff -r aac938e8ba6f11adaa2240560a13cca7263025b0 -r db35188ff312ac07b608bb51eef668fc8691dde8 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -195,7 +195,7 @@
if (this.options.biostar_url)
{
tab_help.add({
- title : "Galaxy Q&A Site",
+ title : "Galaxy Biostar",
content : this.options.biostar_url_redirect,
target : "_blank"
});
diff -r aac938e8ba6f11adaa2240560a13cca7263025b0 -r db35188ff312ac07b608bb51eef668fc8691dde8 static/scripts/packed/galaxy.menu.js
--- a/static/scripts/packed/galaxy.menu.js
+++ b/static/scripts/packed/galaxy.menu.js
@@ -1,1 +1,1 @@
-define(["galaxy.masthead"],function(b){var a=Backbone.Model.extend({options:null,masthead:null,initialize:function(c){this.options=c.config;this.masthead=c.masthead;this.create()},create:function(){var e=new b.GalaxyMastheadTab({id:"analysis",title:"Analyze Data",content:"root/index",title_attribute:"Analysis home view"});this.masthead.append(e);var g={id:"workflow",title:"Workflow",content:"workflow",title_attribute:"Chain tools into workflows"};if(!this.options.user.valid){g.disabled=true}var d=new b.GalaxyMastheadTab(g);this.masthead.append(d);var i=new b.GalaxyMastheadTab({id:"shared",title:"Shared Data",content:"library/index",title_attribute:"Access published resources"});i.add({title:"Data Libraries",content:"library/index"});i.add({title:"Data Libraries Beta",content:"library/list",divider:true});i.add({title:"Published Histories",content:"history/list_published"});i.add({title:"Published Workflows",content:"workflow/list_published"});i.add({title:"Published Visualizations",content:"visualization/list_published"});i.add({title:"Published Pages",content:"page/list_published"});this.masthead.append(i);if(this.options.user.requests){var j=new b.GalaxyMastheadTab({id:"lab",title:"Lab"});j.add({title:"Sequencing Requests",content:"requests/index"});j.add({title:"Find Samples",content:"requests/find_samples_index"});j.add({title:"Help",content:this.options.lims_doc_url});this.masthead.append(j)}var c={id:"visualization",title:"Visualization",content:"visualization/list",title_attribute:"Visualize datasets"};if(!this.options.user.valid){c.disabled=true}var m=new b.GalaxyMastheadTab(c);if(this.options.user.valid){m.add({title:"New Track Browser",content:"visualization/trackster",target:"_frame"});m.add({title:"Saved Visualizations",content:"visualization/list",target:"_frame"})}this.masthead.append(m);if(this.options.enable_cloud_launch){var f=new b.GalaxyMastheadTab({id:"cloud",title:"Cloud",content:"cloudlaunch/index"});f.add({title:"New Cloud Cluster",content:"cloudlaunch/index"});this.masthead.append(f)}if(this.options.is_admin_user){var h=new b.GalaxyMastheadTab({id:"admin",title:"Admin",content:"admin/index",extra_class:"admin-only",title_attribute:"Administer this Galaxy"});this.masthead.append(h)}var l=new b.GalaxyMastheadTab({id:"help",title:"Help",title_attribute:"Support, contact, and community hubs"});if(this.options.biostar_url){l.add({title:"Galaxy Q&A Site",content:this.options.biostar_url_redirect,target:"_blank"});l.add({title:"Ask a question",content:"biostar/biostar_question_redirect",target:"_blank"})}l.add({title:"Support",content:this.options.support_url,target:"_blank"});l.add({title:"Search",content:this.options.search_url,target:"_blank"});l.add({title:"Mailing Lists",content:this.options.mailing_lists,target:"_blank"});l.add({title:"Videos",content:this.options.screencasts_url,target:"_blank"});l.add({title:"Wiki",content:this.options.wiki_url,target:"_blank"});l.add({title:"How to Cite Galaxy",content:this.options.citation_url,target:"_blank"});if(this.options.terms_url){l.add({title:"Terms and Conditions",content:this.options.terms_url,target:"_blank"})}this.masthead.append(l);if(!this.options.user.valid){var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedout-only",title_attribute:"Account registration or login"});k.add({title:"Login",content:"user/login",target:"galaxy_main"});if(this.options.allow_user_creation){k.add({title:"Register",content:"user/create",target:"galaxy_main"})}this.masthead.append(k)}else{var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedin-only",title_attribute:"Account preferences and saved data"});k.add({title:"Logged in as "+this.options.user.email});if(this.options.use_remote_user&&this.options.remote_user_logout_href){k.add({title:"Logout",content:this.options.remote_user_logout_href,target:"_top"})}else{k.add({title:"Preferences",content:"user?cntrller=user",target:"galaxy_main"});k.add({title:"Custom Builds",content:"user/dbkeys",target:"galaxy_main"});k.add({title:"Logout",content:"user/logout",target:"_top",divider:true})}k.add({title:"Saved Histories",content:"history/list",target:"galaxy_main"});k.add({title:"Saved Datasets",content:"dataset/list",target:"galaxy_main"});k.add({title:"Saved Pages",content:"page/list",target:"_top"});k.add({title:"API Keys",content:"user/api_keys?cntrller=user",target:"galaxy_main"});if(this.options.use_remote_user){k.add({title:"Public Name",content:"user/edit_username?cntrller=user",target:"galaxy_main"})}this.masthead.append(k)}if(this.options.active_view){this.masthead.highlight(this.options.active_view)}}});return{GalaxyMenu:a}});
\ No newline at end of file
+define(["galaxy.masthead"],function(b){var a=Backbone.Model.extend({options:null,masthead:null,initialize:function(c){this.options=c.config;this.masthead=c.masthead;this.create()},create:function(){var e=new b.GalaxyMastheadTab({id:"analysis",title:"Analyze Data",content:"root/index",title_attribute:"Analysis home view"});this.masthead.append(e);var g={id:"workflow",title:"Workflow",content:"workflow",title_attribute:"Chain tools into workflows"};if(!this.options.user.valid){g.disabled=true}var d=new b.GalaxyMastheadTab(g);this.masthead.append(d);var i=new b.GalaxyMastheadTab({id:"shared",title:"Shared Data",content:"library/index",title_attribute:"Access published resources"});i.add({title:"Data Libraries",content:"library/index"});i.add({title:"Data Libraries Beta",content:"library/list",divider:true});i.add({title:"Published Histories",content:"history/list_published"});i.add({title:"Published Workflows",content:"workflow/list_published"});i.add({title:"Published Visualizations",content:"visualization/list_published"});i.add({title:"Published Pages",content:"page/list_published"});this.masthead.append(i);if(this.options.user.requests){var j=new b.GalaxyMastheadTab({id:"lab",title:"Lab"});j.add({title:"Sequencing Requests",content:"requests/index"});j.add({title:"Find Samples",content:"requests/find_samples_index"});j.add({title:"Help",content:this.options.lims_doc_url});this.masthead.append(j)}var c={id:"visualization",title:"Visualization",content:"visualization/list",title_attribute:"Visualize datasets"};if(!this.options.user.valid){c.disabled=true}var m=new b.GalaxyMastheadTab(c);if(this.options.user.valid){m.add({title:"New Track Browser",content:"visualization/trackster",target:"_frame"});m.add({title:"Saved Visualizations",content:"visualization/list",target:"_frame"})}this.masthead.append(m);if(this.options.enable_cloud_launch){var f=new b.GalaxyMastheadTab({id:"cloud",title:"Cloud",content:"cloudlaunch/index"});f.add({title:"New Cloud Cluster",content:"cloudlaunch/index"});this.masthead.append(f)}if(this.options.is_admin_user){var h=new b.GalaxyMastheadTab({id:"admin",title:"Admin",content:"admin/index",extra_class:"admin-only",title_attribute:"Administer this Galaxy"});this.masthead.append(h)}var l=new b.GalaxyMastheadTab({id:"help",title:"Help",title_attribute:"Support, contact, and community hubs"});if(this.options.biostar_url){l.add({title:"Galaxy Biostar",content:this.options.biostar_url_redirect,target:"_blank"});l.add({title:"Ask a question",content:"biostar/biostar_question_redirect",target:"_blank"})}l.add({title:"Support",content:this.options.support_url,target:"_blank"});l.add({title:"Search",content:this.options.search_url,target:"_blank"});l.add({title:"Mailing Lists",content:this.options.mailing_lists,target:"_blank"});l.add({title:"Videos",content:this.options.screencasts_url,target:"_blank"});l.add({title:"Wiki",content:this.options.wiki_url,target:"_blank"});l.add({title:"How to Cite Galaxy",content:this.options.citation_url,target:"_blank"});if(this.options.terms_url){l.add({title:"Terms and Conditions",content:this.options.terms_url,target:"_blank"})}this.masthead.append(l);if(!this.options.user.valid){var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedout-only",title_attribute:"Account registration or login"});k.add({title:"Login",content:"user/login",target:"galaxy_main"});if(this.options.allow_user_creation){k.add({title:"Register",content:"user/create",target:"galaxy_main"})}this.masthead.append(k)}else{var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedin-only",title_attribute:"Account preferences and saved data"});k.add({title:"Logged in as "+this.options.user.email});if(this.options.use_remote_user&&this.options.remote_user_logout_href){k.add({title:"Logout",content:this.options.remote_user_logout_href,target:"_top"})}else{k.add({title:"Preferences",content:"user?cntrller=user",target:"galaxy_main"});k.add({title:"Custom Builds",content:"user/dbkeys",target:"galaxy_main"});k.add({title:"Logout",content:"user/logout",target:"_top",divider:true})}k.add({title:"Saved Histories",content:"history/list",target:"galaxy_main"});k.add({title:"Saved Datasets",content:"dataset/list",target:"galaxy_main"});k.add({title:"Saved Pages",content:"page/list",target:"_top"});k.add({title:"API Keys",content:"user/api_keys?cntrller=user",target:"galaxy_main"});if(this.options.use_remote_user){k.add({title:"Public Name",content:"user/edit_username?cntrller=user",target:"galaxy_main"})}this.masthead.append(k)}if(this.options.active_view){this.masthead.highlight(this.options.active_view)}}});return{GalaxyMenu:a}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Handle case where no network is available when attempting to determine repository dependencies for an installed repository.
by commits-noreply@bitbucket.org 22 Apr '14
by commits-noreply@bitbucket.org 22 Apr '14
22 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/aac938e8ba6f/
Changeset: aac938e8ba6f
User: greg
Date: 2014-04-22 16:33:42
Summary: Handle case where no network is available when attempting to determine repository dependencies for an installed repository.
Affected #: 1 file
diff -r 4f4725c1b0ef515a3b8d2e9b62322a6de3feaa32 -r aac938e8ba6f11adaa2240560a13cca7263025b0 lib/tool_shed/util/repository_dependency_util.py
--- a/lib/tool_shed/util/repository_dependency_util.py
+++ b/lib/tool_shed/util/repository_dependency_util.py
@@ -361,7 +361,11 @@
str( repository.changeset_revision ) )
url = common_util.url_join( tool_shed_url,
'repository/get_repository_dependencies%s' % params )
- raw_text = common_util.tool_shed_get( trans.app, tool_shed_url, url )
+ try:
+ raw_text = common_util.tool_shed_get( trans.app, tool_shed_url, url )
+ except Exception, e:
+ print "The URL\n%s\nraised the exception:\n%s\n" % ( url, str( e ) )
+ return ''
if len( raw_text ) > 2:
encoded_text = json.from_json_string( raw_text )
text = encoding_util.tool_shed_decode( encoded_text )
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: davebgx: Merge stable changes into default.
by commits-noreply@bitbucket.org 22 Apr '14
by commits-noreply@bitbucket.org 22 Apr '14
22 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4f4725c1b0ef/
Changeset: 4f4725c1b0ef
User: davebgx
Date: 2014-04-22 16:09:40
Summary: Merge stable changes into default.
Affected #: 1 file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dannon: Merged in dan/galaxy-central-stable-prs/stable (pull request #374)
by commits-noreply@bitbucket.org 22 Apr '14
by commits-noreply@bitbucket.org 22 Apr '14
22 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/1d06e19aa3aa/
Changeset: 1d06e19aa3aa
Branch: stable
User: dannon
Date: 2014-04-22 16:01:58
Summary: Merged in dan/galaxy-central-stable-prs/stable (pull request #374)
Prevent redirect misuse on user log in.
Affected #: 2 files
diff -r 7fe7330660189bb382191a18ee145a7698ddbb74 -r 1d06e19aa3aac0c87b21f8482b8fc73c89e277ad lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -29,6 +29,8 @@
from hashlib import md5
from itertools import izip
+from urlparse import urlparse
+
from galaxy import eggs
eggs.require( 'docutils' )
@@ -691,6 +693,17 @@
def string_to_object( s ):
return pickle.loads( binascii.unhexlify( s ) )
+def compare_urls( url1, url2, compare_scheme=True, compare_hostname=True, compare_path=True ):
+ url1 = urlparse( url1 )
+ url2 = urlparse( url2 )
+ if compare_scheme and url1.scheme and url2.scheme and url1.scheme != url2.scheme:
+ return False
+ if compare_hostname and url1.hostname and url2.hostname and url1.hostname != url2.hostname:
+ return False
+ if compare_path and url1.path and url2.path and url1.path != url2.path:
+ return False
+ return True
+
def get_ucsc_by_build(build):
sites = []
for site in ucsc_build_sites:
diff -r 7fe7330660189bb382191a18ee145a7698ddbb74 -r 1d06e19aa3aac0c87b21f8482b8fc73c89e277ad lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -445,26 +445,35 @@
return self.user_openid_grid( trans, **kwd )
@web.expose
- def login( self, trans, redirect_url='', refresh_frames=[], **kwd ):
+ def login( self, trans, refresh_frames=[], **kwd ):
'''Handle Galaxy Log in'''
redirect = kwd.get( 'redirect', trans.request.referer ).strip()
+ root_url = url_for( '/', qualified=True )
+ redirect_url = '' #always start with redirect_url being empty
+ # compare urls, to prevent a redirect from pointing (directly) outside of galaxy
+ # or to enter a logout/login loop
+ if not util.compare_urls( root_url, redirect, compare_path=False ) or util.compare_urls( url_for( controller='user', action='logout', qualified=True ), redirect ):
+ redirect = root_url
use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
message = kwd.get( 'message', '' )
status = kwd.get( 'status', 'done' )
header = ''
- user = None
+ user = trans.user
email = kwd.get( 'email', '' )
- if kwd.get( 'login_button', False ):
+ if user:
+ #already logged in
+ redirect_url = redirect
+ message = 'You are already logged in.'
+ status = 'info'
+ elif kwd.get( 'login_button', False ):
if trans.webapp.name == 'galaxy' and not refresh_frames:
if trans.app.config.require_login:
refresh_frames = [ 'masthead', 'history', 'tools' ]
else:
refresh_frames = [ 'masthead', 'history' ]
message, status, user, success = self.__validate_login( trans, **kwd )
- if success and redirect and not redirect.startswith( trans.request.base + url_for( controller='user', action='logout' ) ):
+ if success:
redirect_url = redirect
- elif success:
- redirect_url = url_for( '/' )
if not user and trans.app.config.require_login:
if trans.app.config.allow_user_creation:
create_account_str = " If you don't already have an account, <a href='%s'>you may create one</a>." % \
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/3e87c1b1c380/
Changeset: 3e87c1b1c380
Branch: stable
User: dan
Date: 2014-04-21 18:11:49
Summary: Prevent redirect misuse on user log in.
Affected #: 2 files
diff -r e6876f6918548a0805fa0cc5306ba1ced6fb184a -r 3e87c1b1c3801aac29bb9bd7709ead6f996d7dc3 lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -29,6 +29,8 @@
from hashlib import md5
from itertools import izip
+from urlparse import urlparse
+
from galaxy import eggs
eggs.require( 'docutils' )
@@ -691,6 +693,17 @@
def string_to_object( s ):
return pickle.loads( binascii.unhexlify( s ) )
+def compare_urls( url1, url2, compare_scheme=True, compare_hostname=True, compare_path=True ):
+ url1 = urlparse( url1 )
+ url2 = urlparse( url2 )
+ if compare_scheme and url1.scheme and url2.scheme and url1.scheme != url2.scheme:
+ return False
+ if compare_hostname and url1.hostname and url2.hostname and url1.hostname != url2.hostname:
+ return False
+ if compare_path and url1.path and url2.path and url1.path != url2.path:
+ return False
+ return True
+
def get_ucsc_by_build(build):
sites = []
for site in ucsc_build_sites:
diff -r e6876f6918548a0805fa0cc5306ba1ced6fb184a -r 3e87c1b1c3801aac29bb9bd7709ead6f996d7dc3 lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -445,26 +445,35 @@
return self.user_openid_grid( trans, **kwd )
@web.expose
- def login( self, trans, redirect_url='', refresh_frames=[], **kwd ):
+ def login( self, trans, refresh_frames=[], **kwd ):
'''Handle Galaxy Log in'''
redirect = kwd.get( 'redirect', trans.request.referer ).strip()
+ root_url = url_for( '/', qualified=True )
+ redirect_url = '' #always start with redirect_url being empty
+ # compare urls, to prevent a redirect from pointing (directly) outside of galaxy
+ # or to enter a logout/login loop
+ if not util.compare_urls( root_url, redirect, compare_path=False ) or util.compare_urls( url_for( controller='user', action='logout', qualified=True ), redirect ):
+ redirect = root_url
use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
message = kwd.get( 'message', '' )
status = kwd.get( 'status', 'done' )
header = ''
- user = None
+ user = trans.user
email = kwd.get( 'email', '' )
- if kwd.get( 'login_button', False ):
+ if user:
+ #already logged in
+ redirect_url = redirect
+ message = 'You are already logged in.'
+ status = 'info'
+ elif kwd.get( 'login_button', False ):
if trans.webapp.name == 'galaxy' and not refresh_frames:
if trans.app.config.require_login:
refresh_frames = [ 'masthead', 'history', 'tools' ]
else:
refresh_frames = [ 'masthead', 'history' ]
message, status, user, success = self.__validate_login( trans, **kwd )
- if success and redirect and not redirect.startswith( trans.request.base + url_for( controller='user', action='logout' ) ):
+ if success:
redirect_url = redirect
- elif success:
- redirect_url = url_for( '/' )
if not user and trans.app.config.require_login:
if trans.app.config.allow_user_creation:
create_account_str = " If you don't already have an account, <a href='%s'>you may create one</a>." % \
https://bitbucket.org/galaxy/galaxy-central/commits/6e91e5fcd113/
Changeset: 6e91e5fcd113
Branch: stable
User: dan
Date: 2014-04-21 18:12:03
Summary: Fix for masthead link for biostar mainpage.
Affected #: 3 files
diff -r 3e87c1b1c3801aac29bb9bd7709ead6f996d7dc3 -r 6e91e5fcd11351ea04789403e44002f9ccb70060 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -196,7 +196,7 @@
{
tab_help.add({
title : "Galaxy Q&A Site",
- content : this.options.biostar_url,
+ content : this.options.biostar_url_redirect,
target : "_blank"
});
tab_help.add({
diff -r 3e87c1b1c3801aac29bb9bd7709ead6f996d7dc3 -r 6e91e5fcd11351ea04789403e44002f9ccb70060 static/scripts/packed/galaxy.menu.js
--- a/static/scripts/packed/galaxy.menu.js
+++ b/static/scripts/packed/galaxy.menu.js
@@ -1,1 +1,1 @@
-define(["galaxy.masthead"],function(b){var a=Backbone.Model.extend({options:null,masthead:null,initialize:function(c){this.options=c.config;this.masthead=c.masthead;this.create()},create:function(){var e=new b.GalaxyMastheadTab({id:"analysis",title:"Analyze Data",content:"root/index",title_attribute:"Analysis home view"});this.masthead.append(e);var g={id:"workflow",title:"Workflow",content:"workflow",title_attribute:"Chain tools into workflows"};if(!this.options.user.valid){g.disabled=true}var d=new b.GalaxyMastheadTab(g);this.masthead.append(d);var i=new b.GalaxyMastheadTab({id:"shared",title:"Shared Data",content:"library/index",title_attribute:"Access published resources"});i.add({title:"Data Libraries",content:"library/index"});i.add({title:"Data Libraries Beta",content:"library/list",divider:true});i.add({title:"Published Histories",content:"history/list_published"});i.add({title:"Published Workflows",content:"workflow/list_published"});i.add({title:"Published Visualizations",content:"visualization/list_published"});i.add({title:"Published Pages",content:"page/list_published"});this.masthead.append(i);if(this.options.user.requests){var j=new b.GalaxyMastheadTab({id:"lab",title:"Lab"});j.add({title:"Sequencing Requests",content:"requests/index"});j.add({title:"Find Samples",content:"requests/find_samples_index"});j.add({title:"Help",content:this.options.lims_doc_url});this.masthead.append(j)}var c={id:"visualization",title:"Visualization",content:"visualization/list",title_attribute:"Visualize datasets"};if(!this.options.user.valid){c.disabled=true}var m=new b.GalaxyMastheadTab(c);if(this.options.user.valid){m.add({title:"New Track Browser",content:"visualization/trackster",target:"_frame"});m.add({title:"Saved Visualizations",content:"visualization/list",target:"_frame"})}this.masthead.append(m);if(this.options.enable_cloud_launch){var f=new b.GalaxyMastheadTab({id:"cloud",title:"Cloud",content:"cloudlaunch/index"});f.add({title:"New Cloud Cluster",content:"cloudlaunch/index"});this.masthead.append(f)}if(this.options.is_admin_user){var h=new b.GalaxyMastheadTab({id:"admin",title:"Admin",content:"admin/index",extra_class:"admin-only",title_attribute:"Administer this Galaxy"});this.masthead.append(h)}var l=new b.GalaxyMastheadTab({id:"help",title:"Help",title_attribute:"Support, contact, and community hubs"});if(this.options.biostar_url){l.add({title:"Galaxy Q&A Site",content:this.options.biostar_url,target:"_blank"});l.add({title:"Ask a question",content:"biostar/biostar_question_redirect",target:"_blank"})}l.add({title:"Support",content:this.options.support_url,target:"_blank"});l.add({title:"Search",content:this.options.search_url,target:"_blank"});l.add({title:"Mailing Lists",content:this.options.mailing_lists,target:"_blank"});l.add({title:"Videos",content:this.options.screencasts_url,target:"_blank"});l.add({title:"Wiki",content:this.options.wiki_url,target:"_blank"});l.add({title:"How to Cite Galaxy",content:this.options.citation_url,target:"_blank"});if(this.options.terms_url){l.add({title:"Terms and Conditions",content:this.options.terms_url,target:"_blank"})}this.masthead.append(l);if(!this.options.user.valid){var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedout-only",title_attribute:"Account registration or login"});k.add({title:"Login",content:"user/login",target:"galaxy_main"});if(this.options.allow_user_creation){k.add({title:"Register",content:"user/create",target:"galaxy_main"})}this.masthead.append(k)}else{var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedin-only",title_attribute:"Account preferences and saved data"});k.add({title:"Logged in as "+this.options.user.email});if(this.options.use_remote_user&&this.options.remote_user_logout_href){k.add({title:"Logout",content:this.options.remote_user_logout_href,target:"_top"})}else{k.add({title:"Preferences",content:"user?cntrller=user",target:"galaxy_main"});k.add({title:"Custom Builds",content:"user/dbkeys",target:"galaxy_main"});k.add({title:"Logout",content:"user/logout",target:"_top",divider:true})}k.add({title:"Saved Histories",content:"history/list",target:"galaxy_main"});k.add({title:"Saved Datasets",content:"dataset/list",target:"galaxy_main"});k.add({title:"Saved Pages",content:"page/list",target:"_top"});k.add({title:"API Keys",content:"user/api_keys?cntrller=user",target:"galaxy_main"});if(this.options.use_remote_user){k.add({title:"Public Name",content:"user/edit_username?cntrller=user",target:"galaxy_main"})}this.masthead.append(k)}if(this.options.active_view){this.masthead.highlight(this.options.active_view)}}});return{GalaxyMenu:a}});
\ No newline at end of file
+define(["galaxy.masthead"],function(b){var a=Backbone.Model.extend({options:null,masthead:null,initialize:function(c){this.options=c.config;this.masthead=c.masthead;this.create()},create:function(){var e=new b.GalaxyMastheadTab({id:"analysis",title:"Analyze Data",content:"root/index",title_attribute:"Analysis home view"});this.masthead.append(e);var g={id:"workflow",title:"Workflow",content:"workflow",title_attribute:"Chain tools into workflows"};if(!this.options.user.valid){g.disabled=true}var d=new b.GalaxyMastheadTab(g);this.masthead.append(d);var i=new b.GalaxyMastheadTab({id:"shared",title:"Shared Data",content:"library/index",title_attribute:"Access published resources"});i.add({title:"Data Libraries",content:"library/index"});i.add({title:"Data Libraries Beta",content:"library/list",divider:true});i.add({title:"Published Histories",content:"history/list_published"});i.add({title:"Published Workflows",content:"workflow/list_published"});i.add({title:"Published Visualizations",content:"visualization/list_published"});i.add({title:"Published Pages",content:"page/list_published"});this.masthead.append(i);if(this.options.user.requests){var j=new b.GalaxyMastheadTab({id:"lab",title:"Lab"});j.add({title:"Sequencing Requests",content:"requests/index"});j.add({title:"Find Samples",content:"requests/find_samples_index"});j.add({title:"Help",content:this.options.lims_doc_url});this.masthead.append(j)}var c={id:"visualization",title:"Visualization",content:"visualization/list",title_attribute:"Visualize datasets"};if(!this.options.user.valid){c.disabled=true}var m=new b.GalaxyMastheadTab(c);if(this.options.user.valid){m.add({title:"New Track Browser",content:"visualization/trackster",target:"_frame"});m.add({title:"Saved Visualizations",content:"visualization/list",target:"_frame"})}this.masthead.append(m);if(this.options.enable_cloud_launch){var f=new b.GalaxyMastheadTab({id:"cloud",title:"Cloud",content:"cloudlaunch/index"});f.add({title:"New Cloud Cluster",content:"cloudlaunch/index"});this.masthead.append(f)}if(this.options.is_admin_user){var h=new b.GalaxyMastheadTab({id:"admin",title:"Admin",content:"admin/index",extra_class:"admin-only",title_attribute:"Administer this Galaxy"});this.masthead.append(h)}var l=new b.GalaxyMastheadTab({id:"help",title:"Help",title_attribute:"Support, contact, and community hubs"});if(this.options.biostar_url){l.add({title:"Galaxy Q&A Site",content:this.options.biostar_url_redirect,target:"_blank"});l.add({title:"Ask a question",content:"biostar/biostar_question_redirect",target:"_blank"})}l.add({title:"Support",content:this.options.support_url,target:"_blank"});l.add({title:"Search",content:this.options.search_url,target:"_blank"});l.add({title:"Mailing Lists",content:this.options.mailing_lists,target:"_blank"});l.add({title:"Videos",content:this.options.screencasts_url,target:"_blank"});l.add({title:"Wiki",content:this.options.wiki_url,target:"_blank"});l.add({title:"How to Cite Galaxy",content:this.options.citation_url,target:"_blank"});if(this.options.terms_url){l.add({title:"Terms and Conditions",content:this.options.terms_url,target:"_blank"})}this.masthead.append(l);if(!this.options.user.valid){var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedout-only",title_attribute:"Account registration or login"});k.add({title:"Login",content:"user/login",target:"galaxy_main"});if(this.options.allow_user_creation){k.add({title:"Register",content:"user/create",target:"galaxy_main"})}this.masthead.append(k)}else{var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedin-only",title_attribute:"Account preferences and saved data"});k.add({title:"Logged in as "+this.options.user.email});if(this.options.use_remote_user&&this.options.remote_user_logout_href){k.add({title:"Logout",content:this.options.remote_user_logout_href,target:"_top"})}else{k.add({title:"Preferences",content:"user?cntrller=user",target:"galaxy_main"});k.add({title:"Custom Builds",content:"user/dbkeys",target:"galaxy_main"});k.add({title:"Logout",content:"user/logout",target:"_top",divider:true})}k.add({title:"Saved Histories",content:"history/list",target:"galaxy_main"});k.add({title:"Saved Datasets",content:"dataset/list",target:"galaxy_main"});k.add({title:"Saved Pages",content:"page/list",target:"_top"});k.add({title:"API Keys",content:"user/api_keys?cntrller=user",target:"galaxy_main"});if(this.options.use_remote_user){k.add({title:"Public Name",content:"user/edit_username?cntrller=user",target:"galaxy_main"})}this.masthead.append(k)}if(this.options.active_view){this.masthead.highlight(this.options.active_view)}}});return{GalaxyMenu:a}});
\ No newline at end of file
diff -r 3e87c1b1c3801aac29bb9bd7709ead6f996d7dc3 -r 6e91e5fcd11351ea04789403e44002f9ccb70060 templates/webapps/galaxy/galaxy.masthead.mako
--- a/templates/webapps/galaxy/galaxy.masthead.mako
+++ b/templates/webapps/galaxy/galaxy.masthead.mako
@@ -41,6 +41,7 @@
'enable_cloud_launch' : app.config.get_bool('enable_cloud_launch', False),
'lims_doc_url' : app.config.get("lims_doc_url", "http://main.g2.bx.psu.edu/u/rkchak/p/sts"),
'biostar_url' : app.config.biostar_url,
+ 'biostar_url_redirect' : h.url_for( controller='biostar', action='biostar_redirect', qualified=True ),
'support_url' : app.config.get("support_url", "http://wiki.galaxyproject.org/Support"),
'search_url' : app.config.get("search_url", "http://galaxyproject.org/search/usegalaxy/"),
'mailing_lists' : app.config.get("mailing_lists", "http://wiki.galaxyproject.org/MailingLists"),
https://bitbucket.org/galaxy/galaxy-central/commits/1d06e19aa3aa/
Changeset: 1d06e19aa3aa
Branch: stable
User: dannon
Date: 2014-04-22 16:01:58
Summary: Merged in dan/galaxy-central-stable-prs/stable (pull request #374)
Prevent redirect misuse on user log in.
Affected #: 2 files
diff -r 7fe7330660189bb382191a18ee145a7698ddbb74 -r 1d06e19aa3aac0c87b21f8482b8fc73c89e277ad lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -29,6 +29,8 @@
from hashlib import md5
from itertools import izip
+from urlparse import urlparse
+
from galaxy import eggs
eggs.require( 'docutils' )
@@ -691,6 +693,17 @@
def string_to_object( s ):
return pickle.loads( binascii.unhexlify( s ) )
+def compare_urls( url1, url2, compare_scheme=True, compare_hostname=True, compare_path=True ):
+ url1 = urlparse( url1 )
+ url2 = urlparse( url2 )
+ if compare_scheme and url1.scheme and url2.scheme and url1.scheme != url2.scheme:
+ return False
+ if compare_hostname and url1.hostname and url2.hostname and url1.hostname != url2.hostname:
+ return False
+ if compare_path and url1.path and url2.path and url1.path != url2.path:
+ return False
+ return True
+
def get_ucsc_by_build(build):
sites = []
for site in ucsc_build_sites:
diff -r 7fe7330660189bb382191a18ee145a7698ddbb74 -r 1d06e19aa3aac0c87b21f8482b8fc73c89e277ad lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -445,26 +445,35 @@
return self.user_openid_grid( trans, **kwd )
@web.expose
- def login( self, trans, redirect_url='', refresh_frames=[], **kwd ):
+ def login( self, trans, refresh_frames=[], **kwd ):
'''Handle Galaxy Log in'''
redirect = kwd.get( 'redirect', trans.request.referer ).strip()
+ root_url = url_for( '/', qualified=True )
+ redirect_url = '' #always start with redirect_url being empty
+ # compare urls, to prevent a redirect from pointing (directly) outside of galaxy
+ # or to enter a logout/login loop
+ if not util.compare_urls( root_url, redirect, compare_path=False ) or util.compare_urls( url_for( controller='user', action='logout', qualified=True ), redirect ):
+ redirect = root_url
use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
message = kwd.get( 'message', '' )
status = kwd.get( 'status', 'done' )
header = ''
- user = None
+ user = trans.user
email = kwd.get( 'email', '' )
- if kwd.get( 'login_button', False ):
+ if user:
+ #already logged in
+ redirect_url = redirect
+ message = 'You are already logged in.'
+ status = 'info'
+ elif kwd.get( 'login_button', False ):
if trans.webapp.name == 'galaxy' and not refresh_frames:
if trans.app.config.require_login:
refresh_frames = [ 'masthead', 'history', 'tools' ]
else:
refresh_frames = [ 'masthead', 'history' ]
message, status, user, success = self.__validate_login( trans, **kwd )
- if success and redirect and not redirect.startswith( trans.request.base + url_for( controller='user', action='logout' ) ):
+ if success:
redirect_url = redirect
- elif success:
- redirect_url = url_for( '/' )
if not user and trans.app.config.require_login:
if trans.app.config.allow_user_creation:
create_account_str = " If you don't already have an account, <a href='%s'>you may create one</a>." % \
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: dan: Fix for masthead link for biostar mainpage.
by commits-noreply@bitbucket.org 22 Apr '14
by commits-noreply@bitbucket.org 22 Apr '14
22 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/7fe733066018/
Changeset: 7fe733066018
Branch: stable
User: dan
Date: 2014-04-21 18:12:03
Summary: Fix for masthead link for biostar mainpage.
Affected #: 3 files
diff -r e6876f6918548a0805fa0cc5306ba1ced6fb184a -r 7fe7330660189bb382191a18ee145a7698ddbb74 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -196,7 +196,7 @@
{
tab_help.add({
title : "Galaxy Q&A Site",
- content : this.options.biostar_url,
+ content : this.options.biostar_url_redirect,
target : "_blank"
});
tab_help.add({
diff -r e6876f6918548a0805fa0cc5306ba1ced6fb184a -r 7fe7330660189bb382191a18ee145a7698ddbb74 static/scripts/packed/galaxy.menu.js
--- a/static/scripts/packed/galaxy.menu.js
+++ b/static/scripts/packed/galaxy.menu.js
@@ -1,1 +1,1 @@
-define(["galaxy.masthead"],function(b){var a=Backbone.Model.extend({options:null,masthead:null,initialize:function(c){this.options=c.config;this.masthead=c.masthead;this.create()},create:function(){var e=new b.GalaxyMastheadTab({id:"analysis",title:"Analyze Data",content:"root/index",title_attribute:"Analysis home view"});this.masthead.append(e);var g={id:"workflow",title:"Workflow",content:"workflow",title_attribute:"Chain tools into workflows"};if(!this.options.user.valid){g.disabled=true}var d=new b.GalaxyMastheadTab(g);this.masthead.append(d);var i=new b.GalaxyMastheadTab({id:"shared",title:"Shared Data",content:"library/index",title_attribute:"Access published resources"});i.add({title:"Data Libraries",content:"library/index"});i.add({title:"Data Libraries Beta",content:"library/list",divider:true});i.add({title:"Published Histories",content:"history/list_published"});i.add({title:"Published Workflows",content:"workflow/list_published"});i.add({title:"Published Visualizations",content:"visualization/list_published"});i.add({title:"Published Pages",content:"page/list_published"});this.masthead.append(i);if(this.options.user.requests){var j=new b.GalaxyMastheadTab({id:"lab",title:"Lab"});j.add({title:"Sequencing Requests",content:"requests/index"});j.add({title:"Find Samples",content:"requests/find_samples_index"});j.add({title:"Help",content:this.options.lims_doc_url});this.masthead.append(j)}var c={id:"visualization",title:"Visualization",content:"visualization/list",title_attribute:"Visualize datasets"};if(!this.options.user.valid){c.disabled=true}var m=new b.GalaxyMastheadTab(c);if(this.options.user.valid){m.add({title:"New Track Browser",content:"visualization/trackster",target:"_frame"});m.add({title:"Saved Visualizations",content:"visualization/list",target:"_frame"})}this.masthead.append(m);if(this.options.enable_cloud_launch){var f=new b.GalaxyMastheadTab({id:"cloud",title:"Cloud",content:"cloudlaunch/index"});f.add({title:"New Cloud Cluster",content:"cloudlaunch/index"});this.masthead.append(f)}if(this.options.is_admin_user){var h=new b.GalaxyMastheadTab({id:"admin",title:"Admin",content:"admin/index",extra_class:"admin-only",title_attribute:"Administer this Galaxy"});this.masthead.append(h)}var l=new b.GalaxyMastheadTab({id:"help",title:"Help",title_attribute:"Support, contact, and community hubs"});if(this.options.biostar_url){l.add({title:"Galaxy Q&A Site",content:this.options.biostar_url,target:"_blank"});l.add({title:"Ask a question",content:"biostar/biostar_question_redirect",target:"_blank"})}l.add({title:"Support",content:this.options.support_url,target:"_blank"});l.add({title:"Search",content:this.options.search_url,target:"_blank"});l.add({title:"Mailing Lists",content:this.options.mailing_lists,target:"_blank"});l.add({title:"Videos",content:this.options.screencasts_url,target:"_blank"});l.add({title:"Wiki",content:this.options.wiki_url,target:"_blank"});l.add({title:"How to Cite Galaxy",content:this.options.citation_url,target:"_blank"});if(this.options.terms_url){l.add({title:"Terms and Conditions",content:this.options.terms_url,target:"_blank"})}this.masthead.append(l);if(!this.options.user.valid){var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedout-only",title_attribute:"Account registration or login"});k.add({title:"Login",content:"user/login",target:"galaxy_main"});if(this.options.allow_user_creation){k.add({title:"Register",content:"user/create",target:"galaxy_main"})}this.masthead.append(k)}else{var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedin-only",title_attribute:"Account preferences and saved data"});k.add({title:"Logged in as "+this.options.user.email});if(this.options.use_remote_user&&this.options.remote_user_logout_href){k.add({title:"Logout",content:this.options.remote_user_logout_href,target:"_top"})}else{k.add({title:"Preferences",content:"user?cntrller=user",target:"galaxy_main"});k.add({title:"Custom Builds",content:"user/dbkeys",target:"galaxy_main"});k.add({title:"Logout",content:"user/logout",target:"_top",divider:true})}k.add({title:"Saved Histories",content:"history/list",target:"galaxy_main"});k.add({title:"Saved Datasets",content:"dataset/list",target:"galaxy_main"});k.add({title:"Saved Pages",content:"page/list",target:"_top"});k.add({title:"API Keys",content:"user/api_keys?cntrller=user",target:"galaxy_main"});if(this.options.use_remote_user){k.add({title:"Public Name",content:"user/edit_username?cntrller=user",target:"galaxy_main"})}this.masthead.append(k)}if(this.options.active_view){this.masthead.highlight(this.options.active_view)}}});return{GalaxyMenu:a}});
\ No newline at end of file
+define(["galaxy.masthead"],function(b){var a=Backbone.Model.extend({options:null,masthead:null,initialize:function(c){this.options=c.config;this.masthead=c.masthead;this.create()},create:function(){var e=new b.GalaxyMastheadTab({id:"analysis",title:"Analyze Data",content:"root/index",title_attribute:"Analysis home view"});this.masthead.append(e);var g={id:"workflow",title:"Workflow",content:"workflow",title_attribute:"Chain tools into workflows"};if(!this.options.user.valid){g.disabled=true}var d=new b.GalaxyMastheadTab(g);this.masthead.append(d);var i=new b.GalaxyMastheadTab({id:"shared",title:"Shared Data",content:"library/index",title_attribute:"Access published resources"});i.add({title:"Data Libraries",content:"library/index"});i.add({title:"Data Libraries Beta",content:"library/list",divider:true});i.add({title:"Published Histories",content:"history/list_published"});i.add({title:"Published Workflows",content:"workflow/list_published"});i.add({title:"Published Visualizations",content:"visualization/list_published"});i.add({title:"Published Pages",content:"page/list_published"});this.masthead.append(i);if(this.options.user.requests){var j=new b.GalaxyMastheadTab({id:"lab",title:"Lab"});j.add({title:"Sequencing Requests",content:"requests/index"});j.add({title:"Find Samples",content:"requests/find_samples_index"});j.add({title:"Help",content:this.options.lims_doc_url});this.masthead.append(j)}var c={id:"visualization",title:"Visualization",content:"visualization/list",title_attribute:"Visualize datasets"};if(!this.options.user.valid){c.disabled=true}var m=new b.GalaxyMastheadTab(c);if(this.options.user.valid){m.add({title:"New Track Browser",content:"visualization/trackster",target:"_frame"});m.add({title:"Saved Visualizations",content:"visualization/list",target:"_frame"})}this.masthead.append(m);if(this.options.enable_cloud_launch){var f=new b.GalaxyMastheadTab({id:"cloud",title:"Cloud",content:"cloudlaunch/index"});f.add({title:"New Cloud Cluster",content:"cloudlaunch/index"});this.masthead.append(f)}if(this.options.is_admin_user){var h=new b.GalaxyMastheadTab({id:"admin",title:"Admin",content:"admin/index",extra_class:"admin-only",title_attribute:"Administer this Galaxy"});this.masthead.append(h)}var l=new b.GalaxyMastheadTab({id:"help",title:"Help",title_attribute:"Support, contact, and community hubs"});if(this.options.biostar_url){l.add({title:"Galaxy Q&A Site",content:this.options.biostar_url_redirect,target:"_blank"});l.add({title:"Ask a question",content:"biostar/biostar_question_redirect",target:"_blank"})}l.add({title:"Support",content:this.options.support_url,target:"_blank"});l.add({title:"Search",content:this.options.search_url,target:"_blank"});l.add({title:"Mailing Lists",content:this.options.mailing_lists,target:"_blank"});l.add({title:"Videos",content:this.options.screencasts_url,target:"_blank"});l.add({title:"Wiki",content:this.options.wiki_url,target:"_blank"});l.add({title:"How to Cite Galaxy",content:this.options.citation_url,target:"_blank"});if(this.options.terms_url){l.add({title:"Terms and Conditions",content:this.options.terms_url,target:"_blank"})}this.masthead.append(l);if(!this.options.user.valid){var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedout-only",title_attribute:"Account registration or login"});k.add({title:"Login",content:"user/login",target:"galaxy_main"});if(this.options.allow_user_creation){k.add({title:"Register",content:"user/create",target:"galaxy_main"})}this.masthead.append(k)}else{var k=new b.GalaxyMastheadTab({id:"user",title:"User",extra_class:"loggedin-only",title_attribute:"Account preferences and saved data"});k.add({title:"Logged in as "+this.options.user.email});if(this.options.use_remote_user&&this.options.remote_user_logout_href){k.add({title:"Logout",content:this.options.remote_user_logout_href,target:"_top"})}else{k.add({title:"Preferences",content:"user?cntrller=user",target:"galaxy_main"});k.add({title:"Custom Builds",content:"user/dbkeys",target:"galaxy_main"});k.add({title:"Logout",content:"user/logout",target:"_top",divider:true})}k.add({title:"Saved Histories",content:"history/list",target:"galaxy_main"});k.add({title:"Saved Datasets",content:"dataset/list",target:"galaxy_main"});k.add({title:"Saved Pages",content:"page/list",target:"_top"});k.add({title:"API Keys",content:"user/api_keys?cntrller=user",target:"galaxy_main"});if(this.options.use_remote_user){k.add({title:"Public Name",content:"user/edit_username?cntrller=user",target:"galaxy_main"})}this.masthead.append(k)}if(this.options.active_view){this.masthead.highlight(this.options.active_view)}}});return{GalaxyMenu:a}});
\ No newline at end of file
diff -r e6876f6918548a0805fa0cc5306ba1ced6fb184a -r 7fe7330660189bb382191a18ee145a7698ddbb74 templates/webapps/galaxy/galaxy.masthead.mako
--- a/templates/webapps/galaxy/galaxy.masthead.mako
+++ b/templates/webapps/galaxy/galaxy.masthead.mako
@@ -41,6 +41,7 @@
'enable_cloud_launch' : app.config.get_bool('enable_cloud_launch', False),
'lims_doc_url' : app.config.get("lims_doc_url", "http://main.g2.bx.psu.edu/u/rkchak/p/sts"),
'biostar_url' : app.config.biostar_url,
+ 'biostar_url_redirect' : h.url_for( controller='biostar', action='biostar_redirect', qualified=True ),
'support_url' : app.config.get("support_url", "http://wiki.galaxyproject.org/Support"),
'search_url' : app.config.get("search_url", "http://galaxyproject.org/search/usegalaxy/"),
'mailing_lists' : app.config.get("mailing_lists", "http://wiki.galaxyproject.org/MailingLists"),
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: martenson: data libraries: improved chaincall for importing datasets into history, some cleanup
by commits-noreply@bitbucket.org 21 Apr '14
by commits-noreply@bitbucket.org 21 Apr '14
21 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c5cea7463833/
Changeset: c5cea7463833
User: martenson
Date: 2014-04-21 23:31:36
Summary: data libraries: improved chaincall for importing datasets into history, some cleanup
Affected #: 4 files
diff -r 557c0f2c6129026d8a6738292a4ae26e681e1391 -r c5cea74638335a586c2d498ccb13dd5d23085b31 static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -2,7 +2,6 @@
// === MAIN GALAXY LIBRARY MODULE ====
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
-// dependencies
define([
"galaxy.masthead",
"utils/utils",
@@ -26,7 +25,7 @@
) {
// ============================================================================
-//ROUTER
+// ROUTER
var LibraryRouter = Backbone.Router.extend({
routes: {
"" : "libraries",
@@ -48,11 +47,13 @@
// ============================================================================
// Main controller of Galaxy Library
var GalaxyLibrary = Backbone.View.extend({
+
libraryToolbarView: null,
libraryListView: null,
library_router: null,
folderToolbarView: null,
folderListView: null,
+
initialize : function(){
Galaxy.libraries = this;
@@ -79,11 +80,7 @@
this.library_router.on('route:download', function(folder_id, format) {
if ($('#center').find(':checked').length === 0) {
mod_toastr.info('You have to select some datasets to download')
- // this happens rarely when there is a server/data error and client gets an actual response instead of an attachment
- // we don't know what was selected so we can't download again, we redirect to the folder provided
- Galaxy.libraries.library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
} else {
- // send download stream
Galaxy.libraries.folderToolbarView.download(folder_id, format);
Galaxy.libraries.library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
}
diff -r 557c0f2c6129026d8a6738292a4ae26e681e1391 -r c5cea74638335a586c2d498ccb13dd5d23085b31 static/scripts/mvc/library/library-foldertoolbar-view.js
--- a/static/scripts/mvc/library/library-foldertoolbar-view.js
+++ b/static/scripts/mvc/library/library-foldertoolbar-view.js
@@ -20,7 +20,7 @@
defaults: {
'can_add_library_item' : false,
'contains_file' : false,
- 'adding_datasets' : {
+ 'chain_call_control' : {
'total_number' : 0,
'failed_number' : 0
}
@@ -153,7 +153,12 @@
success: function (){
callback(self);
},
- error: function(){
+ error: function(model, response){
+ if (typeof response.responseJSON !== "undefined"){
+ mod_toastr.error(response.responseJSON.err_msg);
+ } else {
+ mod_toastr.error('An error ocurred :(');
+ }
}
});
},
@@ -163,6 +168,10 @@
// disable the button to prevent multiple submission
this.modal.disableButton('Import');
+ // init the control counters
+ this.options.chain_call_control.total_number = 0;
+ this.options.chain_call_control.failed_number = 0;
+
var history_id = $("select[name=dataset_import_bulk] option:selected").val();
// we can save last used history to pre-select it next time
this.options.last_used_history_id = history_id;
@@ -191,6 +200,7 @@
historyItem.source = 'library';
datasets_to_import.push(historyItem);
}
+ this.options.chain_call_control.total_number = datasets_to_import.length;
// call the recursive function to call ajax one after each other (request FIFO queue)
this.chainCall(datasets_to_import, history_name);
},
@@ -199,18 +209,26 @@
var self = this;
var popped_item = history_item_set.pop();
if (typeof popped_item === "undefined") {
- mod_toastr.success('Datasets were imported to history: ' + history_name);
- this.modal.hide();
- return;
+ if (this.options.chain_call_control.failed_number === 0){
+ mod_toastr.success('Selected datasets imported into history');
+ } else if (this.options.chain_call_control.failed_number === this.options.chain_call_control.total_number){
+ mod_toastr.error('There was an error and no datasets were imported into history.');
+ } else if (this.options.chain_call_control.failed_number < this.options.chain_call_control.total_number){
+ mod_toastr.warning('Some of the datasets could not be imported into history');
+ }
+ Galaxy.modal.hide();
+ return;
}
var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source}));
promise.done(function(a1){
+ // we are fine
self.updateProgress();
self.chainCall(history_item_set, history_name);
})
.fail(function(a1){
- mod_toastr.error('An error occured :(');
+ // we have a problem
+ self.options.chain_call_control.failed_number += 1;
self.updateProgress();
self.chainCall(history_item_set, history_name);
});
@@ -307,9 +325,9 @@
addAllDatasetsFromHistory : function (){
// disable the button to prevent multiple submission
this.modal.disableButton('Add');
- // init the counters
- this.options.adding_datasets.total_number = 0;
- this.options.adding_datasets.failed_number = 0;
+ // init the control counters
+ this.options.chain_call_control.total_number = 0;
+ this.options.chain_call_control.failed_number = 0;
var history_dataset_ids = [];
this.modal.$el.find('#selected_history_content').find(':checked').each(function(){
@@ -335,7 +353,7 @@
folder_item.set({'from_hda_id':history_dataset_id});
hdas_to_add.push(folder_item);
}
- this.options.adding_datasets.total_number = hdas_to_add.length;
+ this.options.chain_call_control.total_number = hdas_to_add.length;
// call the recursive function to call ajax one after each other (request FIFO queue)
this.chainCallAddingHdas(hdas_to_add);
},
@@ -345,11 +363,11 @@
this.added_hdas = new mod_library_model.Folder();
var popped_item = hdas_set.pop();
if (typeof popped_item === "undefined") {
- if (this.options.adding_datasets.failed_number === 0){
+ if (this.options.chain_call_control.failed_number === 0){
mod_toastr.success('Selected datasets from history added to the folder');
- } else if (this.options.adding_datasets.failed_number === this.options.adding_datasets.total_number){
+ } else if (this.options.chain_call_control.failed_number === this.options.chain_call_control.total_number){
mod_toastr.error('There was an error and no datasets were added to the folder.');
- } else if (this.options.adding_datasets.failed_number < this.options.adding_datasets.total_number){
+ } else if (this.options.chain_call_control.failed_number < this.options.chain_call_control.total_number){
mod_toastr.warning('Some of the datasets could not be added to the folder');
}
Galaxy.modal.hide();
@@ -365,12 +383,7 @@
})
.fail(function(data){
// we have a problem
- self.options.adding_datasets.failed_number += 1;
- // if (typeof data.responseJSON !== "undefined"){
- // mod_toastr.error(data.responseJSON.err_msg);
- // } else {
- // mod_toastr.error('An error ocurred :(');
- // }
+ self.options.chain_call_control.failed_number += 1;
self.updateProgress();
self.chainCallAddingHdas(hdas_set);
});
diff -r 557c0f2c6129026d8a6738292a4ae26e681e1391 -r c5cea74638335a586c2d498ccb13dd5d23085b31 static/scripts/packed/galaxy.library.js
--- a/static/scripts/packed/galaxy.library.js
+++ b/static/scripts/packed/galaxy.library.js
@@ -1,1 +1,1 @@
-define(["galaxy.masthead","utils/utils","libs/toastr","mvc/base-mvc","mvc/library/library-model","mvc/library/library-folderlist-view","mvc/library/library-librarylist-view","mvc/library/library-librarytoolbar-view","mvc/library/library-foldertoolbar-view"],function(e,c,g,k,h,a,f,d,i){var l=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var j=k.SessionStorageModel.extend({defaults:{with_deleted:false,sort_order:"asc",sort_by:"name"}});var b=Backbone.View.extend({libraryToolbarView:null,libraryListView:null,library_router:null,folderToolbarView:null,folderListView:null,initialize:function(){Galaxy.libraries=this;this.preferences=new j({id:"global-lib-prefs"});this.library_router=new l();this.library_router.on("route:libraries",function(){Galaxy.libraries.libraryToolbarView=new d.LibraryToolbarView();Galaxy.libraries.libraryListView=new f.LibraryListView()});this.library_router.on("route:folder_content",function(m){if(Galaxy.libraries.folderToolbarView){Galaxy.libraries.folderToolbarView.$el.unbind("click")}Galaxy.libraries.folderToolbarView=new i.FolderToolbarView({id:m});Galaxy.libraries.folderListView=new a.FolderListView({id:m})});this.library_router.on("route:download",function(m,n){if($("#center").find(":checked").length===0){g.info("You have to select some datasets to download");Galaxy.libraries.library_router.navigate("folders/"+m,{trigger:true,replace:true})}else{Galaxy.libraries.folderToolbarView.download(m,n);Galaxy.libraries.library_router.navigate("folders/"+m,{trigger:false,replace:true})}});Backbone.history.start({pushState:false})}});return{GalaxyApp:b}});
\ No newline at end of file
+define(["galaxy.masthead","utils/utils","libs/toastr","mvc/base-mvc","mvc/library/library-model","mvc/library/library-folderlist-view","mvc/library/library-librarylist-view","mvc/library/library-librarytoolbar-view","mvc/library/library-foldertoolbar-view"],function(e,c,g,k,h,a,f,d,i){var l=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var j=k.SessionStorageModel.extend({defaults:{with_deleted:false,sort_order:"asc",sort_by:"name"}});var b=Backbone.View.extend({libraryToolbarView:null,libraryListView:null,library_router:null,folderToolbarView:null,folderListView:null,initialize:function(){Galaxy.libraries=this;this.preferences=new j({id:"global-lib-prefs"});this.library_router=new l();this.library_router.on("route:libraries",function(){Galaxy.libraries.libraryToolbarView=new d.LibraryToolbarView();Galaxy.libraries.libraryListView=new f.LibraryListView()});this.library_router.on("route:folder_content",function(m){if(Galaxy.libraries.folderToolbarView){Galaxy.libraries.folderToolbarView.$el.unbind("click")}Galaxy.libraries.folderToolbarView=new i.FolderToolbarView({id:m});Galaxy.libraries.folderListView=new a.FolderListView({id:m})});this.library_router.on("route:download",function(m,n){if($("#center").find(":checked").length===0){g.info("You have to select some datasets to download")}else{Galaxy.libraries.folderToolbarView.download(m,n);Galaxy.libraries.library_router.navigate("folders/"+m,{trigger:false,replace:true})}});Backbone.history.start({pushState:false})}});return{GalaxyApp:b}});
\ No newline at end of file
diff -r 557c0f2c6129026d8a6738292a4ae26e681e1391 -r c5cea74638335a586c2d498ccb13dd5d23085b31 static/scripts/packed/mvc/library/library-foldertoolbar-view.js
--- a/static/scripts/packed/mvc/library/library-foldertoolbar-view.js
+++ b/static/scripts/packed/mvc/library/library-foldertoolbar-view.js
@@ -1,1 +1,1 @@
-define(["galaxy.masthead","utils/utils","libs/toastr","mvc/library/library-model"],function(b,d,e,c){var a=Backbone.View.extend({el:"#center",events:{"click #toolbtn_create_folder":"createFolderFromModal","click #toolbtn_bulk_import":"modalBulkImport","click .toolbtn_add_files":"addFilesToFolderModal"},defaults:{can_add_library_item:false,contains_file:false,adding_datasets:{total_number:0,failed_number:0}},modal:null,histories:null,initialize:function(f){this.options=_.defaults(f||{},this.defaults);this.render()},render:function(f){this.options=_.extend(this.options,f);var h=this.templateToolBar();var g=false;if(Galaxy.currUser){g=Galaxy.currUser.isAdmin()}this.$el.html(h({id:this.options.id,admin_user:g}))},configureElements:function(f){this.options=_.extend(this.options,f);if(this.options.can_add_library_item===true){$("#toolbtn_create_folder").show();$(".toolbtn_add_files").show()}if(this.options.contains_file===true){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}this.$el.find("[data-toggle]").tooltip()},createFolderFromModal:function(){event.preventDefault();event.stopPropagation();var f=this;var g=this.templateNewFolderInModal();this.modal=Galaxy.modal;this.modal.show({closing_events:true,title:"Create New Folder",body:g(),buttons:{Create:function(){f.create_new_folder_event()},Close:function(){Galaxy.modal.hide()}}})},create_new_folder_event:function(){var f=this.serialize_new_folder();if(this.validate_new_folder(f)){var g=new c.FolderAsModel();url_items=Backbone.history.fragment.split("/");current_folder_id=url_items[url_items.length-1];g.url=g.urlRoot+"/"+current_folder_id;g.save(f,{success:function(h){Galaxy.modal.hide();e.success("Folder created");h.set({type:"folder"});Galaxy.libraries.folderListView.collection.add(h)},error:function(i,h){Galaxy.modal.hide();if(typeof h.responseJSON!=="undefined"){e.error(h.responseJSON.err_msg)}else{e.error("An error ocurred :(")}}})}else{e.error("Folder's name is missing")}return false},serialize_new_folder:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val()}},validate_new_folder:function(f){return f.name!==""},modalBulkImport:function(){var f=$("#folder_table").find(":checked");if(f.length===0){e.info("You have to select some datasets first")}else{this.refreshUserHistoriesList(function(g){var h=g.templateBulkImportInModal();g.modal=Galaxy.modal;g.modal.show({closing_events:true,title:"Import into History",body:h({histories:g.histories.models}),buttons:{Import:function(){g.importAllIntoHistory()},Close:function(){Galaxy.modal.hide()}}})})}},refreshUserHistoriesList:function(g){var f=this;this.histories=new c.GalaxyHistories();this.histories.fetch({success:function(){g(f)},error:function(){}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var j=$("select[name=dataset_import_bulk] option:selected").val();this.options.last_used_history_id=j;var m=$("select[name=dataset_import_bulk] option:selected").text();var o=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){o.push(this.parentElement.parentElement.id)}});var n=this.templateImportIntoHistoryProgressBar();this.modal.$el.find(".modal-body").html(n({history_name:m}));var k=100/o.length;this.initProgress(k);var f=[];for(var g=o.length-1;g>=0;g--){var h=o[g];var l=new c.HistoryItem();l.url=l.urlRoot+j+"/contents";l.content=h;l.source="library";f.push(l)}this.chainCall(f,m)},chainCall:function(g,j){var f=this;var h=g.pop();if(typeof h==="undefined"){e.success("Datasets were imported to history: "+j);this.modal.hide();return}var i=$.when(h.save({content:h.content,source:h.source}));i.done(function(k){f.updateProgress();f.chainCall(g,j)}).fail(function(k){e.error("An error occured :(");f.updateProgress();f.chainCall(g,j)})},initProgress:function(f){this.progress=0;this.progressStep=f},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},download:function(f,j){var h=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){h.push(this.parentElement.parentElement.id)}});var g="/api/libraries/datasets/download/"+j;var i={ldda_ids:h};this.processDownload(g,i,"get")},processDownload:function(g,h,i){if(g&&h){h=typeof h==="string"?h:$.param(h);var f="";$.each(h.split("&"),function(){var j=this.split("=");f+='<input type="hidden" name="'+j[0]+'" value="'+j[1]+'" />'});$('<form action="'+g+'" method="'+(i||"post")+'">'+f+"</form>").appendTo("body").submit().remove();e.info("Your download will begin soon")}},addFilesToFolderModal:function(){this.refreshUserHistoriesList(function(f){f.modal=Galaxy.modal;var g=f.templateAddFilesInModal();f.modal.show({closing_events:true,title:"Add datasets from history to "+f.options.folder_name,body:g({histories:f.histories.models}),buttons:{Add:function(){f.addAllDatasetsFromHistory()},Close:function(){Galaxy.modal.hide()}}});if(f.histories.models.length>0){f.fetchAndDisplayHistoryContents(f.histories.models[0].id);$("#dataset_add_bulk").change(function(h){f.fetchAndDisplayHistoryContents(h.target.value)})}else{e.error("An error ocurred :(")}})},fetchAndDisplayHistoryContents:function(h){var g=new c.HistoryContents({id:h});var f=this;g.fetch({success:function(j){var i=f.templateHistoryContents();f.histories.get(h).set({contents:j});f.modal.$el.find("#selected_history_content").html(i({history_contents:j.models.reverse()}))},error:function(j,i){e.error("An error ocurred :(")}})},addAllDatasetsFromHistory:function(){this.modal.disableButton("Add");this.options.adding_datasets.total_number=0;this.options.adding_datasets.failed_number=0;var f=[];this.modal.$el.find("#selected_history_content").find(":checked").each(function(){var i=$(this.parentElement).data("id");if(i){f.push(i)}});var l=this.options.folder_name;var k=this.templateAddingDatasetsProgressBar();this.modal.$el.find(".modal-body").html(k({folder_name:l}));this.progressStep=100/f.length;this.progress=0;var j=[];for(var h=f.length-1;h>=0;h--){history_dataset_id=f[h];var g=new c.Item();g.url="/api/folders/"+this.options.id+"/contents";g.set({from_hda_id:history_dataset_id});j.push(g)}this.options.adding_datasets.total_number=j.length;this.chainCallAddingHdas(j)},chainCallAddingHdas:function(g){var f=this;this.added_hdas=new c.Folder();var h=g.pop();if(typeof h==="undefined"){if(this.options.adding_datasets.failed_number===0){e.success("Selected datasets from history added to the folder")}else{if(this.options.adding_datasets.failed_number===this.options.adding_datasets.total_number){e.error("There was an error and no datasets were added to the folder.")}else{if(this.options.adding_datasets.failed_number<this.options.adding_datasets.total_number){e.warning("Some of the datasets could not be added to the folder")}}}Galaxy.modal.hide();return this.added_hdas}var i=$.when(h.save({from_hda_id:h.get("from_hda_id")}));i.done(function(j){Galaxy.libraries.folderListView.collection.add(j);f.updateProgress();f.chainCallAddingHdas(g)}).fail(function(j){f.options.adding_datasets.failed_number+=1;f.updateProgress();f.chainCallAddingHdas(g)})},templateToolBar:function(){tmpl_array=[];tmpl_array.push('<div class="library_style_container">');tmpl_array.push('<h3>Data Libraries Beta Test. This is work in progress. Please report problems & ideas via <a href="mailto:galaxy-bugs@bx.psu.edu?Subject=DataLibrariesBeta_Feedback" target="_blank">email</a> and <a href="https://trello.com/c/nwYQNFPK/56-data-library-ui-progressive-display-of-fol…" target="_blank">Trello</a>.</h3>');tmpl_array.push('<div id="library_folder_toolbar" >');tmpl_array.push('<div class="btn-group">');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Create New Folder" id="toolbtn_create_folder" class="btn btn-default primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-folder"></span></button>');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Add Datasets to Current Folder" id="toolbtn_add_files" class="btn btn-default toolbtn_add_files primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-file"></span></span></button>');tmpl_array.push("</div>");tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Import selected datasets into history" id="toolbtn_bulk_import" class="primary-button" style="margin-left: 0.5em; " type="button"><span class="fa fa-book"></span> to history</button>');tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; ">');tmpl_array.push(' <button title="Download selected datasets" id="drop_toggle" type="button" class="primary-button dropdown-toggle" data-toggle="dropdown">');tmpl_array.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');tmpl_array.push(" </button>");tmpl_array.push(' <ul class="dropdown-menu" role="menu">');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');tmpl_array.push(" </ul>");tmpl_array.push(" </div>");tmpl_array.push(" </div>");tmpl_array.push(' <div id="folder_items_element">');tmpl_array.push(" </div>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateNewFolderInModal:function(){tmpl_array=[];tmpl_array.push('<div id="new_folder_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push("</form>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateBulkImportInModal:function(){var f=[];f.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');f.push("Select history: ");f.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</span>");return _.template(f.join(""))},templateImportIntoHistoryProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddingDatasetsProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Adding selected datasets from history to library folder <b><%= _.escape(folder_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddFilesInModal:function(){var f=[];f.push('<div id="add_files_modal">');f.push('<div id="history_modal_combo_bulk">');f.push("Select history: ");f.push('<select id="dataset_add_bulk" name="dataset_add_bulk" style="width:66%; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</div>");f.push('<div id="selected_history_content">');f.push("</div>");f.push("</div>");return _.template(f.join(""))},templateHistoryContents:function(){var f=[];f.push("Choose the datasets to import:");f.push("<ul>");f.push(" <% _.each(history_contents, function(history_item) { %>");f.push(' <li data-id="<%= _.escape(history_item.get("id")) %>">');f.push(' <input style="margin: 0;" type="checkbox"><%= _.escape(history_item.get("hid")) %>: <%= _.escape(history_item.get("name")) %>');f.push(" </li>");f.push(" <% }); %>");f.push("</ul>");return _.template(f.join(""))}});return{FolderToolbarView:a}});
\ No newline at end of file
+define(["galaxy.masthead","utils/utils","libs/toastr","mvc/library/library-model"],function(b,d,e,c){var a=Backbone.View.extend({el:"#center",events:{"click #toolbtn_create_folder":"createFolderFromModal","click #toolbtn_bulk_import":"modalBulkImport","click .toolbtn_add_files":"addFilesToFolderModal"},defaults:{can_add_library_item:false,contains_file:false,chain_call_control:{total_number:0,failed_number:0}},modal:null,histories:null,initialize:function(f){this.options=_.defaults(f||{},this.defaults);this.render()},render:function(f){this.options=_.extend(this.options,f);var h=this.templateToolBar();var g=false;if(Galaxy.currUser){g=Galaxy.currUser.isAdmin()}this.$el.html(h({id:this.options.id,admin_user:g}))},configureElements:function(f){this.options=_.extend(this.options,f);if(this.options.can_add_library_item===true){$("#toolbtn_create_folder").show();$(".toolbtn_add_files").show()}if(this.options.contains_file===true){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}this.$el.find("[data-toggle]").tooltip()},createFolderFromModal:function(){event.preventDefault();event.stopPropagation();var f=this;var g=this.templateNewFolderInModal();this.modal=Galaxy.modal;this.modal.show({closing_events:true,title:"Create New Folder",body:g(),buttons:{Create:function(){f.create_new_folder_event()},Close:function(){Galaxy.modal.hide()}}})},create_new_folder_event:function(){var f=this.serialize_new_folder();if(this.validate_new_folder(f)){var g=new c.FolderAsModel();url_items=Backbone.history.fragment.split("/");current_folder_id=url_items[url_items.length-1];g.url=g.urlRoot+"/"+current_folder_id;g.save(f,{success:function(h){Galaxy.modal.hide();e.success("Folder created");h.set({type:"folder"});Galaxy.libraries.folderListView.collection.add(h)},error:function(i,h){Galaxy.modal.hide();if(typeof h.responseJSON!=="undefined"){e.error(h.responseJSON.err_msg)}else{e.error("An error ocurred :(")}}})}else{e.error("Folder's name is missing")}return false},serialize_new_folder:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val()}},validate_new_folder:function(f){return f.name!==""},modalBulkImport:function(){var f=$("#folder_table").find(":checked");if(f.length===0){e.info("You have to select some datasets first")}else{this.refreshUserHistoriesList(function(g){var h=g.templateBulkImportInModal();g.modal=Galaxy.modal;g.modal.show({closing_events:true,title:"Import into History",body:h({histories:g.histories.models}),buttons:{Import:function(){g.importAllIntoHistory()},Close:function(){Galaxy.modal.hide()}}})})}},refreshUserHistoriesList:function(g){var f=this;this.histories=new c.GalaxyHistories();this.histories.fetch({success:function(){g(f)},error:function(i,h){if(typeof h.responseJSON!=="undefined"){e.error(h.responseJSON.err_msg)}else{e.error("An error ocurred :(")}}})},importAllIntoHistory:function(){this.modal.disableButton("Import");this.options.chain_call_control.total_number=0;this.options.chain_call_control.failed_number=0;var j=$("select[name=dataset_import_bulk] option:selected").val();this.options.last_used_history_id=j;var m=$("select[name=dataset_import_bulk] option:selected").text();var o=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){o.push(this.parentElement.parentElement.id)}});var n=this.templateImportIntoHistoryProgressBar();this.modal.$el.find(".modal-body").html(n({history_name:m}));var k=100/o.length;this.initProgress(k);var f=[];for(var g=o.length-1;g>=0;g--){var h=o[g];var l=new c.HistoryItem();l.url=l.urlRoot+j+"/contents";l.content=h;l.source="library";f.push(l)}this.options.chain_call_control.total_number=f.length;this.chainCall(f,m)},chainCall:function(g,j){var f=this;var h=g.pop();if(typeof h==="undefined"){if(this.options.chain_call_control.failed_number===0){e.success("Selected datasets imported into history")}else{if(this.options.chain_call_control.failed_number===this.options.chain_call_control.total_number){e.error("There was an error and no datasets were imported into history.")}else{if(this.options.chain_call_control.failed_number<this.options.chain_call_control.total_number){e.warning("Some of the datasets could not be imported into history")}}}Galaxy.modal.hide();return}var i=$.when(h.save({content:h.content,source:h.source}));i.done(function(k){f.updateProgress();f.chainCall(g,j)}).fail(function(k){f.options.chain_call_control.failed_number+=1;f.updateProgress();f.chainCall(g,j)})},initProgress:function(f){this.progress=0;this.progressStep=f},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},download:function(f,j){var h=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){h.push(this.parentElement.parentElement.id)}});var g="/api/libraries/datasets/download/"+j;var i={ldda_ids:h};this.processDownload(g,i,"get")},processDownload:function(g,h,i){if(g&&h){h=typeof h==="string"?h:$.param(h);var f="";$.each(h.split("&"),function(){var j=this.split("=");f+='<input type="hidden" name="'+j[0]+'" value="'+j[1]+'" />'});$('<form action="'+g+'" method="'+(i||"post")+'">'+f+"</form>").appendTo("body").submit().remove();e.info("Your download will begin soon")}},addFilesToFolderModal:function(){this.refreshUserHistoriesList(function(f){f.modal=Galaxy.modal;var g=f.templateAddFilesInModal();f.modal.show({closing_events:true,title:"Add datasets from history to "+f.options.folder_name,body:g({histories:f.histories.models}),buttons:{Add:function(){f.addAllDatasetsFromHistory()},Close:function(){Galaxy.modal.hide()}}});if(f.histories.models.length>0){f.fetchAndDisplayHistoryContents(f.histories.models[0].id);$("#dataset_add_bulk").change(function(h){f.fetchAndDisplayHistoryContents(h.target.value)})}else{e.error("An error ocurred :(")}})},fetchAndDisplayHistoryContents:function(h){var g=new c.HistoryContents({id:h});var f=this;g.fetch({success:function(j){var i=f.templateHistoryContents();f.histories.get(h).set({contents:j});f.modal.$el.find("#selected_history_content").html(i({history_contents:j.models.reverse()}))},error:function(j,i){e.error("An error ocurred :(")}})},addAllDatasetsFromHistory:function(){this.modal.disableButton("Add");this.options.chain_call_control.total_number=0;this.options.chain_call_control.failed_number=0;var f=[];this.modal.$el.find("#selected_history_content").find(":checked").each(function(){var i=$(this.parentElement).data("id");if(i){f.push(i)}});var l=this.options.folder_name;var k=this.templateAddingDatasetsProgressBar();this.modal.$el.find(".modal-body").html(k({folder_name:l}));this.progressStep=100/f.length;this.progress=0;var j=[];for(var h=f.length-1;h>=0;h--){history_dataset_id=f[h];var g=new c.Item();g.url="/api/folders/"+this.options.id+"/contents";g.set({from_hda_id:history_dataset_id});j.push(g)}this.options.chain_call_control.total_number=j.length;this.chainCallAddingHdas(j)},chainCallAddingHdas:function(g){var f=this;this.added_hdas=new c.Folder();var h=g.pop();if(typeof h==="undefined"){if(this.options.chain_call_control.failed_number===0){e.success("Selected datasets from history added to the folder")}else{if(this.options.chain_call_control.failed_number===this.options.chain_call_control.total_number){e.error("There was an error and no datasets were added to the folder.")}else{if(this.options.chain_call_control.failed_number<this.options.chain_call_control.total_number){e.warning("Some of the datasets could not be added to the folder")}}}Galaxy.modal.hide();return this.added_hdas}var i=$.when(h.save({from_hda_id:h.get("from_hda_id")}));i.done(function(j){Galaxy.libraries.folderListView.collection.add(j);f.updateProgress();f.chainCallAddingHdas(g)}).fail(function(j){f.options.chain_call_control.failed_number+=1;f.updateProgress();f.chainCallAddingHdas(g)})},templateToolBar:function(){tmpl_array=[];tmpl_array.push('<div class="library_style_container">');tmpl_array.push('<h3>Data Libraries Beta Test. This is work in progress. Please report problems & ideas via <a href="mailto:galaxy-bugs@bx.psu.edu?Subject=DataLibrariesBeta_Feedback" target="_blank">email</a> and <a href="https://trello.com/c/nwYQNFPK/56-data-library-ui-progressive-display-of-fol…" target="_blank">Trello</a>.</h3>');tmpl_array.push('<div id="library_folder_toolbar" >');tmpl_array.push('<div class="btn-group">');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Create New Folder" id="toolbtn_create_folder" class="btn btn-default primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-folder"></span></button>');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Add Datasets to Current Folder" id="toolbtn_add_files" class="btn btn-default toolbtn_add_files primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-file"></span></span></button>');tmpl_array.push("</div>");tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Import selected datasets into history" id="toolbtn_bulk_import" class="primary-button" style="margin-left: 0.5em; " type="button"><span class="fa fa-book"></span> to history</button>');tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; ">');tmpl_array.push(' <button title="Download selected datasets" id="drop_toggle" type="button" class="primary-button dropdown-toggle" data-toggle="dropdown">');tmpl_array.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');tmpl_array.push(" </button>");tmpl_array.push(' <ul class="dropdown-menu" role="menu">');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');tmpl_array.push(" </ul>");tmpl_array.push(" </div>");tmpl_array.push(" </div>");tmpl_array.push(' <div id="folder_items_element">');tmpl_array.push(" </div>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateNewFolderInModal:function(){tmpl_array=[];tmpl_array.push('<div id="new_folder_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push("</form>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateBulkImportInModal:function(){var f=[];f.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');f.push("Select history: ");f.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</span>");return _.template(f.join(""))},templateImportIntoHistoryProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddingDatasetsProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Adding selected datasets from history to library folder <b><%= _.escape(folder_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddFilesInModal:function(){var f=[];f.push('<div id="add_files_modal">');f.push('<div id="history_modal_combo_bulk">');f.push("Select history: ");f.push('<select id="dataset_add_bulk" name="dataset_add_bulk" style="width:66%; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</div>");f.push('<div id="selected_history_content">');f.push("</div>");f.push("</div>");return _.template(f.join(""))},templateHistoryContents:function(){var f=[];f.push("Choose the datasets to import:");f.push("<ul>");f.push(" <% _.each(history_contents, function(history_item) { %>");f.push(' <li data-id="<%= _.escape(history_item.get("id")) %>">');f.push(' <input style="margin: 0;" type="checkbox"><%= _.escape(history_item.get("hid")) %>: <%= _.escape(history_item.get("name")) %>');f.push(" </li>");f.push(" <% }); %>");f.push("</ul>");return _.template(f.join(""))}});return{FolderToolbarView:a}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
5 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e51282a5b1bf/
Changeset: e51282a5b1bf
User: dannon
Date: 2014-02-22 15:25:20
Summary: Cloudlaunch cleanup/pep8
Affected #: 1 file
diff -r fac578cca75887d811e67993a655fed20c0d8713 -r e51282a5b1bff80518db274800c2be7de2968785 lib/galaxy/webapps/galaxy/controllers/cloudlaunch.py
--- a/lib/galaxy/webapps/galaxy/controllers/cloudlaunch.py
+++ b/lib/galaxy/webapps/galaxy/controllers/cloudlaunch.py
@@ -11,13 +11,14 @@
import os
import tempfile
import time
-import pkg_resources
+
from galaxy import eggs
-pkg_resources.require('boto')
-import boto
from galaxy import web
from galaxy.web.base.controller import BaseUIController
from galaxy.util.json import to_json_string
+
+eggs.require('boto')
+import boto
from boto.ec2.regioninfo import RegionInfo
from boto.exception import EC2ResponseError, S3ResponseError
from boto.s3.connection import OrdinaryCallingFormat, S3Connection
@@ -29,20 +30,24 @@
class CloudController(BaseUIController):
+ """Galaxy Cloud Functions"""
def __init__(self, app):
BaseUIController.__init__(self, app)
@web.expose
- def index(self, trans, share_string=None, ami=None, bucket_default = None):
+ def index(self, trans, share_string=None, ami=None, bucket_default=None):
+ """
+ Serves the default page requesting AWS keys
+ """
return trans.fill_template("cloud/index.mako",
- default_keypair = DEFAULT_KEYPAIR,
- share_string=share_string,
- ami=ami,
- bucket_default=bucket_default)
+ default_keypair=DEFAULT_KEYPAIR,
+ share_string=share_string,
+ ami=ami,
+ bucket_default=bucket_default)
@web.expose
- def get_account_info(self, trans, key_id, secret, **kwargs):
+ def get_account_info(self, trans, key_id, secret):
"""
Get EC2 Account Info
"""
@@ -54,7 +59,7 @@
except EC2ResponseError, e:
log.error("Problem starting an instance: %s\n%s" % (e, e.body))
trans.response.status = 400
- return e.error_message or "Instance failure, but no specific error was detected. Please check your AWS Console."
+ return e.error_message or "Error retrieving account information. Please verify your credentials are correct, and contact galaxy support to report this issue."
account_info['keypairs'] = [akp.name for akp in kps]
#Existing Clusters
s3_conn = S3Connection(key_id, secret, calling_format=OrdinaryCallingFormat())
@@ -79,7 +84,7 @@
for line in pd_contents.split('\n'):
if 'vol_id' in line:
vol_id = line.split(':')[1].strip()
- v = ec2_conn.get_all_volumes(volume_ids = [vol_id])
+ v = ec2_conn.get_all_volumes(volume_ids=[vol_id])
if v:
zone = v[0].zone
else:
@@ -91,7 +96,7 @@
if key.name.endswith('.clusterName'):
clusters.append({'name': key.name.split('.clusterName')[0],
'persistent_data': pd_contents,
- 'zone':zone})
+ 'zone': zone})
account_info['clusters'] = clusters
account_info['zones'] = [z.name for z in ec2_conn.get_all_zones()]
return to_json_string(account_info)
@@ -111,29 +116,25 @@
return ec2_error
else:
user_provided_data = {'cluster_name': cluster_name,
- 'access_key': key_id,
- 'secret_key': secret,
- 'instance_type': instance_type}
+ 'access_key': key_id,
+ 'secret_key': secret,
+ 'instance_type': instance_type}
if password:
user_provided_data['password'] = password
if share_string:
user_provided_data['share_string'] = share_string
if bucket_default:
- user_provided_data['bucket_default'] = bucket_default
-
+ user_provided_data['bucket_default'] = bucket_default
if not ami:
ami = trans.app.config.cloudlaunch_default_ami
-
rs = run_instance(ec2_conn=ec2_conn,
- image_id = ami,
- user_provided_data=user_provided_data,
- key_name=kp_name,
- security_groups=[sg_name],
- placement=zone
- )
+ image_id=ami,
+ user_provided_data=user_provided_data,
+ key_name=kp_name,
+ security_groups=[sg_name],
+ placement=zone)
if rs:
instance = rs.instances[0]
- ct = 0
while not instance.public_dns_name:
try:
instance.update()
@@ -141,7 +142,6 @@
#This can happen when update is invoked before the instance is fully registered.
#Prevent failure, wait it out.
pass
- ct += 1
time.sleep(1)
if kp_material:
#We have created a keypair. Save to tempfile for one time retrieval.
@@ -152,14 +152,12 @@
kp_material_tag = fname[fname.rfind(PKEY_PREFIX) + len(PKEY_PREFIX):]
else:
kp_material_tag = None
- return to_json_string({
- 'cluster_name': cluster_name,
- 'instance_id': rs.instances[0].id,
- 'image_id': rs.instances[0].image_id,
- 'public_dns_name': rs.instances[0].public_dns_name,
- 'kp_name': kp_name,
- 'kp_material_tag':kp_material_tag
- })
+ return to_json_string({'cluster_name': cluster_name,
+ 'instance_id': rs.instances[0].id,
+ 'image_id': rs.instances[0].image_id,
+ 'public_dns_name': rs.instances[0].public_dns_name,
+ 'kp_name': kp_name,
+ 'kp_material_tag': kp_material_tag})
else:
trans.response.status = 400
return "Instance failure, but no specific error was detected. Please check your AWS Console."
@@ -173,16 +171,17 @@
kp_material = f.read()
f.close()
trans.response.headers['Content-Length'] = int( os.stat( expected_path ).st_size )
- trans.response.set_content_type( "application/octet-stream" ) #force octet-stream so Safari doesn't append mime extensions to filename
+ trans.response.set_content_type( "application/octet-stream" ) # force octet-stream so Safari doesn't append mime extensions to filename
trans.response.headers["Content-Disposition"] = 'attachment; filename="%s.pem"' % DEFAULT_KEYPAIR
os.remove(expected_path)
return kp_material
trans.response.status = 400
return "Invalid identifier"
-# ## Cloud interaction methods
+
def connect_ec2(a_key, s_key):
- """ Create and return an EC2 connection object.
+ """
+ Create and return an EC2 connection object.
"""
# Use variables for forward looking flexibility
# AWS connection values
@@ -193,14 +192,15 @@
ec2_conn_path = '/'
r = RegionInfo(name=region_name, endpoint=region_endpoint)
ec2_conn = boto.connect_ec2(aws_access_key_id=a_key,
- aws_secret_access_key=s_key,
- api_version='2011-11-01', # needed for availability zone support
- is_secure=is_secure,
- region=r,
- port=ec2_port,
- path=ec2_conn_path)
+ aws_secret_access_key=s_key,
+ api_version='2011-11-01', # needed for availability zone support
+ is_secure=is_secure,
+ region=r,
+ port=ec2_port,
+ path=ec2_conn_path)
return ec2_conn
+
def create_cm_security_group(ec2_conn, sg_name='CloudMan'):
""" Create a security group with all authorizations required to run CloudMan.
If the group already exists, check its rules and add the missing ones.
@@ -220,11 +220,11 @@
cmsg = ec2_conn.create_security_group(sg_name, 'A security group for CloudMan')
# Add appropriate authorization rules
# If these rules already exist, nothing will be changed in the SG
- ports = (('80', '80'), # Web UI
- ('20', '21'), # FTP
- ('22', '22'), # ssh
- ('30000', '30100'), # FTP transfer
- ('42284', '42284')) # CloudMan UI
+ ports = (('80', '80'), # Web UI
+ ('20', '21'), # FTP
+ ('22', '22'), # ssh
+ ('30000', '30100'), # FTP transfer
+ ('42284', '42284')) # CloudMan UI
for port in ports:
try:
if not rule_exists(cmsg.rules, from_port=port[0], to_port=port[1]):
@@ -234,7 +234,7 @@
except EC2ResponseError, e:
log.error("A problem with security group authorizations: %s" % e)
# Add rule that allows communication between instances in the same SG
- g_rule_exists = False # Flag to indicate if group rule already exists
+ g_rule_exists = False # Flag to indicate if group rule already exists
for rule in cmsg.rules:
for grant in rule.grants:
if grant.name == cmsg.name:
@@ -250,6 +250,7 @@
log.info("Done configuring '%s' security group" % cmsg.name)
return cmsg.name
+
def rule_exists(rules, from_port, to_port, ip_protocol='tcp', cidr_ip='0.0.0.0/0'):
""" A convenience method to check if an authorization rule in a security
group exists.
@@ -260,6 +261,7 @@
return True
return False
+
def create_key_pair(ec2_conn, key_name=DEFAULT_KEYPAIR):
""" Create a key pair with the provided name.
Return the name of the key or None if there was an error creating the key.
@@ -279,6 +281,7 @@
return None, None
return kp.name, kp.material
+
def run_instance(ec2_conn, user_provided_data, image_id=None,
kernel_id=None, ramdisk_id=None, key_name=DEFAULT_KEYPAIR,
placement=None, security_groups=['CloudMan']):
@@ -310,6 +313,7 @@
log.warning("Problem starting an instance?")
return rs
+
def _find_placement(ec2_conn, instance_type):
"""Find a region zone that supports our requested instance type.
@@ -328,4 +332,3 @@
return cur_loc
log.error("Did not find availabilty zone in {0} for {1}".format(base, instance_type))
return None
-
https://bitbucket.org/galaxy/galaxy-central/commits/e45c2616e207/
Changeset: e45c2616e207
User: dannon
Date: 2014-04-03 21:23:52
Summary: Incremental work / shifting changes to bioblend but committed here for posterity.
Affected #: 1 file
diff -r e51282a5b1bff80518db274800c2be7de2968785 -r e45c2616e207e25e4978d14343bd5bff495a16b0 lib/galaxy/webapps/galaxy/controllers/cloudlaunch.py
--- a/lib/galaxy/webapps/galaxy/controllers/cloudlaunch.py
+++ b/lib/galaxy/webapps/galaxy/controllers/cloudlaunch.py
@@ -3,10 +3,8 @@
Adapted from Brad Chapman and Enis Afgan's BioCloudCentral
BioCloudCentral Source: https://github.com/chapmanb/biocloudcentral
-
"""
-import datetime
import logging
import os
import tempfile
@@ -19,14 +17,19 @@
eggs.require('boto')
import boto
+import boto.vpc
from boto.ec2.regioninfo import RegionInfo
from boto.exception import EC2ResponseError, S3ResponseError
from boto.s3.connection import OrdinaryCallingFormat, S3Connection
log = logging.getLogger(__name__)
+logging.getLogger('boto').setLevel(logging.INFO)
+
+
PKEY_PREFIX = 'gxy_pkey'
DEFAULT_KEYPAIR = 'cloudman_keypair'
+CLOUDMAN_TAG_KEY = 'galaxy:cloudman'
class CloudController(BaseUIController):
@@ -51,7 +54,6 @@
"""
Get EC2 Account Info
"""
- #Keypairs
account_info = {}
try:
ec2_conn = connect_ec2(key_id, secret)
@@ -79,19 +81,7 @@
# This will also allow for much more sophisticated rendering of existing clusters
# Currently, this zone detection is a hack.
pd_contents = pd.get_contents_as_string()
- zone = ''
- try:
- for line in pd_contents.split('\n'):
- if 'vol_id' in line:
- vol_id = line.split(':')[1].strip()
- v = ec2_conn.get_all_volumes(volume_ids=[vol_id])
- if v:
- zone = v[0].zone
- else:
- zone = ''
- except:
- #If anything goes wrong with zone detection, use the default selection.
- zone = ''
+ zone = get_zone_from_pd(pd_contents, ec2_conn)
for key in bucket.list():
if key.name.endswith('.clusterName'):
clusters.append({'name': key.name.split('.clusterName')[0],
@@ -107,60 +97,51 @@
try:
# Create security group & key pair used when starting an instance
ec2_conn = connect_ec2(key_id, secret)
- sg_name = create_cm_security_group(ec2_conn)
+ sg = get_or_create_cm_security_group(ec2_conn)
kp_name, kp_material = create_key_pair(ec2_conn, key_name=keypair)
except EC2ResponseError, err:
ec2_error = err.error_message
if ec2_error:
trans.response.status = 400
return ec2_error
+ #Get an elastic ip, add to UserData.
+ user_provided_data = _format_user_data(cluster_name, key_id, secret, password, share_string, bucket_default)
+ if not ami:
+ ami = trans.app.config.cloudlaunch_default_ami
+ rs = _run_instance(ec2_conn=ec2_conn,
+ image_id=ami,
+ user_provided_data=user_provided_data,
+ key_name=kp_name,
+ security_group_ids=[sg.id],
+ placement=zone,
+ instance_type=instance_type,
+ subnet_id=get_or_create_cm_subnet(key_id, secret, zone).id)
+ if not rs:
+ trans.response.status = 400
+ return "Instance failure, but no specific error was detected. Please check your AWS Console."
+ instance = rs.instances[0]
+ while not instance.public_dns_name:
+ try:
+ instance.update()
+ except EC2ResponseError:
+ # This can happen when update is invoked before the instance is fully registered.
+ pass
+ time.sleep(1)
+ if kp_material:
+ #We have created a keypair. Save to tempfile for one time retrieval.
+ (fd, fname) = tempfile.mkstemp(prefix=PKEY_PREFIX, dir=trans.app.config.new_file_path)
+ f = os.fdopen(fd, 'wt')
+ f.write(kp_material)
+ f.close()
+ kp_material_tag = fname[fname.rfind(PKEY_PREFIX) + len(PKEY_PREFIX):]
else:
- user_provided_data = {'cluster_name': cluster_name,
- 'access_key': key_id,
- 'secret_key': secret,
- 'instance_type': instance_type}
- if password:
- user_provided_data['password'] = password
- if share_string:
- user_provided_data['share_string'] = share_string
- if bucket_default:
- user_provided_data['bucket_default'] = bucket_default
- if not ami:
- ami = trans.app.config.cloudlaunch_default_ami
- rs = run_instance(ec2_conn=ec2_conn,
- image_id=ami,
- user_provided_data=user_provided_data,
- key_name=kp_name,
- security_groups=[sg_name],
- placement=zone)
- if rs:
- instance = rs.instances[0]
- while not instance.public_dns_name:
- try:
- instance.update()
- except EC2ResponseError:
- #This can happen when update is invoked before the instance is fully registered.
- #Prevent failure, wait it out.
- pass
- time.sleep(1)
- if kp_material:
- #We have created a keypair. Save to tempfile for one time retrieval.
- (fd, fname) = tempfile.mkstemp(prefix=PKEY_PREFIX, dir=trans.app.config.new_file_path)
- f = os.fdopen(fd, 'wt')
- f.write(kp_material)
- f.close()
- kp_material_tag = fname[fname.rfind(PKEY_PREFIX) + len(PKEY_PREFIX):]
- else:
- kp_material_tag = None
- return to_json_string({'cluster_name': cluster_name,
- 'instance_id': rs.instances[0].id,
- 'image_id': rs.instances[0].image_id,
- 'public_dns_name': rs.instances[0].public_dns_name,
- 'kp_name': kp_name,
- 'kp_material_tag': kp_material_tag})
- else:
- trans.response.status = 400
- return "Instance failure, but no specific error was detected. Please check your AWS Console."
+ kp_material_tag = None
+ return to_json_string({'cluster_name': cluster_name,
+ 'instance_id': rs.instances[0].id,
+ 'image_id': rs.instances[0].image_id,
+ 'public_dns_name': rs.instances[0].public_dns_name,
+ 'kp_name': kp_name,
+ 'kp_material_tag': kp_material_tag})
@web.expose
def get_pkey(self, trans, kp_material_tag=None):
@@ -179,6 +160,21 @@
return "Invalid identifier"
+def get_zone_from_pd(pd_contents, ec2_conn):
+ try:
+ for line in pd_contents.split('\n'):
+ if 'vol_id' in line:
+ vol_id = line.split(':')[1].strip()
+ v = ec2_conn.get_all_volumes(volume_ids=[vol_id])
+ if v:
+ return v[0].zone
+ except:
+ # Things can go wrong with Zone detection outside of our control. Use
+ # the default zone if this happens.
+ pass
+ return ''
+
+
def connect_ec2(a_key, s_key):
"""
Create and return an EC2 connection object.
@@ -193,7 +189,6 @@
r = RegionInfo(name=region_name, endpoint=region_endpoint)
ec2_conn = boto.connect_ec2(aws_access_key_id=a_key,
aws_secret_access_key=s_key,
- api_version='2011-11-01', # needed for availability zone support
is_secure=is_secure,
region=r,
port=ec2_port,
@@ -201,23 +196,22 @@
return ec2_conn
-def create_cm_security_group(ec2_conn, sg_name='CloudMan'):
- """ Create a security group with all authorizations required to run CloudMan.
- If the group already exists, check its rules and add the missing ones.
- Return the name of the created security group.
+def get_or_create_cm_security_group(ec2_conn, sg_name='CloudManVPC'):
"""
- cmsg = None
- # Check if this security group already exists
- sgs = ec2_conn.get_all_security_groups()
- for sg in sgs:
- if sg.name == sg_name:
- cmsg = sg
- log.debug("Security group '%s' already exists; will add authorizations next." % sg_name)
- break
- # If it does not exist, create security group
- if cmsg is None:
+ Create a security group with all authorizations required to run CloudMan.
+ If the group already exists, check its rules and add the missing ones.
+ Return the name of the created security group.
+ """
+ cm_sg = None
+ # Check if this security group already exists. We can't use the built-in
+ # filtering here, because VPC security groups can't be filtered by name.
+ sg = ec2_conn.get_all_security_groups(filters={'group_name': sg_name})
+ if sg:
+ cm_sg = sg[0]
+ else:
+ # If it does not exist, create security group
log.debug("Creating Security Group %s" % sg_name)
- cmsg = ec2_conn.create_security_group(sg_name, 'A security group for CloudMan')
+ cm_sg = ec2_conn.create_security_group(sg_name, 'A security group for CloudMan', vpc_id=get_or_create_cm_vpc(ec2_conn.access_key, ec2_conn.secret_key).id)
# Add appropriate authorization rules
# If these rules already exist, nothing will be changed in the SG
ports = (('80', '80'), # Web UI
@@ -226,29 +220,68 @@
('30000', '30100'), # FTP transfer
('42284', '42284')) # CloudMan UI
for port in ports:
- try:
- if not rule_exists(cmsg.rules, from_port=port[0], to_port=port[1]):
- cmsg.authorize(ip_protocol='tcp', from_port=port[0], to_port=port[1], cidr_ip='0.0.0.0/0')
- else:
- log.debug("Rule (%s:%s) already exists in the SG" % (port[0], port[1]))
- except EC2ResponseError, e:
- log.error("A problem with security group authorizations: %s" % e)
+ if not rule_exists(cm_sg.rules, from_port=port[0], to_port=port[1]):
+ cm_sg.authorize(ip_protocol='tcp', from_port=port[0], to_port=port[1], cidr_ip='0.0.0.0/0')
+ else:
+ log.debug("Rule (%s:%s) already exists in the SG" % (port[0], port[1]))
# Add rule that allows communication between instances in the same SG
g_rule_exists = False # Flag to indicate if group rule already exists
- for rule in cmsg.rules:
- for grant in rule.grants:
- if grant.name == cmsg.name:
+ for rule in cm_sg.rules:
+ for grant in rule.grants:#[g for g in rule.grants if g.name == cm_sg.name]:
+ print grant.name
+ if grant.name == cm_sg.name:
g_rule_exists = True
log.debug("Group rule already exists in the SG")
if g_rule_exists:
break
- if g_rule_exists is False:
- try:
- cmsg.authorize(src_group=cmsg)
- except EC2ResponseError, e:
- log.error("A problem w/ security group authorization: %s" % e)
- log.info("Done configuring '%s' security group" % cmsg.name)
- return cmsg.name
+ #if g_rule_exists is False:
+ # cm_sg.authorize(src_group=cm_sg)
+ log.info("Done configuring '%s' security group" % cm_sg.name)
+ return cm_sg
+
+
+
+def get_or_create_cm_vpc(a_key, s_key):
+ vpc_conn = boto.vpc.VPCConnection(a_key, s_key)
+ vpcs = vpc_conn.get_all_vpcs()
+ cloudman_vpc = None
+ for vpc in vpcs:
+ if CLOUDMAN_TAG_KEY in vpc.tags:
+ cloudman_vpc = vpc
+ if not cloudman_vpc:
+ #create a new one.
+ cloudman_vpc = vpc_conn.create_vpc('10.0.0.0/16')
+ cloudman_vpc.add_tag(CLOUDMAN_TAG_KEY, '')
+ return cloudman_vpc
+
+
+def get_or_create_cm_subnet(a_key, s_key, availability_zone):
+ log.debug("GETTING SUBNET in %s" % availability_zone)
+ vpc_conn = boto.vpc.VPCConnection(a_key, s_key)
+ subnets = vpc_conn.get_all_subnets()
+ cloudman_subnet = None
+ for subnet in subnets:
+ log.debug("SN: %s, SNAZ:AZ: %s:%s:%s" % (subnet, subnet.availability_zone, availability_zone, subnet.availability_zone == availability_zone))
+ if (not availability_zone or subnet.availability_zone == availability_zone) and CLOUDMAN_TAG_KEY in subnet.tags:
+ cloudman_subnet = subnet
+ if not cloudman_subnet:
+ #create a new one.
+ cloudman_subnet = vpc_conn.create_subnet(get_or_create_cm_vpc(a_key, s_key).id, '10.0.0.0/24', availability_zone)
+ cloudman_subnet.add_tag(CLOUDMAN_TAG_KEY, '')
+ return cloudman_subnet
+
+
+def _format_user_data(cluster_name, key_id, secret, password, share_string, bucket_default):
+ user_provided_data = {'cluster_name': cluster_name,
+ 'access_key': key_id,
+ 'secret_key': secret}
+ if password:
+ user_provided_data['password'] = password
+ if share_string:
+ user_provided_data['share_string'] = share_string
+ if bucket_default:
+ user_provided_data['bucket_default'] = bucket_default
+ return "\n".join(['%s: %s' % (key, value) for key, value in user_provided_data.iteritems() if key != 'kp_material'])
def rule_exists(rules, from_port, to_port, ip_protocol='tcp', cidr_ip='0.0.0.0/0'):
@@ -282,24 +315,22 @@
return kp.name, kp.material
-def run_instance(ec2_conn, user_provided_data, image_id=None,
- kernel_id=None, ramdisk_id=None, key_name=DEFAULT_KEYPAIR,
- placement=None, security_groups=['CloudMan']):
- """ Start an instance. If instance start was OK, return the ResultSet object
- else return None.
+def _run_instance(ec2_conn, user_provided_data, instance_type, image_id=None,
+ kernel_id=None, ramdisk_id=None, key_name=DEFAULT_KEYPAIR,
+ placement=None, security_group_ids=None, subnet_id=None):
+ """
+ Start an instance. If instance start was OK, return the ResultSet object
+ else return None.
"""
rs = None
- instance_type = user_provided_data['instance_type']
- # Remove 'instance_type' key from the dict before creating user data
- del user_provided_data['instance_type']
- ud = "\n".join(['%s: %s' % (key, value) for key, value in user_provided_data.iteritems() if key != 'kp_material'])
try:
rs = ec2_conn.run_instances(image_id=image_id,
instance_type=instance_type,
key_name=key_name,
- security_groups=security_groups,
- user_data=ud,
+ security_group_ids=security_group_ids,
+ user_data=user_provided_data,
kernel_id=kernel_id,
+ subnet_id=subnet_id,
ramdisk_id=ramdisk_id,
placement=placement)
except EC2ResponseError, e:
@@ -312,23 +343,3 @@
else:
log.warning("Problem starting an instance?")
return rs
-
-
-def _find_placement(ec2_conn, instance_type):
- """Find a region zone that supports our requested instance type.
-
- We need to check spot prices in the potential availability zones
- for support before deciding on a region:
-
- http://blog.piefox.com/2011/07/ec2-availability-zones-and-instance.html
- """
- base = ec2_conn.region.name
- yesterday = datetime.datetime.now() - datetime.timedelta(1)
- for loc_choice in ["b", "a", "c", "d"]:
- cur_loc = "{base}{ext}".format(base=base, ext=loc_choice)
- if len(ec2_conn.get_spot_price_history(instance_type=instance_type,
- end_time=yesterday.isoformat(),
- availability_zone=cur_loc)) > 0:
- return cur_loc
- log.error("Did not find availabilty zone in {0} for {1}".format(base, instance_type))
- return None
https://bitbucket.org/galaxy/galaxy-central/commits/347a9169e3ec/
Changeset: 347a9169e3ec
User: dannon
Date: 2014-04-03 21:24:32
Summary: Merge back with -central.
Affected #: 558 files
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -77,6 +77,7 @@
tool-data/shared/ucsc/builds.txt
tool-data/shared/ensembl/builds.txt
tool-data/shared/ucsc/publicbuilds.txt
+tool-data/shared/ucsc/ucsc_build_sites.txt
tool-data/*.loc
tool-data/genome/*
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 README.txt
--- a/README.txt
+++ b/README.txt
@@ -26,6 +26,6 @@
on adding tools can be found on the Galaxy website (linked above).
Not all dependencies are included for the tools provided in the sample
-tool_conf.xml. A full list of external dependencies is available at:
+tool_conf.xml. A full list of external dependencies is available at:
-http://wiki.g2.bx.psu.edu/Admin/Tools/Tool%20Dependencies
+https://wiki.galaxyproject.org/Admin/Tools/ToolDependencies
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/Gruntfile.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/Gruntfile.js
@@ -0,0 +1,24 @@
+// NOTE: use 'sudo npm install .', then 'grunt' to use this file
+module.exports = function(grunt) {
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+ requirejs: {
+ compile: {
+ options: {
+ baseUrl : "../../../../static/scripts/",
+ paths : {
+ "plugin": "../../config/plugins/visualizations/charts/static"
+ },
+ shim : {
+ "libs/underscore": { exports: "_" },
+ "libs/backbone/backbone": { exports: "Backbone" }
+ },
+ name : "plugin/app",
+ out : "static/build-app.js",
+ }
+ }
+ }
+ });
+ grunt.loadNpmTasks('grunt-contrib-requirejs');
+ grunt.registerTask( 'default', [ 'requirejs' ]);
+};
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/package.json
--- /dev/null
+++ b/config/plugins/visualizations/charts/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "galaxy-charts",
+ "version": "0.0.0",
+ "description": "Charts visualization plugin for the Galaxy informatics framework",
+ "main": " ",
+ "keywords": [
+ "galaxy",
+ "visualization",
+ "d3"
+ ],
+ "author": "Aysam Guerler",
+ "license": "BSD",
+ "devDependencies": {
+ "grunt": "~0.4.1",
+ "grunt-cli": "~0.1.9",
+ "grunt-contrib-requirejs": "~0.4.3"
+ }
+}
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/app.js
--- a/config/plugins/visualizations/charts/static/app.js
+++ b/config/plugins/visualizations/charts/static/app.js
@@ -1,10 +1,12 @@
// dependencies
-define(['mvc/ui/ui-portlet', 'plugin/library/ui', 'utils/utils',
- 'plugin/views/charts', 'plugin/views/viewport', 'plugin/views/chart', 'plugin/views/group',
- 'plugin/models/config', 'plugin/models/datasets', 'plugin/models/chart', 'plugin/models/charts', 'plugin/models/group', 'plugin/models/types'],
- function( Portlet, Ui, Utils,
- ChartsView, ViewportView, ChartView, GroupView,
- Config, Datasets, Chart, Charts, Group, Types
+define(['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( Modal, Portlet, Ui, Utils, Jobs, Datasets, Storage,
+ ViewerView, EditorView,
+ Config, Chart, Types
) {
// widget
@@ -13,66 +15,75 @@
// initialize
initialize: function(options)
{
+ // deactivate all debugs outputs
+ //window.console.debug = function() {};
+
// link options
this.options = options;
- // link galaxy
- this.modal = parent.Galaxy.modal;
+ // link galaxy modal or create one
+ if (Galaxy && Galaxy.modal) {
+ this.modal = Galaxy.modal;
+ } else {
+ this.modal = new Modal.View();
+ }
- // create configuration model
+ //
+ // models
+ //
this.config = new Config();
-
- // create chart models
this.types = new Types();
this.chart = new Chart();
- this.charts = new Charts();
- this.group = new Group();
- // create dataset handler
+ //
+ // libraries
+ //
+ this.jobs = new Jobs(this);
this.datasets = new Datasets(this);
+ this.storage = new Storage(this);
- // create views
- this.charts_view = new ChartsView(this);
- this.group_view = new GroupView(this);
- this.chart_view = new ChartView(this);
- this.viewport_view = new ViewportView(this);
-
- // append view port to charts viewer
- this.charts_view.append(this.viewport_view.$el);
-
- // create portlet
- if (!this.options.config.widget) {
- this.portlet = new Portlet.View({icon : 'fa-bar-chart-o', title : 'Charts'});
- } else {
- this.portlet = $('<div></div>');
- }
+ //
+ // views
+ //
+ this.viewer_view = new ViewerView(this);
+ this.editor_view = new EditorView(this);
// append views
- this.portlet.append(this.charts_view.$el);
- this.portlet.append(this.group_view.$el);
- this.portlet.append(this.chart_view.$el);
-
- // set element
- if (!this.options.config.widget) {
- this.setElement(this.portlet.$el);
+ this.$el.append(this.viewer_view.$el);
+ this.$el.append(this.editor_view.$el);
+
+ // pick start screen
+ if (!this.storage.load()) {
+ // show editor
+ this.go('editor');
} else {
- this.setElement(this.portlet);
+ // show viewport
+ this.go('viewer');
+
+ // draw chart
+ var self = this;
+ this.chart.deferred.execute(function() {
+ self.chart.trigger('redraw');
+ });
}
-
- // hide views
- this.group_view.$el.hide();
- this.charts_view.$el.hide();
-
- // events
- var self = this;
- this.config.on('change:current_view', function() {
- self._showCurrent();
- });
},
- // current view
- _showCurrent: function() {
+ // loads a view and makes sure that all others are hidden
+ go: function(view_id) {
+ // hide all tooltips
+ $('.tooltip').hide();
+ // pick view
+ switch (view_id) {
+ case 'editor' :
+ this.editor_view.show();
+ this.viewer_view.hide();
+ break;
+ case 'viewer' :
+ this.editor_view.hide();
+ this.viewer_view.show();
+ break;
+ }
},
// execute command
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/build-app.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/build-app.js
@@ -0,0 +1,6 @@
+// Underscore.js 1.4.4
+// http://underscorejs.org
+// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
+// Underscore may be freely distributed under the MIT license.
+
+(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-modal",["utils/utils"],function(e){var t=Backbone.View.extend({elMain:"body",optionsDefault:{title:"ui-modal",body:"",backdrop:!0,height:null,width:null,closing_events:!1},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")},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:t}}),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.setElement(this._template(this.options)),this.$select=this.$el.find("#select"),this.$icon=this.$el.find("#icon"),this.selected=this.options.value;var n=this;this.options.onchange&&this.$select.on("change",function(){n.value(n.$select.val())}),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},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.selected&&e.length>0&&this.value(e[0].value),this._refresh()},_refresh:function(){this.$select.find("option[value=null]").remove();var e=this.$select.find("option").length;e==0?(this.$select.append(this._templateOption({value:"null",label:this.options.empty})),this.disable()):(this.enable(),this.selected&&this.$select.val(this.selected))},_exists:function(e){return 0!=this.$select.find("option[value="+e+"]").length},_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:{title:""},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><b>"+e.title+"</b></label>"},value:function(){return options.title}}),r=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}}),i=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>"}}),s=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}}),o=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>"}}),u=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>"}}),a=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>"}}),f=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>"}}),l=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>'),"</button></div>",t}}),c=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.options.onchange&&this.$el.on("input",function(){n.options.onchange()})},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:n,Button:r,Icon:i,ButtonIcon:s,Input:c,Anchor:o,Message:u,Searchbox:a,Title:f,Select:t,ButtonMenu:l}}),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)},cleanup:function(t){var n=t.get("dataset_id_job");if(n!=""){var r=this;e.request("PUT",config.root+"api/histories/none/contents/"+n,{deleted:!0},function(){r._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:"chartskit",inputs:{input:{id:t.get("dataset_id"),src:"hda"},module:a,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","Job has been queued..."),t.set("dataset_id_job",n.id),this.app.storage.save(),o._loop(n.id,function(e){switch(e.state){case"ok":return t.state("wait","Job completed successfully..."),i(e),!0;case"error":return t.state("failed","Job has failed. Please check the history for details."),s&&s(),!0;case"running":return t.state("wait","Job is running..."),!1}})}},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-Kit. Please make sure it is installed. "+n),s&&s()})},_loop:function(t,n){var r=this;e.request("GET",config.root+"api/jobs/"+t,{},function(e){n(e)||setTimeout(function(){r._loop(t,n)},r.app.config.get("query_timeout"))})},_refreshHdas:function(){Galaxy&&Galaxy.currHistoryPanel&&Galaxy.currHistoryPanel.refreshHdas()}})}),define("plugin/library/datasets",["utils/utils"],function(e){return Backbone.Collection.extend({list:{},initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault)},request:function(t,n,r){var i=this;if(t.groups)this._fetch(t,n);else{var s=this.list[t.id];if(s){n(s);return}e.request("GET",config.root+"api/datasets/"+t.id,{},function(e){switch(e.state){case"error":r&&r(e);break;default:i.list[t.id]=e,n(e)}})}},_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="",u={},a=0;for(var f in t.groups){var l=t.groups[f];for(var c in l.columns){var h=l.columns[c];o+=h+",",u[h]=a,a++}}if(a==0){n({});return}o=o.substring(0,o.length-1);var p=t.groups.slice(0);for(var f in p)p[f].values=[];var d=this;e.request("GET",config.root+"api/datasets/"+t.id,{data_type:"raw_data",provider:"dataset-column",limit:i,offset:r,indeces:o},function(e){for(var i in e.data){var s=e.data[i];for(var o in t.groups){var a=t.groups[o],f={x:parseInt(i)+r};for(var l in a.columns){var c=a.columns[l],h=u[c],d=s[h];if(isNaN(d)||!d)d=0;f[l]=d}p[o].values.push(f)}}n(p)})}})}),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)this.queue[e](),this.queue.splice(e,1)})},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){delete this.process[e],this.counter--,console.debug("Deferred:done() - Unregistering "+e),this.trigger("refresh")},ready:function(){return this.counter==0?!0:!1}})});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/views/viewport",["mvc/ui/ui-portlet","plugin/library/ui","utils/utils"],function(e,t,n){return Backbone.View.extend({initialize:function(e,t){this.app=e,this.chart=this.app.chart,this.options=n.merge(t,this.optionsDefault),this.setElement($(this._template())),this._create_svg();var r=this;this.chart.on("redraw",function(){r._draw(r.chart)}),this.chart.on("set:state",function(){var e=r.$el.find("#info"),t=e.find("#icon");t.removeClass(),e.show(),e.find("#text").html(r.chart.get("state_info"));var n=r.chart.get("state");switch(n){case"ok":e.hide();break;case"failed":t.addClass("fa fa-warning");break;default:t.addClass("fa fa-spinner fa-spin")}})},show:function(){this.$el.show()},hide:function(){this.$el.hide()},_create_svg:function(){this.svg&&this.svg.remove(),this.$el.append($(this._template_svg())),this.svg_el=this.$el.find("svg"),this.svg=d3.select(this.svg_el[0])},_draw:function(e){var t=this;this._create_svg(),e.state("wait","Please wait...");var n=e.deferred.register(),r=e.get("type"),i=this.app.types.get(r);if(!i.execute||i.execute&&e.get("modified"))this.app.jobs.cleanup(e),e.set("modified",!1);var t=this;require(["plugin/charts/"+r+"/"+r],function(r){var s=new r(t.app,{svg:t.svg});i.execute?e.get("dataset_id_job")==""?t.app.jobs.submit(e,t._defaultSettingsString(e),t._defaultRequestString(e),function(){s.draw(n,e,t._defaultRequestDictionary(e))},function(){e.deferred.done(n)}):s.draw(n,e,t._defaultRequestDictionary(e)):s.draw(n,e,t._defaultRequestDictionary(e))})},_defaultRequestString:function(e){var t=this.app.types.get(e.get("type")),n="",r=0;return e.groups.each(function(e){for(var i in t.columns)n+=i+"_"+ ++r+":"+(parseInt(e.get(i))+1)+", "}),n.substring(0,n.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=this.app.types.get(e.get("type")),n={groups:[]};t.execute?n.id=e.get("dataset_id_job"):n.id=e.get("dataset_id");var r=0;return e.groups.each(function(e){var i={};for(var s in t.columns)i[s]=e.get(s);n.groups.push({key:++r+":"+e.get("key"),columns:i})}),n},_template:function(){return'<div style="height: 100%; min-height: 50px;"><div id="info" style="position: absolute; margin-left: 10px; margin-top: 10px; margin-bottom: 50px;"><span id="icon" style="font-size: 1.2em; display: inline-block;"/><span id="text" style="position: relative; margin-left: 5px; top: -1px; font-size: 1.0em;"/></div></div>'},_template_svg:function(){return'<svg style="height: calc(100% - 80px)"/>'}})}),define("plugin/views/viewer",["utils/utils","plugin/library/ui","mvc/ui/ui-portlet","plugin/models/group","plugin/views/viewport"],function(e,t,n,r,i){return Backbone.View.extend({initialize:function(e,r){this.app=e,this.chart=this.app.chart,this.viewport_view=new i(e);var s=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:"Customize",onclick:function(){s._wait(s.chart,function(){s.app.go("editor")})}})}}),this.portlet.append(this.viewport_view.$el),this.setElement(this.portlet.$el);var s=this;this.chart.on("change:title",function(){s._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)},_screenshot:function(){var e=new XMLSerializer,t=e.serializeToString(this.viewport_view.svg.node()),n="data:image/svg+xml;base64,"+btoa(t);window.location.href="data:application/x-download/;charset=utf-8,"+encodeURIComponent(t)},_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},events:{click:"_onclick",dblclick:"_ondblclick"},first:!0,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){var n=$("<td></td>");t&&n.css("width",t),n.append(e),this.row.append(n)},append:function(e){this._commit(e)},prepend:function(e){this._commit(e,!0)},remove:function(e){var t=this.$tbody.find("#"+e);t.length>0&&(t.remove(),this.row_count--,this._refresh())},removeAll: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.remove(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&&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="grid"><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({columns:[],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(){s.group.set("key",s.group_key.value())}}),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.removeAll();var s={};for(var o in i.columns){var u=this.group.get(o);u||this.group.set(o,0);var a=i.columns[o],f=new t.Select.View({id:"select_"+o,gid:o,onchange:function(e){r.group.set(this.gid,e),r.chart.set("modified",!0)},value:u,wait:!0});this.table.add(a.title,"25%"),this.table.add(f.$el),this.table.append(o),s[o]=f}this.chart.state("wait","Loading metadata...");var l=this.chart.deferred.register();this.app.datasets.request({id:e},function(e){r.columns=[];var t=e.metadata_column_types;for(var n in t)(t[n]=="int"||t[n]=="float")&&r.columns.push({label:"Column: "+(parseInt(n)+1)+" ["+t[n]+"]",value:n});for(var n in s)s[n].update(r.columns),s[n].show();r.chart.state("wait","Metadata initialized..."),r.chart.deferred.done(l)})},_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({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.removeAll(),this.list=[];for(var n in e)this._add(n,e[n],t);for(var n in this.list){var r=this.list[n].options.onchange;r&&r()}},_add:function(e,n,r){var i=this,s=null,o=n.type;switch(o){case"text":s=new t.Input({id:e,placeholder:n.placeholder,onchange:function(){r.set(e,s.value())}});break;case"select":s=new t.Select.View({id:e,data:n.data,onchange:function(){var t=s.value();r.set(e,t);var o=_.findWhere(n.data,{value:t});o&&(o.show&&i.$el.find("#"+o.show).fadeIn("fast"),o.hide&&i.$el.find("#"+o.hide).fadeOut("fast"))}});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.title+":"),this.form.update(t.settings,this.chart.settings)}})}),define("plugin/views/editor",["mvc/ui/ui-tabs","plugin/library/ui-table","plugin/library/ui","mvc/ui/ui-portlet","utils/utils","plugin/models/chart","plugin/models/group","plugin/views/group","plugin/views/settings"],function(e,t,n,r,i,s,o,u,a){return Backbone.View.extend({optionsDefault:{header:!0,content:"No content available."},initialize:function(s,o){var u=this;this.app=s,this.chart=this.app.chart,this.options=i.merge(o,this.optionsDefault),this.portlet=new r.View({icon:"fa-bar-chart-o",title:"Editor",operations:{save:new n.ButtonIcon({icon:"fa-save",tooltip:"Draw Chart",title:"Draw",onclick:function(){u.app.go("viewer"),u._saveChart()}}),back:new n.ButtonIcon({icon:"fa-caret-left",tooltip:"Return to Viewer",title:"Return",onclick:function(){u.app.go("viewer"),u.app.storage.load()}})}}),this.table=new t.View({header:!1,onconfirm:function(e){u.chart.groups.length>0?u.app.modal.show({title:"Switching to another chart type?",body:"If you continue your settings and selections will be cleared.",buttons:{Cancel:function(){u.app.modal.hide()},Continue:function(){u.app.modal.hide(),u.table.value(e)}}}):u.table.value(e)},onchange:function(e){u.chart.groups.reset(),u.chart.settings.clear(),u.chart.set({type:e})},ondblclick:function(e){u.tabs.show("settings")},content:"No chart types available"});var f=0,l=s.types.attributes;for(var c in l){var h=l[c];this.table.add(++f+"."),h.execute?this.table.add(h.title+" (requires processing)"):this.table.add(h.title),this.table.append(c)}this.tabs=new e.View({title_new:"Add Data",onnew:function(){var e=u._addGroupModel();u.tabs.show(e.id)}}),this.title=new n.Input({placeholder:"Chart title",onchange:function(){u.chart.set("title",u.title.value())}});var p=$("<div/>");p.append(i.wrap((new n.Label({title:"Provide a chart title:"})).$el)),p.append(i.wrap(this.title.$el)),p.append(i.wrap((new n.Label({title:"Select a chart type:"})).$el)),p.append(i.wrap(this.table.$el)),this.tabs.add({id:"main",title:"Start",$el:p}),this.settings=new a(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 u=this;this.chart.on("change:title",function(e){u._refreshTitle()}),this.chart.on("change:type",function(e){u.table.value(e.get("type"))}),this.chart.on("reset",function(e){u._resetChart()}),this.app.chart.on("redraw",function(e){u.portlet.showOperation("back")}),this.app.chart.groups.on("add",function(e){u._addGroup(e)}),this.app.chart.groups.on("remove",function(e){u._removeGroup(e)}),this.app.chart.groups.on("reset",function(e){u._removeAllGroups()}),this.app.chart.groups.on("change:key",function(e){u._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 o({id:i.uuid()});return this.chart.groups.add(e),e},_addGroup:function(e){var t=this,n=new u(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",i.uuid()),this.chart.set("type","bardiagram"),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.table.value(),title:this.title.value(),date:i.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:1e3,query_timeout:500}})}),define("plugin/charts/_nvd3/config",[],function(){return{title:"",columns:{y:{title:"Values for y-axis"}},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/bardiagram/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Bar diagram"})}),define("plugin/charts/histogram/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Histogram",execute:!0,columns:{y:{title:"Observations"}},settings:{x_axis_label:{init:"Breaks"},y_axis_label:{init:"Density"}}})}),define("plugin/charts/horizontal/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Bar diagram (horizontal)",settings:{x_axis_type:{init:"hide"}}})}),define("plugin/charts/line/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line chart"})}),define("plugin/charts/linewithfocus/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line with focus"})}),define("plugin/charts/piechart/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Pie chart"})}),define("plugin/charts/scatterplot/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Scatter plot",columns:{x:{title:"Values for x-axis"}}})}),define("plugin/charts/stackedarea/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Stacked area"})}),define("plugin/charts/types",["plugin/charts/bardiagram/config","plugin/charts/histogram/config","plugin/charts/horizontal/config","plugin/charts/line/config","plugin/charts/linewithfocus/config","plugin/charts/piechart/config","plugin/charts/scatterplot/config","plugin/charts/stackedarea/config"],function(e,t,n,r,i,s,o,u){return Backbone.Model.extend({defaults:{bardiagram:e,horizontal:n,histogram:t,line:r,linewithfocus:i,piechart:s,scatterplot:o,stackedarea:u}})}),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 e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/_nvd3/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/_nvd3/config.js
@@ -0,0 +1,218 @@
+define([], function() {
+
+return {
+ title : '',
+ columns : {
+ y : {
+ title : 'Values for y-axis'
+ }
+ },
+ 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'
+ }
+ ]
+ },
+ }
+};
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/_nvd3/nvd3.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/_nvd3/nvd3.js
@@ -0,0 +1,80 @@
+// dependencies
+define([], function() {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, nvd3_model, chart, request_dictionary, callback)
+ {
+ // request data
+ var self = this;
+ this.app.datasets.request(request_dictionary, function(data) {
+ nv.addGraph(function() {
+ // x axis
+ self._axis(nvd3_model.xAxis, chart.settings.get('x_axis_type'), chart.settings.get('x_axis_tick'));
+
+ // x axis label
+ nvd3_model.xAxis.axisLabel(chart.settings.get('x_axis_label'));
+
+ // y axis
+ self._axis(nvd3_model.yAxis, chart.settings.get('y_axis_type'), chart.settings.get('y_axis_tick'));
+
+ // y axis label
+ nvd3_model.yAxis.axisLabel(chart.settings.get('y_axis_label'))
+ .axisLabelDistance(30);
+
+ // controls
+ if (chart.groups.length == 1) {
+ nvd3_model.options({ showControls: false });
+ }
+
+ // legend
+ var legend_visible = true;
+ if (chart.settings.get('show_legend') == 'false') {
+ legend_visible = false;
+ }
+ nvd3_model.showLegend(legend_visible);
+
+ // custom callback
+ if (callback) {
+ callback(nvd3_model);
+ }
+
+ // parse data to svg
+ self.options.svg.datum(data)
+ .call(nvd3_model);
+
+ // refresh on window resize
+ nv.utils.windowResize(nvd3_model.update);
+
+ // set chart state
+ chart.state('ok', 'Chart has been drawn.');
+
+ // unregister process
+ chart.deferred.done(process_id);
+ });
+ });
+ },
+
+ // make axis
+ _axis: function(axis, type, tick) {
+ switch (type) {
+ case 'hide':
+ axis.tickFormat(function() { return '' });
+ break;
+ case 'auto':
+ break;
+ default:
+ axis.tickFormat(d3.format(tick + type));
+ }
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/bardiagram.js
--- a/config/plugins/visualizations/charts/static/charts/bardiagram.js
+++ /dev/null
@@ -1,33 +0,0 @@
-// dependencies
-define([], function() {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- },
-
- // render
- refresh : function(data)
- {
- // add graph to screen
- var self = this;
- nv.addGraph(function() {
- self.d3_chart = nv.models.multiBarChart();
-
- self.d3_chart.xAxis.tickFormat(d3.format('.2f'))
- self.d3_chart.yAxis.tickFormat(d3.format('.1f'))
-
- d3.select(self.options.svg_id)
- .datum(data)
- .call(self.d3_chart);
-
- nv.utils.windowResize(self.d3_chart.update);
- });
- }
-});
-
-});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/bardiagram/bardiagram.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/bardiagram/bardiagram.js
@@ -0,0 +1,21 @@
+// dependencies
+define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.multiBarChart(), chart, request_dictionary);
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/bardiagram/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/bardiagram/config.js
@@ -0,0 +1,7 @@
+define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Bar diagram',
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/histogram/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/histogram/config.js
@@ -0,0 +1,21 @@
+define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Histogram',
+ execute : true,
+ columns : {
+ y : {
+ title : 'Observations'
+ }
+ },
+ settings : {
+ x_axis_label : {
+ init : 'Breaks'
+ },
+ y_axis_label : {
+ init : 'Density'
+ }
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/histogram/histogram.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/histogram/histogram.js
@@ -0,0 +1,31 @@
+// dependencies
+define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ // configure request
+ var index = 0;
+ for (var i in request_dictionary.groups) {
+ var group = request_dictionary.groups[i];
+ group.columns = {
+ x: index++,
+ y: index++
+ }
+ }
+
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.multiBarChart(), chart, request_dictionary);
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/horizontal.js
--- a/config/plugins/visualizations/charts/static/charts/horizontal.js
+++ /dev/null
@@ -1,33 +0,0 @@
-// dependencies
-define(['utils/utils'], function(Utils) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- this.chart = options.chart;
- },
-
- // render
- refresh : function(data)
- {
- // add graph to screen
- var self = this;
- nv.addGraph(function() {
- self.d3_chart = nv.models.multiBarHorizontalChart();
-
- self.d3_chart.xAxis.tickFormat(function() { return ''; });
-
- d3.select(self.options.svg_id)
- .datum(data)
- .call(self.d3_chart);
-
- nv.utils.windowResize(self.d3_chart.update);
- });
- }
-});
-
-});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/horizontal/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/horizontal/config.js
@@ -0,0 +1,12 @@
+define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Bar diagram (horizontal)',
+ settings : {
+ x_axis_type : {
+ init : 'hide'
+ }
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/horizontal/horizontal.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/horizontal/horizontal.js
@@ -0,0 +1,21 @@
+// dependencies
+define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.multiBarHorizontalChart(), chart, request_dictionary);
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/line.js
--- a/config/plugins/visualizations/charts/static/charts/line.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// dependencies
-define(['utils/utils'], function(Utils) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- this.chart = options.chart;
- },
-
- // render
- refresh : function(data)
- {
- // add graph to screen
- var self = this;
- nv.addGraph(function() {
- self.chart_3d = nv.models.lineChart();
-
- self.chart_3d.xAxis
- .tickFormat(d3.format(',f'));
-
- self.chart_3d.yAxis
- .tickFormat(d3.format(',.2f'));
-
- d3.select(self.options.svg_id)
- .datum(data)
- .call(self.chart_3d);
-
- nv.utils.windowResize(self.chart_3d.update);
-
- return self.chart_3d;
- });
- }
-});
-
-});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/line/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/line/config.js
@@ -0,0 +1,7 @@
+define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Line chart',
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/line/line.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/line/line.js
@@ -0,0 +1,21 @@
+// dependencies
+define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.lineChart(), chart, request_dictionary);
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/linewithfocus.js
--- a/config/plugins/visualizations/charts/static/charts/linewithfocus.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// dependencies
-define(['utils/utils'], function(Utils) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- this.chart = options.chart;
- },
-
- // render
- refresh : function(data)
- {
- // add graph to screen
- var self = this;
- nv.addGraph(function() {
- self.chart_3d = nv.models.lineWithFocusChart();
-
- self.chart_3d.xAxis
- .tickFormat(d3.format(',f'));
-
- self.chart_3d.yAxis
- .tickFormat(d3.format(',.2f'));
-
- d3.select(self.options.svg_id)
- .datum(data)
- .call(self.chart_3d);
-
- nv.utils.windowResize(self.chart_3d.update);
- });
- }
-});
-
-});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/linewithfocus/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/linewithfocus/config.js
@@ -0,0 +1,7 @@
+define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Line with focus',
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/linewithfocus/linewithfocus.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/linewithfocus/linewithfocus.js
@@ -0,0 +1,21 @@
+// dependencies
+define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.lineWithFocusChart(), chart, request_dictionary);
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/piechart.js
--- a/config/plugins/visualizations/charts/static/charts/piechart.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// dependencies
-define(['utils/utils'], function(Utils) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- this.chart = options.chart;
- },
-
- // render
- refresh : function(data)
- {
- // loop through data groups
- for (var key in data) {
- // get group
- var group = data[key];
-
- // format chart data
- var pie_data = [];
- for (var key in group.values) {
- var value = group.values[key];
- pie_data.push ({
- key : value.x,
- y : value.y
- });
- }
-
- // add graph to screen
- var self = this;
- nv.addGraph(function() {
- self.chart_3d = nv.models.pieChart()
- .donut(true)
- .showLegend(false);
-
- d3.select(self.options.svg_id)
- .datum(pie_data)
- .call(self.chart_3d);
-
- nv.utils.windowResize(self.chart_3d.update);
-
- return self.chart_3d;
- });
- }
- }
-});
-
-});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/piechart/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/piechart/config.js
@@ -0,0 +1,7 @@
+define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Pie chart',
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/piechart/piechart.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/piechart/piechart.js
@@ -0,0 +1,57 @@
+// dependencies
+define(['utils/utils'], function(Utils) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ // request data
+ var self = this;
+ this.app.datasets.request(request_dictionary, function(data) {
+
+ // loop through data groups
+ for (var key in request_dictionary.groups) {
+ // get group
+ var group = request_dictionary.groups[key];
+
+ // format chart data
+ var pie_data = [];
+ for (var key in group.values) {
+ var value = group.values[key];
+ pie_data.push ({
+ key : value.x,
+ y : value.y
+ });
+ }
+
+ // add graph to screen
+ nv.addGraph(function() {
+ self.chart_3d = nv.models.pieChart()
+ .donut(true)
+ .showLegend(false);
+
+ self.options.svg.datum(pie_data)
+ .call(self.chart_3d);
+
+ nv.utils.windowResize(self.chart_3d.update);
+
+ // set chart state
+ chart.state('ok', 'Pie chart drawn.');
+
+ // unregister process
+ chart.deferred.done(process_id);
+ });
+ }
+ });
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/scatterplot.js
--- a/config/plugins/visualizations/charts/static/charts/scatterplot.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// dependencies
-define(['utils/utils'], function(Utils) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- this.chart = options.chart;
- },
-
- // render
- refresh : function(data)
- {
- // add graph to screen
- var self = this;
- nv.addGraph(function() {
- self.d3_chart = nv.models.scatterChart()
- .showDistX(true)
- .showDistY(true)
- .color(d3.scale.category10().range());
-
- self.d3_chart.xAxis.tickFormat(d3.format('.02f'))
- self.d3_chart.yAxis.tickFormat(d3.format('.02f'))
-
- d3.select(self.options.svg_id)
- .datum(data)
- .call(self.d3_chart);
-
- nv.utils.windowResize(self.d3_chart.update);
- });
- }
-});
-
-});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/scatterplot/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/scatterplot/config.js
@@ -0,0 +1,12 @@
+define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Scatter plot',
+ columns : {
+ x : {
+ title : 'Values for x-axis'
+ }
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/scatterplot/scatterplot.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/scatterplot/scatterplot.js
@@ -0,0 +1,25 @@
+// dependencies
+define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.scatterChart(), chart, request_dictionary, function(nvd3_model) {
+ nvd3_model.showDistX(true)
+ .showDistY(true)
+ .color(d3.scale.category10().range());
+ });
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/stackedarea.js
--- a/config/plugins/visualizations/charts/static/charts/stackedarea.js
+++ /dev/null
@@ -1,59 +0,0 @@
-// dependencies
-define(['utils/utils'], function(Utils) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- this.chart = options.chart;
- },
-
- // render
- refresh : function(data)
- {
- // add graph to screen
- var self = this;
- nv.addGraph(function() {
- // check data
- var valid = true;
- var length = 0;
- for (var key in data) {
- // evalute length
- if (length == 0) {
- length = data[key].values.length;
- } else {
- if (length != data[key].values.length) {
- valid = false;
- break;
- }
- }
- }
- if (!valid) {
- return;
- }
-
- // make plot
- self.d3_chart = nv.models.stackedAreaChart()
- .x(function(d) {
- return d.x
- })
- .y(function(d) {
- return d.y
- })
- .clipEdge(true);
-
- self.d3_chart.xAxis.tickFormat(function() { return ''; });
-
- d3.select(self.options.svg_id)
- .datum(data)
- .call(self.d3_chart);
-
- nv.utils.windowResize(self.d3_chart.update);
- });
- }
-});
-
-});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/stackedarea/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/stackedarea/config.js
@@ -0,0 +1,7 @@
+define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Stacked area'
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/stackedarea/stackedarea.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/stackedarea/stackedarea.js
@@ -0,0 +1,26 @@
+// dependencies
+define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.stackedAreaChart(), chart, request_dictionary, function(nvd3_model) {
+ // make plot
+ nvd3_model.x(function(d) { return d.x })
+ .y(function(d) { return d.y })
+ .clipEdge(true);
+ });
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/charts/types.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/types.js
@@ -0,0 +1,39 @@
+// dependencies
+define(['plugin/charts/bardiagram/config',
+ //'plugin/charts/boxplot/config',
+ 'plugin/charts/histogram/config',
+ 'plugin/charts/horizontal/config',
+ 'plugin/charts/line/config',
+ 'plugin/charts/linewithfocus/config',
+ 'plugin/charts/piechart/config',
+ 'plugin/charts/scatterplot/config',
+ 'plugin/charts/stackedarea/config',
+ ], function(bardiagram,
+ //boxplot,
+ histogram,
+ horizontal,
+ line,
+ linewithfocus,
+ piechart,
+ scatterplot,
+ stackedarea
+ ) {
+
+// widget
+return Backbone.Model.extend(
+{
+ // types
+ defaults: {
+ 'bardiagram' : bardiagram,
+ //'boxplot' : boxplot,
+ 'horizontal' : horizontal,
+ 'histogram' : histogram,
+ 'line' : line,
+ 'linewithfocus' : linewithfocus,
+ 'piechart' : piechart,
+ 'scatterplot' : scatterplot,
+ 'stackedarea' : stackedarea
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/library/datasets.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/library/datasets.js
@@ -0,0 +1,149 @@
+// dependencies
+define(['utils/utils'], function(Utils) {
+
+// widget
+return Backbone.Collection.extend(
+{
+ // list of datasets
+ list: {},
+
+ // initialize
+ initialize: function(app, options)
+ {
+ // link app
+ this.app = app;
+
+ // configure options
+ this.options = Utils.merge(options, this.optionsDefault);
+ },
+
+ // wait
+ request: function(options, success, error) {
+ // link this
+ var self = this;
+
+ // check if column data is requested
+ if (options.groups) {
+ this._fetch(options, success);
+ } else {
+ // check if dataset is available from cache
+ var dataset = this.list[options.id];
+ if (dataset) {
+ success(dataset);
+ return;
+ }
+
+ // request dataset
+ Utils.request('GET', config.root + 'api/datasets/' + options.id, {}, function(dataset) {
+ switch (dataset.state) {
+ case 'error':
+ if (error) {
+ error(dataset);
+ }
+ break;
+ default:
+ self.list[options.id] = dataset;
+ success(dataset);
+ }
+ });
+ }
+ },
+
+ // fetch data columns into dataset object
+ _fetch: function(options, callback) {
+ // set offset
+ var offset = options.start ? options.start : 0;
+
+ // set limit
+ var limit = Math.abs(options.end - options.start);
+ var query_limit = this.app.config.get('query_limit');
+ if (!limit || limit > query_limit) {
+ limit = query_limit;
+ }
+
+ // get column indices
+ var index_string = '';
+ var index_map = {};
+ var index_count = 0;
+ for (var i in options.groups) {
+ var group = options.groups[i];
+ for (var key in group.columns) {
+ var column = group.columns[key];
+
+ // add to index string
+ index_string += column + ',';
+
+ // add to dictionary
+ index_map[column] = index_count;
+
+ // increase counter
+ index_count++;
+ }
+ }
+ if (index_count == 0) {
+ callback({});
+ return;
+ }
+ index_string = index_string.substring(0, index_string.length - 1);
+
+ // initialize result dictionary
+ var result = options.groups.slice(0);
+ for (var i in result) {
+ result[i].values = [];
+ }
+
+ // make request
+ var self = this;
+ Utils.request('GET', config.root + 'api/datasets/' + options.id, {
+ data_type : 'raw_data',
+ provider : 'dataset-column',
+ limit : limit,
+ offset : offset,
+ indeces : index_string
+ }, function(response) {
+
+ // loop through rows
+ for (var i in response.data) {
+ // get row
+ var row = response.data[i];
+
+ // collect all data into the defined groups
+ for (var j in options.groups) {
+ // get group
+ var group = options.groups[j];
+
+ // initialize value
+ var value = {
+ x : parseInt(i) + offset
+ };
+
+ // fill value
+ for (var key in group.columns) {
+ // get column
+ var column = group.columns[key];
+
+ // identify column
+ var index = index_map[column];
+
+ // read value from row
+ var v = row[index];
+ if(isNaN(v) || !v) {
+ v = 0;
+ }
+
+ // add value to dictionary
+ value[key] = v;
+ }
+
+ // add to result
+ result[j].values.push(value);
+ }
+ }
+
+ // callback
+ callback(result);
+ });
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/library/deferred.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/library/deferred.js
@@ -0,0 +1,85 @@
+// dependencies
+define(['utils/utils'], function(Utils) {
+
+// deferred process handler
+return Backbone.Model.extend(
+{
+ // queue
+ queue: [],
+
+ // list of currently registered processes
+ process: {},
+
+ // process counter
+ counter: 0,
+
+ // initialize
+ initialize: function()
+ {
+ // loop through queue and check states
+ this.on('refresh', function() {
+ if (this.counter == 0) {
+ for (var index in this.queue) {
+ // execute callback
+ this.queue[index]();
+
+ // remove callback
+ this.queue.splice(index, 1);
+ }
+ }
+ });
+ },
+
+ // executes callback once all processes are unregistered
+ execute: function(callback) {
+ // add wrapper to queue
+ this.queue.push(callback);
+
+ // trigger change
+ this.trigger('refresh');
+ },
+
+ // register process
+ register: function() {
+ // create unique id
+ var id = Utils.uuid();
+
+ // add process to queue
+ this.process[id] = true;
+
+ // increase process counter
+ this.counter++;
+
+ // log
+ console.debug('Deferred:register() - Registering ' + id);
+
+ // return unique id
+ return id;
+ },
+
+ // unregister process
+ done: function(id) {
+ // delete tag
+ delete this.process[id];
+
+ // decrease process counter
+ this.counter--;
+
+ // log
+ console.debug('Deferred:done() - Unregistering ' + id);
+
+ // trigger change
+ this.trigger('refresh');
+ },
+
+ // ready
+ ready: function() {
+ if (this.counter == 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/library/jobs.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/library/jobs.js
@@ -0,0 +1,152 @@
+// dependencies
+define(['utils/utils'], function(Utils) {
+
+// widget
+return Backbone.Model.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ // link app
+ this.app = app;
+
+ // link options
+ this.options = Utils.merge(options, this.optionsDefault);
+ },
+
+ // clean
+ cleanup: function(chart) {
+ // cleanup previous dataset file
+ var previous = chart.get('dataset_id_job');
+ if (previous != '') {
+ var self = this;
+ Utils.request('PUT', config.root + 'api/histories/none/contents/' + previous, { deleted: true }, function() {
+ // update galaxy history
+ self._refreshHdas();
+ });
+
+ // reset id
+ chart.set('dataset_id_job', '');
+ }
+ },
+
+ // create job
+ submit: function(chart, settings_string, columns_string, success, error) {
+ // link this
+ var self = this;
+
+ // backup chart details
+ var chart_id = chart.id;
+ var chart_type = chart.get('type');
+
+ // get chart settings
+ var chart_settings = this.app.types.get(chart_type);
+
+ // configure tool
+ data = {
+ 'tool_id' : 'chartskit',
+ 'inputs' : {
+ 'input' : {
+ 'id' : chart.get('dataset_id'),
+ 'src' : 'hda'
+ },
+ 'module' : chart_type,
+ 'columns' : columns_string,
+ 'settings' : settings_string
+ }
+ }
+
+ // set chart state
+ chart.state('wait', 'Sending job request...');
+
+ // post job
+ Utils.request('POST', config.root + 'api/tools', data,
+ // success handler
+ function(response) {
+ if (!response.outputs || response.outputs.length == 0) {
+ chart.state('failed', 'Job submission failed. No response.');
+
+ // call error
+ error && error();
+ } else {
+ // update galaxy history
+ self._refreshHdas();
+
+ // get dataset
+ var job = response.outputs[0];
+
+ // check dataset
+ chart.state('wait', 'Job has been queued...');
+
+ // backup resulting dataset id
+ chart.set('dataset_id_job', job.id);
+
+ // save
+ this.app.storage.save();
+
+ // wait for job completion
+ self._loop(job.id, function(job) {
+ switch (job.state) {
+ case 'ok':
+ // update state
+ chart.state('wait', 'Job completed successfully...');
+
+ // execute success
+ success(job);
+
+ // stop loop
+ return true;
+ case 'error':
+ // update state
+ chart.state('failed', 'Job has failed. Please check the history for details.');
+
+ // call error
+ error && error();
+
+ // stop loop
+ return true;
+ case 'running':
+ // wait
+ chart.state('wait', 'Job is running...');
+
+ // continue loop
+ return false;
+ }
+ });
+ }
+ },
+ // error handler
+ function(response) {
+ var message = '';
+ if (response && response.message && response.message.data && response.message.data.input) {
+ message = response.message.data.input + '.';
+ }
+ // update state
+ chart.state('failed', 'This visualization requires the Charts-Kit. Please make sure it is installed. ' + message);
+
+ // call error
+ error && error();
+
+ }
+ );
+ },
+
+ // request job details
+ _loop: function(id, callback) {
+ var self = this;
+ Utils.request('GET', config.root + 'api/jobs/' + id, {}, function(job) {
+ if (!callback(job)) {
+ setTimeout(function() { self._loop(id, callback); }, self.app.config.get('query_timeout'));
+ }
+ });
+ },
+
+ // refresh history panel
+ _refreshHdas: function() {
+ // update galaxy history
+ if (Galaxy && Galaxy.currHistoryPanel) {
+ Galaxy.currHistoryPanel.refreshHdas();
+ }
+ }
+});
+
+});
\ No newline at end of file
diff -r e45c2616e207e25e4978d14343bd5bff495a16b0 -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 config/plugins/visualizations/charts/static/library/storage.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/library/storage.js
@@ -0,0 +1,120 @@
+// dependencies
+define(['utils/utils', 'plugin/models/chart', 'plugin/models/group'], function(Utils, Chart, Group) {
+
+// collection
+return Backbone.Model.extend(
+{
+ // viz model
+ vis: null,
+
+ // initialize
+ initialize: function(app) {
+ // link app
+ this.app = app;
+
+ // link chart
+ this.chart = this.app.chart;
+
+ // link options
+ this.options = this.app.options;
+
+ // initialize parameters
+ this.id = this.options.id;
+
+ // create visualization
+ this.vis = new Visualization({
+ type : 'charts',
+ config : {
+ dataset_id : this.options.config.dataset_id,
+ chart_dict : {}
+ }
+ });
+
+ // add visualization id
+ if (this.id) {
+ this.vis.id = this.id;
+ }
+
+ // add charts
+ var chart_dict = this.options.config.chart_dict;
+ if (chart_dict) {
+ this.vis.get('config').chart_dict = chart_dict;
+ }
+ },
+
+ // pack and save nested chart model
+ save: function() {
+
+ // link chart
+ var chart = this.app.chart;
+
+ // reset
+ this.vis.get('config').chart_dict = {};
+
+ // set title
+ var title = chart.get('title');
+ if (title != '') {
+ this.vis.set('title', title);
+ }
+
+ // create chart dictionary
+ var chart_dict = {
+ attributes : chart.attributes,
+ settings : chart.settings.attributes,
+ groups : []
+ };
+
+ // append groups
+ chart.groups.each(function(group) {
+ chart_dict.groups.push(group.attributes);
+ });
+
+ // add chart to charts array
+ this.vis.get('config').chart_dict = chart_dict;
+
+ // save visualization
+ var self = this;
+ this.vis.save()
+ .fail(function(xhr, status, message) {
+ console.error(xhr, status, message);
+ })
+ .then(function(response) {
+ if (response && response.id) {
+ self.id = response.id;
+ }
+ });
+ },
+
+ // load nested models/collections from packed dictionary
+ load: function() {
+ // get charts array
+ var chart_dict = this.vis.get('config').chart_dict;
+
+ // check
+ if (!chart_dict.attributes) {
+ return false;
+ }
+
+ // main
+ this.chart.set(chart_dict.attributes);
+
+ // set state
+ this.chart.state('ok', 'Loading saved visualization...');
+
+ // get settings
+ this.chart.settings.set(chart_dict.settings);
+
+ // get groups
+ for (var j in chart_dict.groups) {
+ this.chart.groups.add(new Group(chart_dict.groups[j]));
+ }
+
+ // reset modified flag
+ this.chart.set('modified', false);
+
+ // return
+ return true;
+ }
+});
+
+});
\ No newline at end of file
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/19e8344a0497/
Changeset: 19e8344a0497
User: dannon
Date: 2014-04-21 23:10:23
Summary: Convert cloudlaunch over to bioblend egg/launching, unifying the two approaches. The bioblend codebase will be where improvements (vpc, etc) happen moving forward.
Affected #: 3 files
diff -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 eggs.ini
--- a/eggs.ini
+++ b/eggs.ini
@@ -35,6 +35,7 @@
amqp = 1.4.3
anyjson = 0.3.3
Beaker = 1.4
+bioblend = 0.4.2
boto = 2.27.0
decorator = 3.1.2
docutils = 0.7
diff -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 lib/galaxy/webapps/galaxy/controllers/cloudlaunch.py
--- a/lib/galaxy/webapps/galaxy/controllers/cloudlaunch.py
+++ b/lib/galaxy/webapps/galaxy/controllers/cloudlaunch.py
@@ -16,16 +16,16 @@
from galaxy.util.json import to_json_string
eggs.require('boto')
-import boto
-import boto.vpc
-from boto.ec2.regioninfo import RegionInfo
-from boto.exception import EC2ResponseError, S3ResponseError
-from boto.s3.connection import OrdinaryCallingFormat, S3Connection
+eggs.require('bioblend')
+eggs.require('pyyaml')
+
+from boto.exception import EC2ResponseError
+
+
+from bioblend import cloudman
log = logging.getLogger(__name__)
-logging.getLogger('boto').setLevel(logging.INFO)
-
PKEY_PREFIX = 'gxy_pkey'
DEFAULT_KEYPAIR = 'cloudman_keypair'
@@ -55,71 +55,29 @@
Get EC2 Account Info
"""
account_info = {}
- try:
- ec2_conn = connect_ec2(key_id, secret)
- kps = ec2_conn.get_all_key_pairs()
- except EC2ResponseError, e:
- log.error("Problem starting an instance: %s\n%s" % (e, e.body))
- trans.response.status = 400
- return e.error_message or "Error retrieving account information. Please verify your credentials are correct, and contact galaxy support to report this issue."
+ cml = cloudman.launch.CloudManLauncher(key_id, secret)
+ ec2_conn = cml.connect_ec2(key_id, secret)
+ kps = ec2_conn.get_all_key_pairs()
+ account_info['clusters'] = cml.get_clusters_pd()
account_info['keypairs'] = [akp.name for akp in kps]
- #Existing Clusters
- s3_conn = S3Connection(key_id, secret, calling_format=OrdinaryCallingFormat())
- buckets = s3_conn.get_all_buckets()
- clusters = []
- for bucket in buckets:
- try:
- pd = bucket.get_key('persistent_data.yaml')
- except S3ResponseError, e:
- # This can fail for a number of reasons for non-us and/or CNAME'd buckets.
- log.error("Problem fetching persistent_data.yaml from bucket: %s \n%s" % (e, e.body))
- continue
- if pd:
- # This is a cloudman bucket.
- # We need to get persistent data, and the cluster name.
- # DBTODO: Add pyyaml egg, and rewrite this.
- # This will also allow for much more sophisticated rendering of existing clusters
- # Currently, this zone detection is a hack.
- pd_contents = pd.get_contents_as_string()
- zone = get_zone_from_pd(pd_contents, ec2_conn)
- for key in bucket.list():
- if key.name.endswith('.clusterName'):
- clusters.append({'name': key.name.split('.clusterName')[0],
- 'persistent_data': pd_contents,
- 'zone': zone})
- account_info['clusters'] = clusters
- account_info['zones'] = [z.name for z in ec2_conn.get_all_zones()]
return to_json_string(account_info)
@web.expose
- def launch_instance(self, trans, cluster_name, password, key_id, secret, instance_type, share_string, keypair, ami=None, zone=None, bucket_default=None, **kwargs):
- ec2_error = None
- try:
- # Create security group & key pair used when starting an instance
- ec2_conn = connect_ec2(key_id, secret)
- sg = get_or_create_cm_security_group(ec2_conn)
- kp_name, kp_material = create_key_pair(ec2_conn, key_name=keypair)
- except EC2ResponseError, err:
- ec2_error = err.error_message
- if ec2_error:
- trans.response.status = 400
- return ec2_error
- #Get an elastic ip, add to UserData.
- user_provided_data = _format_user_data(cluster_name, key_id, secret, password, share_string, bucket_default)
- if not ami:
- ami = trans.app.config.cloudlaunch_default_ami
- rs = _run_instance(ec2_conn=ec2_conn,
- image_id=ami,
- user_provided_data=user_provided_data,
- key_name=kp_name,
- security_group_ids=[sg.id],
- placement=zone,
- instance_type=instance_type,
- subnet_id=get_or_create_cm_subnet(key_id, secret, zone).id)
- if not rs:
+ def launch_instance(self, trans, cluster_name, password, key_id, secret,
+ instance_type, share_string, keypair, ami=None,
+ zone=None, bucket_default=None, **kwargs):
+ ami = ami or trans.app.config.cloudlaunch_default_ami
+ cfg = cloudman.CloudManConfig(key_id, secret, cluster_name, ami,
+ instance_type, password, placement=zone)
+ cml = cloudman.launch.CloudManLauncher(key_id, secret)
+ result = cml.launch(cluster_name, ami, instance_type, password,
+ cfg.kernel_id, cfg.ramdisk_id, cfg.key_name,
+ cfg.security_groups, cfg.placement)
+ #result is a dict with sg_names, kp_name, kp_material, rs, and instance_id
+ if not result['rs']:
trans.response.status = 400
return "Instance failure, but no specific error was detected. Please check your AWS Console."
- instance = rs.instances[0]
+ instance = result['rs'].instances[0]
while not instance.public_dns_name:
try:
instance.update()
@@ -127,20 +85,20 @@
# This can happen when update is invoked before the instance is fully registered.
pass
time.sleep(1)
- if kp_material:
+ if result['kp_material']:
#We have created a keypair. Save to tempfile for one time retrieval.
(fd, fname) = tempfile.mkstemp(prefix=PKEY_PREFIX, dir=trans.app.config.new_file_path)
f = os.fdopen(fd, 'wt')
- f.write(kp_material)
+ f.write(result['kp_material'])
f.close()
kp_material_tag = fname[fname.rfind(PKEY_PREFIX) + len(PKEY_PREFIX):]
else:
kp_material_tag = None
return to_json_string({'cluster_name': cluster_name,
- 'instance_id': rs.instances[0].id,
- 'image_id': rs.instances[0].image_id,
- 'public_dns_name': rs.instances[0].public_dns_name,
- 'kp_name': kp_name,
+ 'instance_id': result['rs'].instances[0].id,
+ 'image_id': result['rs'].instances[0].image_id,
+ 'public_dns_name': result['rs'].instances[0].public_dns_name,
+ 'kp_name': result['kp_name'],
'kp_material_tag': kp_material_tag})
@web.expose
@@ -158,188 +116,3 @@
return kp_material
trans.response.status = 400
return "Invalid identifier"
-
-
-def get_zone_from_pd(pd_contents, ec2_conn):
- try:
- for line in pd_contents.split('\n'):
- if 'vol_id' in line:
- vol_id = line.split(':')[1].strip()
- v = ec2_conn.get_all_volumes(volume_ids=[vol_id])
- if v:
- return v[0].zone
- except:
- # Things can go wrong with Zone detection outside of our control. Use
- # the default zone if this happens.
- pass
- return ''
-
-
-def connect_ec2(a_key, s_key):
- """
- Create and return an EC2 connection object.
- """
- # Use variables for forward looking flexibility
- # AWS connection values
- region_name = 'us-east-1'
- region_endpoint = 'ec2.amazonaws.com'
- is_secure = True
- ec2_port = None
- ec2_conn_path = '/'
- r = RegionInfo(name=region_name, endpoint=region_endpoint)
- ec2_conn = boto.connect_ec2(aws_access_key_id=a_key,
- aws_secret_access_key=s_key,
- is_secure=is_secure,
- region=r,
- port=ec2_port,
- path=ec2_conn_path)
- return ec2_conn
-
-
-def get_or_create_cm_security_group(ec2_conn, sg_name='CloudManVPC'):
- """
- Create a security group with all authorizations required to run CloudMan.
- If the group already exists, check its rules and add the missing ones.
- Return the name of the created security group.
- """
- cm_sg = None
- # Check if this security group already exists. We can't use the built-in
- # filtering here, because VPC security groups can't be filtered by name.
- sg = ec2_conn.get_all_security_groups(filters={'group_name': sg_name})
- if sg:
- cm_sg = sg[0]
- else:
- # If it does not exist, create security group
- log.debug("Creating Security Group %s" % sg_name)
- cm_sg = ec2_conn.create_security_group(sg_name, 'A security group for CloudMan', vpc_id=get_or_create_cm_vpc(ec2_conn.access_key, ec2_conn.secret_key).id)
- # Add appropriate authorization rules
- # If these rules already exist, nothing will be changed in the SG
- ports = (('80', '80'), # Web UI
- ('20', '21'), # FTP
- ('22', '22'), # ssh
- ('30000', '30100'), # FTP transfer
- ('42284', '42284')) # CloudMan UI
- for port in ports:
- if not rule_exists(cm_sg.rules, from_port=port[0], to_port=port[1]):
- cm_sg.authorize(ip_protocol='tcp', from_port=port[0], to_port=port[1], cidr_ip='0.0.0.0/0')
- else:
- log.debug("Rule (%s:%s) already exists in the SG" % (port[0], port[1]))
- # Add rule that allows communication between instances in the same SG
- g_rule_exists = False # Flag to indicate if group rule already exists
- for rule in cm_sg.rules:
- for grant in rule.grants:#[g for g in rule.grants if g.name == cm_sg.name]:
- print grant.name
- if grant.name == cm_sg.name:
- g_rule_exists = True
- log.debug("Group rule already exists in the SG")
- if g_rule_exists:
- break
- #if g_rule_exists is False:
- # cm_sg.authorize(src_group=cm_sg)
- log.info("Done configuring '%s' security group" % cm_sg.name)
- return cm_sg
-
-
-
-def get_or_create_cm_vpc(a_key, s_key):
- vpc_conn = boto.vpc.VPCConnection(a_key, s_key)
- vpcs = vpc_conn.get_all_vpcs()
- cloudman_vpc = None
- for vpc in vpcs:
- if CLOUDMAN_TAG_KEY in vpc.tags:
- cloudman_vpc = vpc
- if not cloudman_vpc:
- #create a new one.
- cloudman_vpc = vpc_conn.create_vpc('10.0.0.0/16')
- cloudman_vpc.add_tag(CLOUDMAN_TAG_KEY, '')
- return cloudman_vpc
-
-
-def get_or_create_cm_subnet(a_key, s_key, availability_zone):
- log.debug("GETTING SUBNET in %s" % availability_zone)
- vpc_conn = boto.vpc.VPCConnection(a_key, s_key)
- subnets = vpc_conn.get_all_subnets()
- cloudman_subnet = None
- for subnet in subnets:
- log.debug("SN: %s, SNAZ:AZ: %s:%s:%s" % (subnet, subnet.availability_zone, availability_zone, subnet.availability_zone == availability_zone))
- if (not availability_zone or subnet.availability_zone == availability_zone) and CLOUDMAN_TAG_KEY in subnet.tags:
- cloudman_subnet = subnet
- if not cloudman_subnet:
- #create a new one.
- cloudman_subnet = vpc_conn.create_subnet(get_or_create_cm_vpc(a_key, s_key).id, '10.0.0.0/24', availability_zone)
- cloudman_subnet.add_tag(CLOUDMAN_TAG_KEY, '')
- return cloudman_subnet
-
-
-def _format_user_data(cluster_name, key_id, secret, password, share_string, bucket_default):
- user_provided_data = {'cluster_name': cluster_name,
- 'access_key': key_id,
- 'secret_key': secret}
- if password:
- user_provided_data['password'] = password
- if share_string:
- user_provided_data['share_string'] = share_string
- if bucket_default:
- user_provided_data['bucket_default'] = bucket_default
- return "\n".join(['%s: %s' % (key, value) for key, value in user_provided_data.iteritems() if key != 'kp_material'])
-
-
-def rule_exists(rules, from_port, to_port, ip_protocol='tcp', cidr_ip='0.0.0.0/0'):
- """ A convenience method to check if an authorization rule in a security
- group exists.
- """
- for rule in rules:
- if rule.ip_protocol == ip_protocol and rule.from_port == from_port and \
- rule.to_port == to_port and cidr_ip in [ip.cidr_ip for ip in rule.grants]:
- return True
- return False
-
-
-def create_key_pair(ec2_conn, key_name=DEFAULT_KEYPAIR):
- """ Create a key pair with the provided name.
- Return the name of the key or None if there was an error creating the key.
- """
- kp = None
- # Check if a key pair under the given name already exists. If it does not,
- # create it, else return.
- kps = ec2_conn.get_all_key_pairs()
- for akp in kps:
- if akp.name == key_name:
- log.debug("Key pair '%s' already exists; not creating it again." % key_name)
- return akp.name, None
- try:
- kp = ec2_conn.create_key_pair(key_name)
- except EC2ResponseError, e:
- log.error("Problem creating key pair '%s': %s" % (key_name, e))
- return None, None
- return kp.name, kp.material
-
-
-def _run_instance(ec2_conn, user_provided_data, instance_type, image_id=None,
- kernel_id=None, ramdisk_id=None, key_name=DEFAULT_KEYPAIR,
- placement=None, security_group_ids=None, subnet_id=None):
- """
- Start an instance. If instance start was OK, return the ResultSet object
- else return None.
- """
- rs = None
- try:
- rs = ec2_conn.run_instances(image_id=image_id,
- instance_type=instance_type,
- key_name=key_name,
- security_group_ids=security_group_ids,
- user_data=user_provided_data,
- kernel_id=kernel_id,
- subnet_id=subnet_id,
- ramdisk_id=ramdisk_id,
- placement=placement)
- except EC2ResponseError, e:
- log.error("Problem starting an instance: %s\n%s" % (e, e.body))
- if rs:
- try:
- log.info("Started an instance with ID %s" % rs.instances[0].id)
- except Exception, e:
- log.error("Problem with the started instance object: %s" % e)
- else:
- log.warning("Problem starting an instance?")
- return rs
diff -r 347a9169e3ec43a5719a4ca57d7de04ea50cd0e7 -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 templates/webapps/galaxy/cloud/index.mako
--- a/templates/webapps/galaxy/cloud/index.mako
+++ b/templates/webapps/galaxy/cloud/index.mako
@@ -65,7 +65,7 @@
clusterlist.append($('<option/>').val('New Cluster').text('New Cluster'));
if (_.size(result.clusters) > 0){
_.each(result.clusters, function(cluster, index){
- clusterlist.append($('<option/>').val(cluster.name).text(cluster.name));
+ clusterlist.append($('<option/>').val(cluster.cluster_name).text(cluster.cluster_name));
});
$('#existing_instance_wrapper').show();
}
@@ -95,13 +95,13 @@
type: 'POST',
dataType: 'json',
beforeSubmit: function(data, form){
- // Dig up zone info for selected cluster, set hidden input.
+ // Dig up placement info for selected cluster, set hidden input.
// This is not necessary to present to the user though the interface may prove useful.
var ei_val = _.find(data, function(f_obj){return f_obj.name === 'existing_instance'});
if( ei_val && (ei_val.value !== "New Cluster")){
- var cluster = _.find(cloudlaunch_clusters, function(cluster){return cluster.name === ei_val.value});
- var zdata = _.find(data, function(f_obj){return f_obj.name === 'zone'});
- zdata.value = cluster.zone;
+ var cluster = _.find(cloudlaunch_clusters, function(cluster){return cluster.cluster_name === ei_val.value});
+ var placement_field = _.find(data, function(f_obj){return f_obj.name === 'placement'});
+ placement_field.value = cluster.placement;
}else if($('#id_cluster_name').val() === ''){
// If we're not using an existing cluster, this must be set.
form.prepend('<div class="errormessage">You must specify a cluster name</div>');
@@ -188,7 +188,7 @@
<label for="id_existing_instance">Instances in your account</label><select name="existing_instance" id="id_existing_instance" style="min-width: 228px"></select>
- <input id='id_zone' type='hidden' name='zone' value=''/>
+ <input id='id_placement' type='hidden' name='placement' value=''/></div>
%endif
<div id='cluster_name_wrapper' class="form-row">
https://bitbucket.org/galaxy/galaxy-central/commits/557c0f2c6129/
Changeset: 557c0f2c6129
User: dannon
Date: 2014-04-21 23:10:48
Summary: Merge.
Affected #: 372 files
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 .hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -6,3 +6,4 @@
1ae95b3aa98d1ccf15b243ac3ce6a895eb7efc53 release_2013.08.12
26f58e05aa1068761660681583821e21e6cbf7ab release_2013.11.04
5e605ed6069fe4c5ca9875e95e91b2713499e8ca release_2014.02.10
+9e53251b0b7e93b9563008a2b112f2e815a04bbc release_2014.04.14
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/app.js
--- a/config/plugins/visualizations/charts/static/app.js
+++ b/config/plugins/visualizations/charts/static/app.js
@@ -51,7 +51,7 @@
// append views
this.$el.append(this.viewer_view.$el);
this.$el.append(this.editor_view.$el);
-
+
// pick start screen
if (!this.storage.load()) {
// show editor
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 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.
-(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-modal",["utils/utils"],function(e){var t=Backbone.View.extend({elMain:"body",optionsDefault:{title:"ui-modal",body:"",backdrop:!0,height:null,width:null,closing_events:!1},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")},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:t}}),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.setElement(this._template(this.options)),this.$select=this.$el.find("#select"),this.$icon=this.$el.find("#icon"),this.selected=this.options.value;var n=this;this.options.onchange&&this.$select.on("change",function(){n.value(n.$select.val())}),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},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.selected&&e.length>0&&this.value(e[0].value),this._refresh()},_refresh:function(){this.$select.find("option[value=null]").remove();var e=this.$select.find("option").length;e==0?(this.$select.append(this._templateOption({value:"null",label:this.options.empty})),this.disable()):(this.enable(),this.selected&&this.$select.val(this.selected))},_exists:function(e){return 0!=this.$select.find("option[value="+e+"]").length},_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:{title:""},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><b>"+e.title+"</b></label>"},value:function(){return options.title}}),r=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}}),i=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>"}}),s=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}}),o=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>"}}),u=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>"}}),a=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>"}}),f=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>"}}),l=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>'),"</button></div>",t}}),c=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.options.onchange&&this.$el.on("input",function(){n.options.onchange()})},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:n,Button:r,Icon:i,ButtonIcon:s,Input:c,Anchor:o,Message:u,Searchbox:a,Title:f,Select:t,ButtonMenu:l}}),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)},cleanup:function(t){var n=t.get("dataset_id_job");if(n!=""){var r=this;e.request("PUT",config.root+"api/histories/none/contents/"+n,{deleted:!0},function(){r._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:"chartskit",inputs:{input:{id:t.get("dataset_id"),src:"hda"},module:a,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","Job has been queued..."),t.set("dataset_id_job",n.id),this.app.storage.save(),o._loop(n.id,function(e){switch(e.state){case"ok":return t.state("wait","Job completed successfully..."),i(e),!0;case"error":return t.state("failed","Job has failed. Please check the history for details."),s&&s(),!0;case"running":return t.state("wait","Job is running..."),!1}})}},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-Kit. Please make sure it is installed. "+n),s&&s()})},_loop:function(t,n){var r=this;e.request("GET",config.root+"api/jobs/"+t,{},function(e){n(e)||setTimeout(function(){r._loop(t,n)},r.app.config.get("query_timeout"))})},_refreshHdas:function(){Galaxy&&Galaxy.currHistoryPanel&&Galaxy.currHistoryPanel.refreshHdas()}})}),define("plugin/library/datasets",["utils/utils"],function(e){return Backbone.Collection.extend({list:{},initialize:function(t,n){this.app=t,this.options=e.merge(n,this.optionsDefault)},request:function(t,n,r){var i=this;if(t.groups)this._fetch(t,n);else{var s=this.list[t.id];if(s){n(s);return}e.request("GET",config.root+"api/datasets/"+t.id,{},function(e){switch(e.state){case"error":r&&r(e);break;default:i.list[t.id]=e,n(e)}})}},_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="",u={},a=0;for(var f in t.groups){var l=t.groups[f];for(var c in l.columns){var h=l.columns[c];o+=h+",",u[h]=a,a++}}if(a==0){n({});return}o=o.substring(0,o.length-1);var p=t.groups.slice(0);for(var f in p)p[f].values=[];var d=this;e.request("GET",config.root+"api/datasets/"+t.id,{data_type:"raw_data",provider:"dataset-column",limit:i,offset:r,indeces:o},function(e){for(var i in e.data){var s=e.data[i];for(var o in t.groups){var a=t.groups[o],f={x:parseInt(i)+r};for(var l in a.columns){var c=a.columns[l],h=u[c],d=s[h];if(isNaN(d)||!d)d=0;f[l]=d}p[o].values.push(f)}}n(p)})}})}),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)this.queue[e](),this.queue.splice(e,1)})},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){delete this.process[e],this.counter--,console.debug("Deferred:done() - Unregistering "+e),this.trigger("refresh")},ready:function(){return this.counter==0?!0:!1}})});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/views/viewport",["mvc/ui/ui-portlet","plugin/library/ui","utils/utils"],function(e,t,n){return Backbone.View.extend({initialize:function(e,t){this.app=e,this.chart=this.app.chart,this.options=n.merge(t,this.optionsDefault),this.setElement($(this._template())),this._create_svg();var r=this;this.chart.on("redraw",function(){r._draw(r.chart)}),this.chart.on("set:state",function(){var e=r.$el.find("#info"),t=e.find("#icon");t.removeClass(),e.show(),e.find("#text").html(r.chart.get("state_info"));var n=r.chart.get("state");switch(n){case"ok":e.hide();break;case"failed":t.addClass("fa fa-warning");break;default:t.addClass("fa fa-spinner fa-spin")}})},show:function(){this.$el.show()},hide:function(){this.$el.hide()},_create_svg:function(){this.svg&&this.svg.remove(),this.$el.append($(this._template_svg())),this.svg_el=this.$el.find("svg"),this.svg=d3.select(this.svg_el[0])},_draw:function(e){var t=this;this._create_svg(),e.state("wait","Please wait...");var n=e.deferred.register(),r=e.get("type"),i=this.app.types.get(r);if(!i.execute||i.execute&&e.get("modified"))this.app.jobs.cleanup(e),e.set("modified",!1);var t=this;require(["plugin/charts/"+r+"/"+r],function(r){var s=new r(t.app,{svg:t.svg});i.execute?e.get("dataset_id_job")==""?t.app.jobs.submit(e,t._defaultSettingsString(e),t._defaultRequestString(e),function(){s.draw(n,e,t._defaultRequestDictionary(e))},function(){e.deferred.done(n)}):s.draw(n,e,t._defaultRequestDictionary(e)):s.draw(n,e,t._defaultRequestDictionary(e))})},_defaultRequestString:function(e){var t=this.app.types.get(e.get("type")),n="",r=0;return e.groups.each(function(e){for(var i in t.columns)n+=i+"_"+ ++r+":"+(parseInt(e.get(i))+1)+", "}),n.substring(0,n.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=this.app.types.get(e.get("type")),n={groups:[]};t.execute?n.id=e.get("dataset_id_job"):n.id=e.get("dataset_id");var r=0;return e.groups.each(function(e){var i={};for(var s in t.columns)i[s]=e.get(s);n.groups.push({key:++r+":"+e.get("key"),columns:i})}),n},_template:function(){return'<div style="height: 100%; min-height: 50px;"><div id="info" style="position: absolute; margin-left: 10px; margin-top: 10px; margin-bottom: 50px;"><span id="icon" style="font-size: 1.2em; display: inline-block;"/><span id="text" style="position: relative; margin-left: 5px; top: -1px; font-size: 1.0em;"/></div></div>'},_template_svg:function(){return'<svg style="height: calc(100% - 80px)"/>'}})}),define("plugin/views/viewer",["utils/utils","plugin/library/ui","mvc/ui/ui-portlet","plugin/models/group","plugin/views/viewport"],function(e,t,n,r,i){return Backbone.View.extend({initialize:function(e,r){this.app=e,this.chart=this.app.chart,this.viewport_view=new i(e);var s=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:"Customize",onclick:function(){s._wait(s.chart,function(){s.app.go("editor")})}})}}),this.portlet.append(this.viewport_view.$el),this.setElement(this.portlet.$el);var s=this;this.chart.on("change:title",function(){s._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)},_screenshot:function(){var e=new XMLSerializer,t=e.serializeToString(this.viewport_view.svg.node()),n="data:image/svg+xml;base64,"+btoa(t);window.location.href="data:application/x-download/;charset=utf-8,"+encodeURIComponent(t)},_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},events:{click:"_onclick",dblclick:"_ondblclick"},first:!0,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){var n=$("<td></td>");t&&n.css("width",t),n.append(e),this.row.append(n)},append:function(e){this._commit(e)},prepend:function(e){this._commit(e,!0)},remove:function(e){var t=this.$tbody.find("#"+e);t.length>0&&(t.remove(),this.row_count--,this._refresh())},removeAll: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.remove(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&&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="grid"><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({columns:[],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(){s.group.set("key",s.group_key.value())}}),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.removeAll();var s={};for(var o in i.columns){var u=this.group.get(o);u||this.group.set(o,0);var a=i.columns[o],f=new t.Select.View({id:"select_"+o,gid:o,onchange:function(e){r.group.set(this.gid,e),r.chart.set("modified",!0)},value:u,wait:!0});this.table.add(a.title,"25%"),this.table.add(f.$el),this.table.append(o),s[o]=f}this.chart.state("wait","Loading metadata...");var l=this.chart.deferred.register();this.app.datasets.request({id:e},function(e){r.columns=[];var t=e.metadata_column_types;for(var n in t)(t[n]=="int"||t[n]=="float")&&r.columns.push({label:"Column: "+(parseInt(n)+1)+" ["+t[n]+"]",value:n});for(var n in s)s[n].update(r.columns),s[n].show();r.chart.state("wait","Metadata initialized..."),r.chart.deferred.done(l)})},_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({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.removeAll(),this.list=[];for(var n in e)this._add(n,e[n],t);for(var n in this.list){var r=this.list[n].options.onchange;r&&r()}},_add:function(e,n,r){var i=this,s=null,o=n.type;switch(o){case"text":s=new t.Input({id:e,placeholder:n.placeholder,onchange:function(){r.set(e,s.value())}});break;case"select":s=new t.Select.View({id:e,data:n.data,onchange:function(){var t=s.value();r.set(e,t);var o=_.findWhere(n.data,{value:t});o&&(o.show&&i.$el.find("#"+o.show).fadeIn("fast"),o.hide&&i.$el.find("#"+o.hide).fadeOut("fast"))}});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.title+":"),this.form.update(t.settings,this.chart.settings)}})}),define("plugin/views/editor",["mvc/ui/ui-tabs","plugin/library/ui-table","plugin/library/ui","mvc/ui/ui-portlet","utils/utils","plugin/models/chart","plugin/models/group","plugin/views/group","plugin/views/settings"],function(e,t,n,r,i,s,o,u,a){return Backbone.View.extend({optionsDefault:{header:!0,content:"No content available."},initialize:function(s,o){var u=this;this.app=s,this.chart=this.app.chart,this.options=i.merge(o,this.optionsDefault),this.portlet=new r.View({icon:"fa-bar-chart-o",title:"Editor",operations:{save:new n.ButtonIcon({icon:"fa-save",tooltip:"Draw Chart",title:"Draw",onclick:function(){u.app.go("viewer"),u._saveChart()}}),back:new n.ButtonIcon({icon:"fa-caret-left",tooltip:"Return to Viewer",title:"Return",onclick:function(){u.app.go("viewer"),u.app.storage.load()}})}}),this.table=new t.View({header:!1,onconfirm:function(e){u.chart.groups.length>0?u.app.modal.show({title:"Switching to another chart type?",body:"If you continue your settings and selections will be cleared.",buttons:{Cancel:function(){u.app.modal.hide()},Continue:function(){u.app.modal.hide(),u.table.value(e)}}}):u.table.value(e)},onchange:function(e){u.chart.groups.reset(),u.chart.settings.clear(),u.chart.set({type:e})},ondblclick:function(e){u.tabs.show("settings")},content:"No chart types available"});var f=0,l=s.types.attributes;for(var c in l){var h=l[c];this.table.add(++f+"."),h.execute?this.table.add(h.title+" (requires processing)"):this.table.add(h.title),this.table.append(c)}this.tabs=new e.View({title_new:"Add Data",onnew:function(){var e=u._addGroupModel();u.tabs.show(e.id)}}),this.title=new n.Input({placeholder:"Chart title",onchange:function(){u.chart.set("title",u.title.value())}});var p=$("<div/>");p.append(i.wrap((new n.Label({title:"Provide a chart title:"})).$el)),p.append(i.wrap(this.title.$el)),p.append(i.wrap((new n.Label({title:"Select a chart type:"})).$el)),p.append(i.wrap(this.table.$el)),this.tabs.add({id:"main",title:"Start",$el:p}),this.settings=new a(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 u=this;this.chart.on("change:title",function(e){u._refreshTitle()}),this.chart.on("change:type",function(e){u.table.value(e.get("type"))}),this.chart.on("reset",function(e){u._resetChart()}),this.app.chart.on("redraw",function(e){u.portlet.showOperation("back")}),this.app.chart.groups.on("add",function(e){u._addGroup(e)}),this.app.chart.groups.on("remove",function(e){u._removeGroup(e)}),this.app.chart.groups.on("reset",function(e){u._removeAllGroups()}),this.app.chart.groups.on("change:key",function(e){u._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 o({id:i.uuid()});return this.chart.groups.add(e),e},_addGroup:function(e){var t=this,n=new u(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",i.uuid()),this.chart.set("type","bardiagram"),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.table.value(),title:this.title.value(),date:i.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:1e3,query_timeout:500}})}),define("plugin/charts/_nvd3/config",[],function(){return{title:"",columns:{y:{title:"Values for y-axis"}},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/bardiagram/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Bar diagram"})}),define("plugin/charts/histogram/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Histogram",execute:!0,columns:{y:{title:"Observations"}},settings:{x_axis_label:{init:"Breaks"},y_axis_label:{init:"Density"}}})}),define("plugin/charts/horizontal/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Bar diagram (horizontal)",settings:{x_axis_type:{init:"hide"}}})}),define("plugin/charts/line/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line chart"})}),define("plugin/charts/linewithfocus/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line with focus"})}),define("plugin/charts/piechart/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Pie chart"})}),define("plugin/charts/scatterplot/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Scatter plot",columns:{x:{title:"Values for x-axis"}}})}),define("plugin/charts/stackedarea/config",["plugin/charts/_nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Stacked area"})}),define("plugin/charts/types",["plugin/charts/bardiagram/config","plugin/charts/histogram/config","plugin/charts/horizontal/config","plugin/charts/line/config","plugin/charts/linewithfocus/config","plugin/charts/piechart/config","plugin/charts/scatterplot/config","plugin/charts/stackedarea/config"],function(e,t,n,r,i,s,o,u){return Backbone.Model.extend({defaults:{bardiagram:e,horizontal:n,histogram:t,line:r,linewithfocus:i,piechart:s,scatterplot:o,stackedarea:u}})}),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
+(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-modal",["utils/utils"],function(e){var t=Backbone.View.extend({elMain:"body",optionsDefault:{title:"ui-modal",body:"",backdrop:!0,height:null,width:null,closing_events:!1},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")},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:t}}),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.setElement(this._template(this.options)),this.$select=this.$el.find("#select"),this.$icon=this.$el.find("#icon"),this.selected=this.options.value;var n=this;this.options.onchange&&this.$select.on("change",function(){n.value(n.$select.val())}),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},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.selected&&e.length>0&&this.value(e[0].value),this._refresh()},_refresh:function(){this.$select.find("option[value=null]").remove();var e=this.$select.find("option").length;e==0?(this.$select.append(this._templateOption({value:"null",label:this.options.empty})),this.disable()):(this.enable(),this.selected&&this.$select.val(this.selected))},_exists:function(e){return 0!=this.$select.find("option[value="+e+"]").length},_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:{title:""},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><b>"+e.title+"</b></label>"},value:function(){return options.title}}),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.html(e)},_template:function(e){return'<div class="'+e.cls+'">'+e.title+"</div>"},value:function(){return options.title}}),i=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}}),s=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>"}}),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:{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>"}}),a=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>"}}),f=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>"}}),l=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>"}}),c=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>'),"</button></div>",t}}),h=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.options.onchange&&this.$el.on("input",function(){n.options.onchange()})},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:n,Button:i,Icon:s,ButtonIcon:o,Input:h,Anchor:u,Message:a,Searchbox:f,Title:l,Text:r,Select:t,ButtonMenu:c}}),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)},cleanup:function(t){var n=t.get("dataset_id_job");if(n!=""){var r=this;e.request("PUT",config.root+"api/histories/none/contents/"+n,{deleted:!0},function(){r._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","Job has been queued..."),t.set("dataset_id_job",n.id),this.app.storage.save(),o._loop(n.id,function(e){switch(e.state){case"ok":return t.state("wait","Job completed successfully..."),i(e),!0;case"error":return t.state("failed","Job has failed. Please check the history for details."),s&&s(),!0;case"running":return t.state("wait","Job is running..."),!1}})}},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()})},_loop:function(t,n){var r=this;e.request("GET",config.root+"api/datasets/"+t,{},function(e){n(e)||setTimeout(function(){r._loop(t,n)},r.app.config.get("query_timeout"))})},_refreshHdas:function(){Galaxy&&Galaxy.currHistoryPanel&&Galaxy.currHistoryPanel.refreshHdas()}})}),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(t,n,r){var i=this;if(t.groups)this._get(t,n);else{var s=this.list[t.id];if(s){n(s);return}e.request("GET",config.root+"api/datasets/"+t.id,{},function(e){switch(e.state){case"error":r&&r(e);break;default:i.list[t.id]=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])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.");for(var t in e.groups){var n=e.groups[t];n.values=[];for(var r in n.columns){var i=n.columns[r],s=this._block_id(e,i.index),o=this.cache[s];for(k in o){var u=n.values[k];u===undefined&&(u={x:parseInt(k)+e.start},n.values[k]=u);var a=o[k];isNaN(a)&&!i.is_label&&(a=0),u[r]=a}}}},_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)this.queue[e](),this.queue.splice(e,1)})},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?!0:!1}})});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/views/viewport",["mvc/ui/ui-portlet","plugin/library/ui","utils/utils"],function(e,t,n){return Backbone.View.extend({canvas:[],initialize:function(e,t){this.app=e,this.chart=this.app.chart,this.options=n.merge(t,this.optionsDefault),this.setElement($(this._template())),this._fullscreen(this.$el,80),this._create_canvas("div");var r=this;this.chart.on("redraw",function(){r._draw(r.chart)}),this.chart.on("set:state",function(){var e=r.$el.find("#info"),t=e.find("#icon");t.removeClass(),e.show(),e.find("#text").html(r.chart.get("state_info"));var n=r.chart.get("state");switch(n){case"ok":e.hide();break;case"failed":t.addClass("fa fa-warning");break;default:t.addClass("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)})},_create_canvas:function(e,t){t=t||1;for(var n in this.canvas)this.canvas[n].remove(),this.canvas.slice(n,0);for(var n=0;n<t;n++){var r=$(this._template_canvas(e,parseInt(100/t)));this.$el.append(r),e=="svg"?this.canvas[n]=d3.select(r[0]):this.canvas[n]=r}},_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._create_canvas(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+"/"+r],function(r){var i=new r(t.app,{canvas:t.canvas});t.chart_settings.execute?e.get("dataset_id_job")==""?t.app.jobs.submit(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)):i.draw(n,e,t._defaultRequestDictionary(e))})},_defaultRequestString:function(e){var t="",n=0,r=this;return e.groups.each(function(e){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 style="height: inherit; min-height: 50px;"><div id="info" style="position: absolute; margin-left: 10px; margin-top: 10px; margin-bottom: 50px;"><span id="icon" style="font-size: 1.2em; display: inline-block;"/><span id="text" style="position: relative; margin-left: 5px; top: -1px; font-size: 1.0em;"/></div></div>'},_template_canvas:function(e,t){return"<"+e+' class="canvas" style="float: left; display: block; width:'+t+'%; height: 100%;"/>'}})}),define("plugin/views/viewer",["utils/utils","plugin/library/ui","mvc/ui/ui-portlet","plugin/models/group","plugin/views/viewport"],function(e,t,n,r,i){return Backbone.View.extend({initialize:function(e,r){this.app=e,this.chart=this.app.chart,this.viewport_view=new i(e);var s=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(){s._wait(s.chart,function(){s.app.go("editor")})}})}}),this.portlet.append(this.viewport_view.$el),this.setElement(this.portlet.$el);var s=this;this.chart.on("change:title",function(){s._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)},_screenshot:function(){var e=new XMLSerializer,t=e.serializeToString(this.viewport_view.svg.node()),n="data:image/svg+xml;base64,"+btoa(t);window.location.href="data:application/x-download/;charset=utf-8,"+encodeURIComponent(t)},_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},events:{click:"_onclick",dblclick:"_ondblclick"},first:!0,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)},remove:function(e){var t=this.$tbody.find("#"+e);t.length>0&&(t.remove(),this.row_count--,this._refresh())},removeAll: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.remove(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&&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="grid"><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(){s.group.set("key",s.group_key.value())}}),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.removeAll();var s={};for(var o in i.columns){var u=this.group.get(o);u||this.group.set(o,0);var a=i.columns[o],f=new t.Select.View({id:"select_"+o,gid:o,onchange:function(e){r.group.set(this.gid,e),r.chart.set("modified",!0)},value:u,wait:!0});this.table.add(a.title,"25%"),this.table.add(f.$el),this.table.append(o),s[o]=f}this.chart.state("wait","Loading metadata...");var l=this.chart.deferred.register();this.app.datasets.request({id:e},function(e){for(var t in s){var n=i.columns[t].is_label,o=[],u=e.metadata_column_types;for(var a in u)(!n&&(u[a]=="int"||u[a]=="float")||n)&&o.push({label:"Column: "+(parseInt(a)+1)+" ["+u[a]+"]",value:a});s[t].update(o),s[t].show()}r.chart.state("ok","Metadata initialized..."),r.chart.deferred.done(l)})},_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({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.removeAll(),this.list=[];for(var n in e)this._add(n,e[n],t);for(var n in this.list){var r=this.list[n].options.onchange;r&&r()}},_add:function(e,n,r){var i=this,s=null,o=n.type;switch(o){case"text":s=new t.Input({id:e,placeholder:n.placeholder,onchange:function(){r.set(e,s.value())}});break;case"select":s=new t.Select.View({id:e,data:n.data,onchange:function(){var t=s.value();r.set(e,t);var o=_.findWhere(n.data,{value:t});o&&(o.show&&i.$el.find("#"+o.show).fadeIn("fast"),o.hide&&i.$el.find("#"+o.hide).fadeOut("fast"))}});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.title+":"),this.form.update(t.settings,this.chart.settings)}})}),define("plugin/views/editor",["mvc/ui/ui-tabs","plugin/library/ui-table","plugin/library/ui","mvc/ui/ui-portlet","utils/utils","plugin/models/chart","plugin/models/group","plugin/views/group","plugin/views/settings"],function(e,t,n,r,i,s,o,u,a){return Backbone.View.extend({optionsDefault:{header:!0,content:"No content available."},initialize:function(s,o){var u=this;this.app=s,this.chart=this.app.chart,this.options=i.merge(o,this.optionsDefault),this.portlet=new r.View({icon:"fa-bar-chart-o",title:"Editor",operations:{save:new n.ButtonIcon({icon:"fa-save",tooltip:"Draw Chart",title:"Draw",onclick:function(){u.app.go("viewer"),u._saveChart()}}),back:new n.ButtonIcon({icon:"fa-caret-left",tooltip:"Return to Viewer",title:"Return",onclick:function(){u.app.go("viewer"),u.app.storage.load()}})}}),this.table=new t.View({header:!1,onchange:function(e){u.chart.settings.clear(),u.chart.set({type:e}),u.chart.set("modified",!0)},ondblclick:function(e){u.tabs.show("settings")},content:"No chart types available"}),this.table.addHeader("No."),this.table.addHeader("Type"),this.table.addHeader("Library"),this.table.addHeader("Processing*"),this.table.appendHeader();var f=0,l=s.types.attributes;for(var c in l){var h=l[c];this.table.add(++f+"."),this.table.add(h.title),this.table.add(h.library,"10%"),h.execute?this.table.add((new n.Icon({icon:"fa-check"})).$el,"10%","center"):this.table.add(""),this.table.append(c)}this.tabs=new e.View({title_new:"Add Data",onnew:function(){var e=u._addGroupModel();u.tabs.show(e.id)}}),this.title=new n.Input({placeholder:"Chart title",onchange:function(){u.chart.set("title",u.title.value())}});var p=$("<div/>");p.append(i.wrap((new n.Label({title:"Provide a chart title:"})).$el)),p.append(i.wrap(this.title.$el)),p.append(i.wrap((new n.Label({title:"Select a chart type:"})).$el)),p.append(i.wrap(this.table.$el)),p.append((new n.Text({title:"*Certain chart types pre-process data before rendering the visualization. The pre-processing is done using the chartskit available in the Toolshed.",cls:"toolParamHelp"})).$el),this.tabs.add({id:"main",title:"Start",$el:p}),this.settings=new a(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 u=this;this.chart.on("change:title",function(e){u._refreshTitle()}),this.chart.on("change:type",function(e){u.table.value(e.get("type"))}),this.chart.on("reset",function(e){u._resetChart()}),this.app.chart.on("redraw",function(e){u.portlet.showOperation("back")}),this.app.chart.groups.on("add",function(e){u._addGroup(e)}),this.app.chart.groups.on("remove",function(e){u._removeGroup(e)}),this.app.chart.groups.on("reset",function(e){u._removeAllGroups()}),this.app.chart.groups.on("change:key",function(e){u._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 o({id:i.uuid()});return this.chart.groups.add(e),e},_addGroup:function(e){var t=this,n=new u(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",i.uuid()),this.chart.set("type","nvd3_bardiagram"),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.table.value(),title:this.title.value(),date:i.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:1e3,query_timeout:500}})}),define("plugin/charts/nvd3/config",[],function(){return{title:"",library:"nvd3.js",tag:"svg",columns:{y:{title:"Values for y-axis"}},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_bardiagram/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Bar diagram"})}),define("plugin/charts/nvd3_histogram/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Histogram",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_horizontal/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Bar diagram (horizontal)",settings:{x_axis_type:{init:"hide"}}})}),define("plugin/charts/nvd3_line/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line chart"})}),define("plugin/charts/nvd3_linewithfocus/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Line with focus"})}),define("plugin/charts/nvd3_piechart/config",[],function(){return{title:"Pie chart",library:"nvd3.js",tag:"svg",use_panels:!0,columns:{label:{title:"Labels",is_label:!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_scatterplot/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Scatter plot",columns:{x:{title:"Values for x-axis"}}})}),define("plugin/charts/nvd3_stackedarea/config",["plugin/charts/nvd3/config"],function(e){return $.extend(!0,{},e,{title:"Stacked area"})}),define("plugin/charts/highcharts_boxplot/config",[],function(){return{title:"Box plot",library:"highcharts.js",element:"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/types",["plugin/charts/nvd3_bardiagram/config","plugin/charts/nvd3_histogram/config","plugin/charts/nvd3_horizontal/config","plugin/charts/nvd3_line/config","plugin/charts/nvd3_linewithfocus/config","plugin/charts/nvd3_piechart/config","plugin/charts/nvd3_scatterplot/config","plugin/charts/nvd3_stackedarea/config","plugin/charts/highcharts_boxplot/config"],function(e,t,n,r,i,s,o,u,a){return Backbone.Model.extend({defaults:{nvd3_bardiagram:e,nvd3_horizontal:n,nvd3_histogram:t,nvd3_line:r,nvd3_linewithfocus:i,nvd3_piechart:s,nvd3_scatterplot:o,nvd3_stackedarea:u,highcharts_boxplot:a}})}),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 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/_nvd3/config.js
--- a/config/plugins/visualizations/charts/static/charts/_nvd3/config.js
+++ /dev/null
@@ -1,218 +0,0 @@
-define([], function() {
-
-return {
- title : '',
- columns : {
- y : {
- title : 'Values for y-axis'
- }
- },
- 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'
- }
- ]
- },
- }
-};
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/_nvd3/nvd3.js
--- a/config/plugins/visualizations/charts/static/charts/_nvd3/nvd3.js
+++ /dev/null
@@ -1,80 +0,0 @@
-// dependencies
-define([], function() {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- },
-
- // render
- draw : function(process_id, nvd3_model, chart, request_dictionary, callback)
- {
- // request data
- var self = this;
- this.app.datasets.request(request_dictionary, function(data) {
- nv.addGraph(function() {
- // x axis
- self._axis(nvd3_model.xAxis, chart.settings.get('x_axis_type'), chart.settings.get('x_axis_tick'));
-
- // x axis label
- nvd3_model.xAxis.axisLabel(chart.settings.get('x_axis_label'));
-
- // y axis
- self._axis(nvd3_model.yAxis, chart.settings.get('y_axis_type'), chart.settings.get('y_axis_tick'));
-
- // y axis label
- nvd3_model.yAxis.axisLabel(chart.settings.get('y_axis_label'))
- .axisLabelDistance(30);
-
- // controls
- if (chart.groups.length == 1) {
- nvd3_model.options({ showControls: false });
- }
-
- // legend
- var legend_visible = true;
- if (chart.settings.get('show_legend') == 'false') {
- legend_visible = false;
- }
- nvd3_model.showLegend(legend_visible);
-
- // custom callback
- if (callback) {
- callback(nvd3_model);
- }
-
- // parse data to svg
- self.options.svg.datum(data)
- .call(nvd3_model);
-
- // refresh on window resize
- nv.utils.windowResize(nvd3_model.update);
-
- // set chart state
- chart.state('ok', 'Chart has been drawn.');
-
- // unregister process
- chart.deferred.done(process_id);
- });
- });
- },
-
- // make axis
- _axis: function(axis, type, tick) {
- switch (type) {
- case 'hide':
- axis.tickFormat(function() { return '' });
- break;
- case 'auto':
- break;
- default:
- axis.tickFormat(d3.format(tick + type));
- }
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/bardiagram/bardiagram.js
--- a/config/plugins/visualizations/charts/static/charts/bardiagram/bardiagram.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// dependencies
-define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- },
-
- // render
- draw : function(process_id, chart, request_dictionary)
- {
- var nvd3 = new NVD3(this.app, this.options);
- nvd3.draw(process_id, nv.models.multiBarChart(), chart, request_dictionary);
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/bardiagram/config.js
--- a/config/plugins/visualizations/charts/static/charts/bardiagram/config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
-
-return $.extend(true, {}, nvd3_config, {
- title : 'Bar diagram',
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/highcharts_boxplot/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/highcharts_boxplot/config.js
@@ -0,0 +1,39 @@
+define([], function() {
+
+return {
+ title : 'Box plot',
+ library : 'highcharts.js',
+ element : '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'
+ }
+ }
+};
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/highcharts_boxplot/highcharts_boxplot.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/highcharts_boxplot/highcharts_boxplot.js
@@ -0,0 +1,122 @@
+// dependencies
+define(['utils/utils'], function(Utils) {
+
+// widget
+return Backbone.View.extend(
+{
+ // highcharts configuration
+ hc_config : {
+ chart: {
+ type: 'boxplot'
+ },
+
+ title: {
+ text: ''
+ },
+
+ legend: {
+ enabled: false
+ },
+
+ credits: {
+ enabled: false
+ },
+
+ plotOptions: {
+ series: {
+ animation: false
+ }
+ },
+
+ xAxis: {
+ categories: [],
+ title: {
+ text: ''
+ }
+ },
+
+ yAxis: {
+ title: {
+ text: ''
+ }
+ },
+
+ series: [{
+ name: 'Details:',
+ data: [],
+ tooltip: {
+ headerFormat: '<em>{point.key}</em><br/>'
+ }
+ }]
+ },
+
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ // configure request
+ var index = 0;
+ for (var i in request_dictionary.groups) {
+ var group = request_dictionary.groups[i];
+ group.columns = null;
+ group.columns = {
+ x: {
+ index: index++
+ }
+ }
+ }
+
+ // set axis labels
+ this.hc_config.xAxis.title.text = chart.settings.get('x_axis_label');
+ this.hc_config.yAxis.title.text = chart.settings.get('y_axis_label');
+
+ // request data
+ var self = this;
+ this.app.datasets.request(request_dictionary, function() {
+
+ // reset data/categories
+ var data = [];
+ var categories = [];
+
+ // loop through data groups
+ for (var key in request_dictionary.groups) {
+ // get group
+ var group = request_dictionary.groups[key];
+
+ // add category
+ categories.push(group.key);
+
+ // format chart data
+ var point = [];
+ for (var key in group.values) {
+ point.push(group.values[key].x);
+ }
+
+ // add to data
+ data.push (point);
+ }
+
+ // categories
+ self.hc_config.xAxis.categories = categories;
+
+ // update data
+ self.hc_config.series[0].data = data;
+
+ // draw plot
+ self.options.canvas[0].highcharts(self.hc_config);
+
+ // set chart state
+ chart.state('ok', 'Box plot drawn.');
+
+ // unregister process
+ chart.deferred.done(process_id);
+ });
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/histogram/config.js
--- a/config/plugins/visualizations/charts/static/charts/histogram/config.js
+++ /dev/null
@@ -1,21 +0,0 @@
-define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
-
-return $.extend(true, {}, nvd3_config, {
- title : 'Histogram',
- execute : true,
- columns : {
- y : {
- title : 'Observations'
- }
- },
- settings : {
- x_axis_label : {
- init : 'Breaks'
- },
- y_axis_label : {
- init : 'Density'
- }
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/histogram/histogram.js
--- a/config/plugins/visualizations/charts/static/charts/histogram/histogram.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// dependencies
-define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- },
-
- // render
- draw : function(process_id, chart, request_dictionary)
- {
- // configure request
- var index = 0;
- for (var i in request_dictionary.groups) {
- var group = request_dictionary.groups[i];
- group.columns = {
- x: index++,
- y: index++
- }
- }
-
- var nvd3 = new NVD3(this.app, this.options);
- nvd3.draw(process_id, nv.models.multiBarChart(), chart, request_dictionary);
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/horizontal/config.js
--- a/config/plugins/visualizations/charts/static/charts/horizontal/config.js
+++ /dev/null
@@ -1,12 +0,0 @@
-define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
-
-return $.extend(true, {}, nvd3_config, {
- title : 'Bar diagram (horizontal)',
- settings : {
- x_axis_type : {
- init : 'hide'
- }
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/horizontal/horizontal.js
--- a/config/plugins/visualizations/charts/static/charts/horizontal/horizontal.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// dependencies
-define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- },
-
- // render
- draw : function(process_id, chart, request_dictionary)
- {
- var nvd3 = new NVD3(this.app, this.options);
- nvd3.draw(process_id, nv.models.multiBarHorizontalChart(), chart, request_dictionary);
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/line/config.js
--- a/config/plugins/visualizations/charts/static/charts/line/config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
-
-return $.extend(true, {}, nvd3_config, {
- title : 'Line chart',
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/line/line.js
--- a/config/plugins/visualizations/charts/static/charts/line/line.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// dependencies
-define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- },
-
- // render
- draw : function(process_id, chart, request_dictionary)
- {
- var nvd3 = new NVD3(this.app, this.options);
- nvd3.draw(process_id, nv.models.lineChart(), chart, request_dictionary);
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/linewithfocus/config.js
--- a/config/plugins/visualizations/charts/static/charts/linewithfocus/config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
-
-return $.extend(true, {}, nvd3_config, {
- title : 'Line with focus',
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/linewithfocus/linewithfocus.js
--- a/config/plugins/visualizations/charts/static/charts/linewithfocus/linewithfocus.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// dependencies
-define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- },
-
- // render
- draw : function(process_id, chart, request_dictionary)
- {
- var nvd3 = new NVD3(this.app, this.options);
- nvd3.draw(process_id, nv.models.lineWithFocusChart(), chart, request_dictionary);
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3/config.js
@@ -0,0 +1,220 @@
+define([], function() {
+
+return {
+ title : '',
+ library : 'nvd3.js',
+ tag : 'svg',
+ columns : {
+ y : {
+ title : 'Values for y-axis'
+ }
+ },
+ 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'
+ }
+ ]
+ }
+ }
+};
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3/nvd3.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3/nvd3.js
@@ -0,0 +1,80 @@
+// dependencies
+define([], function() {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, nvd3_model, chart, request_dictionary, callback)
+ {
+ // request data
+ var self = this;
+ this.app.datasets.request(request_dictionary, function() {
+ nv.addGraph(function() {
+ // x axis
+ self._axis(nvd3_model.xAxis, chart.settings.get('x_axis_type'), chart.settings.get('x_axis_tick'));
+
+ // x axis label
+ nvd3_model.xAxis.axisLabel(chart.settings.get('x_axis_label'));
+
+ // y axis
+ self._axis(nvd3_model.yAxis, chart.settings.get('y_axis_type'), chart.settings.get('y_axis_tick'));
+
+ // y axis label
+ nvd3_model.yAxis.axisLabel(chart.settings.get('y_axis_label'))
+ .axisLabelDistance(30);
+
+ // controls
+ if (chart.groups.length == 1) {
+ nvd3_model.options({ showControls: false });
+ }
+
+ // legend
+ var legend_visible = true;
+ if (chart.settings.get('show_legend') == 'false') {
+ legend_visible = false;
+ }
+ nvd3_model.showLegend(legend_visible);
+
+ // custom callback
+ if (callback) {
+ callback(nvd3_model);
+ }
+
+ // parse data to canvas
+ self.options.canvas[0].datum(request_dictionary.groups)
+ .call(nvd3_model);
+
+ // refresh on window resize
+ nv.utils.windowResize(nvd3_model.update);
+
+ // set chart state
+ chart.state('ok', 'Chart has been drawn.');
+
+ // unregister process
+ chart.deferred.done(process_id);
+ });
+ });
+ },
+
+ // make axis
+ _axis: function(axis, type, tick) {
+ switch (type) {
+ case 'hide':
+ axis.tickFormat(function() { return '' });
+ break;
+ case 'auto':
+ break;
+ default:
+ axis.tickFormat(d3.format(tick + type));
+ }
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_bardiagram/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_bardiagram/config.js
@@ -0,0 +1,7 @@
+define(['plugin/charts/nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Bar diagram',
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_bardiagram/nvd3_bardiagram.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_bardiagram/nvd3_bardiagram.js
@@ -0,0 +1,21 @@
+// dependencies
+define(['plugin/charts/nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.multiBarChart(), chart, request_dictionary);
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_histogram/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_histogram/config.js
@@ -0,0 +1,27 @@
+define(['plugin/charts/nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Histogram',
+ 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'
+ }
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_histogram/nvd3_histogram.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_histogram/nvd3_histogram.js
@@ -0,0 +1,39 @@
+// dependencies
+define(['plugin/charts/nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ // configure request
+ var index = 1;
+ for (var i in request_dictionary.groups) {
+ var group = request_dictionary.groups[i];
+ group.columns = {
+ x: {
+ index: 0
+ },
+ y: {
+ index: index++
+ },
+ }
+ }
+
+ // link this
+ var self = this;
+
+ // load nvd3
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.multiBarChart(), chart, request_dictionary);
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_horizontal/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_horizontal/config.js
@@ -0,0 +1,12 @@
+define(['plugin/charts/nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Bar diagram (horizontal)',
+ settings : {
+ x_axis_type : {
+ init : 'hide'
+ }
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_horizontal/nvd3_horizontal.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_horizontal/nvd3_horizontal.js
@@ -0,0 +1,21 @@
+// dependencies
+define(['plugin/charts/nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.multiBarHorizontalChart(), chart, request_dictionary);
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_line/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_line/config.js
@@ -0,0 +1,7 @@
+define(['plugin/charts/nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Line chart',
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_line/nvd3_line.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_line/nvd3_line.js
@@ -0,0 +1,21 @@
+// dependencies
+define(['plugin/charts/nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.lineChart(), chart, request_dictionary);
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_linewithfocus/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_linewithfocus/config.js
@@ -0,0 +1,7 @@
+define(['plugin/charts/nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Line with focus',
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_linewithfocus/nvd3_linewithfocus.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_linewithfocus/nvd3_linewithfocus.js
@@ -0,0 +1,21 @@
+// dependencies
+define(['plugin/charts/nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.lineWithFocusChart(), chart, request_dictionary);
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_piechart/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_piechart/config.js
@@ -0,0 +1,114 @@
+define([], function() {
+
+return {
+ title : 'Pie chart',
+ library : 'nvd3.js',
+ tag : 'svg',
+ use_panels : true,
+ columns : {
+ label : {
+ title : 'Labels',
+ is_label : true
+ },
+ 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'
+ }
+ ]
+ }
+ }
+};
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_piechart/nvd3_piechart.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_piechart/nvd3_piechart.js
@@ -0,0 +1,115 @@
+// dependencies
+define(['utils/utils'], function(Utils) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ // request data
+ var self = this;
+ this.app.datasets.request(request_dictionary, function() {
+
+ // loop through data groups
+ for (var group_index in request_dictionary.groups) {
+ // get group
+ var group = request_dictionary.groups[group_index];
+
+ // draw group
+ self._draw_group(chart, group, self.options.canvas[group_index]);
+ }
+
+ // set chart state
+ chart.state('ok', 'Pie chart has been drawn.');
+
+ // unregister process
+ chart.deferred.done(process_id);
+ });
+ },
+
+ // draw group
+ _draw_group: function(chart, group, canvas) {
+
+ // add title
+ var title = canvas.append('text');
+
+ // configure title attributes
+ this._fix_title(chart, canvas, title, group.key);
+
+ // format chart data
+ var pie_data = [];
+ for (var key in group.values) {
+ var value = group.values[key];
+ pie_data.push ({
+ y : value.y,
+ x : value.label
+ });
+ }
+
+ // add graph to screen
+ var self = this;
+ nv.addGraph(function() {
+ // legend
+ var legend_visible = true;
+ if (chart.settings.get('show_legend') == 'false') {
+ legend_visible = false;
+ }
+
+ // legend
+ var label_outside = true;
+ if (chart.settings.get('label_outside') == 'false') {
+ label_outside = false;
+ }
+
+ // label type
+ var label_type = chart.settings.get('label_type');
+
+ // ratio
+ var donut_ratio = parseFloat(chart.settings.get('donut_ratio'))
+
+ // create chart model
+ var chart_3d = nv.models.pieChart()
+ .donut(true)
+ .labelThreshold(.05)
+ .showLegend(legend_visible)
+ .labelType(label_type)
+ .donutRatio(donut_ratio)
+ .donutLabelsOutside(label_outside);
+
+ // add data to canvas
+ canvas.datum(pie_data)
+ .call(chart_3d);
+
+ // add resize trigger
+ nv.utils.windowResize(function() {
+ // update chart
+ chart_3d.update();
+
+ // fix title
+ self._fix_title(chart, canvas, title, group.key);
+ });
+ });
+ },
+
+ // fix title
+ _fix_title: function(chart, canvas, title_element, title_text) {
+ // update title
+ var width = parseInt(canvas.style('width'));
+ var height = parseInt(canvas.style('height'));
+
+ // add title
+ title_element.attr('x', width / 2)
+ .attr('y', height - 10)
+ .attr('text-anchor', 'middle')
+ .text(title_text);
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_scatterplot/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_scatterplot/config.js
@@ -0,0 +1,12 @@
+define(['plugin/charts/nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Scatter plot',
+ columns : {
+ x : {
+ title : 'Values for x-axis'
+ }
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_scatterplot/nvd3_scatterplot.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_scatterplot/nvd3_scatterplot.js
@@ -0,0 +1,25 @@
+// dependencies
+define(['plugin/charts/nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.scatterChart(), chart, request_dictionary, function(nvd3_model) {
+ nvd3_model.showDistX(true)
+ .showDistY(true)
+ .color(d3.scale.category10().range());
+ });
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_stackedarea/config.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_stackedarea/config.js
@@ -0,0 +1,7 @@
+define(['plugin/charts/nvd3/config'], function(nvd3_config) {
+
+return $.extend(true, {}, nvd3_config, {
+ title : 'Stacked area'
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/nvd3_stackedarea/nvd3_stackedarea.js
--- /dev/null
+++ b/config/plugins/visualizations/charts/static/charts/nvd3_stackedarea/nvd3_stackedarea.js
@@ -0,0 +1,26 @@
+// dependencies
+define(['plugin/charts/nvd3/nvd3'], function(NVD3) {
+
+// widget
+return Backbone.View.extend(
+{
+ // initialize
+ initialize: function(app, options) {
+ this.app = app;
+ this.options = options;
+ },
+
+ // render
+ draw : function(process_id, chart, request_dictionary)
+ {
+ var nvd3 = new NVD3(this.app, this.options);
+ nvd3.draw(process_id, nv.models.stackedAreaChart(), chart, request_dictionary, function(nvd3_model) {
+ // make plot
+ nvd3_model.x(function(d) { return d.x })
+ .y(function(d) { return d.y })
+ .clipEdge(true);
+ });
+ }
+});
+
+});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/piechart/config.js
--- a/config/plugins/visualizations/charts/static/charts/piechart/config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
-
-return $.extend(true, {}, nvd3_config, {
- title : 'Pie chart',
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/piechart/piechart.js
--- a/config/plugins/visualizations/charts/static/charts/piechart/piechart.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// dependencies
-define(['utils/utils'], function(Utils) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- },
-
- // render
- draw : function(process_id, chart, request_dictionary)
- {
- // request data
- var self = this;
- this.app.datasets.request(request_dictionary, function(data) {
-
- // loop through data groups
- for (var key in request_dictionary.groups) {
- // get group
- var group = request_dictionary.groups[key];
-
- // format chart data
- var pie_data = [];
- for (var key in group.values) {
- var value = group.values[key];
- pie_data.push ({
- key : value.x,
- y : value.y
- });
- }
-
- // add graph to screen
- nv.addGraph(function() {
- self.chart_3d = nv.models.pieChart()
- .donut(true)
- .showLegend(false);
-
- self.options.svg.datum(pie_data)
- .call(self.chart_3d);
-
- nv.utils.windowResize(self.chart_3d.update);
-
- // set chart state
- chart.state('ok', 'Pie chart drawn.');
-
- // unregister process
- chart.deferred.done(process_id);
- });
- }
- });
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/scatterplot/config.js
--- a/config/plugins/visualizations/charts/static/charts/scatterplot/config.js
+++ /dev/null
@@ -1,12 +0,0 @@
-define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
-
-return $.extend(true, {}, nvd3_config, {
- title : 'Scatter plot',
- columns : {
- x : {
- title : 'Values for x-axis'
- }
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/scatterplot/scatterplot.js
--- a/config/plugins/visualizations/charts/static/charts/scatterplot/scatterplot.js
+++ /dev/null
@@ -1,25 +0,0 @@
-// dependencies
-define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- },
-
- // render
- draw : function(process_id, chart, request_dictionary)
- {
- var nvd3 = new NVD3(this.app, this.options);
- nvd3.draw(process_id, nv.models.scatterChart(), chart, request_dictionary, function(nvd3_model) {
- nvd3_model.showDistX(true)
- .showDistY(true)
- .color(d3.scale.category10().range());
- });
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/stackedarea/config.js
--- a/config/plugins/visualizations/charts/static/charts/stackedarea/config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
-
-return $.extend(true, {}, nvd3_config, {
- title : 'Stacked area'
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/stackedarea/stackedarea.js
--- a/config/plugins/visualizations/charts/static/charts/stackedarea/stackedarea.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// dependencies
-define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
-
-// widget
-return Backbone.View.extend(
-{
- // initialize
- initialize: function(app, options) {
- this.app = app;
- this.options = options;
- },
-
- // render
- draw : function(process_id, chart, request_dictionary)
- {
- var nvd3 = new NVD3(this.app, this.options);
- nvd3.draw(process_id, nv.models.stackedAreaChart(), chart, request_dictionary, function(nvd3_model) {
- // make plot
- nvd3_model.x(function(d) { return d.x })
- .y(function(d) { return d.y })
- .clipEdge(true);
- });
- }
-});
-
-});
\ No newline at end of file
diff -r 19e8344a0497c1dc8009b93bbef3ce3ca4e8ece5 -r 557c0f2c6129026d8a6738292a4ae26e681e1391 config/plugins/visualizations/charts/static/charts/types.js
--- a/config/plugins/visualizations/charts/static/charts/types.js
+++ b/config/plugins/visualizations/charts/static/charts/types.js
@@ -1,22 +1,22 @@
// dependencies
-define(['plugin/charts/bardiagram/config',
- //'plugin/charts/boxplot/config',
- 'plugin/charts/histogram/config',
- 'plugin/charts/horizontal/config',
- 'plugin/charts/line/config',
- 'plugin/charts/linewithfocus/config',
- 'plugin/charts/piechart/config',
- 'plugin/charts/scatterplot/config',
- 'plugin/charts/stackedarea/config',
- ], function(bardiagram,
- //boxplot,
- histogram,
- horizontal,
- line,
- linewithfocus,
- piechart,
- scatterplot,
- stackedarea
+define(['plugin/charts/nvd3_bardiagram/config',
+ 'plugin/charts/nvd3_histogram/config',
+ 'plugin/charts/nvd3_horizontal/config',
+ 'plugin/charts/nvd3_line/config',
+ 'plugin/charts/nvd3_linewithfocus/config',
+ 'plugin/charts/nvd3_piechart/config',
+ 'plugin/charts/nvd3_scatterplot/config',
+ 'plugin/charts/nvd3_stackedarea/config',
+ 'plugin/charts/highcharts_boxplot/config',
+ ], function(nvd3_bardiagram,
+ nvd3_histogram,
+ nvd3_horizontal,
+ nvd3_line,
+ nvd3_linewithfocus,
+ nvd3_piechart,
+ nvd3_scatterplot,
+ nvd3_stackedarea,
+ highcharts_boxplot
) {
// widget
@@ -24,15 +24,15 @@
{
// types
defaults: {
- 'bardiagram' : bardiagram,
- //'boxplot' : boxplot,
- 'horizontal' : horizontal,
- 'histogram' : histogram,
- 'line' : line,
- 'linewithfocus' : linewithfocus,
- 'piechart' : piechart,
- 'scatterplot' : scatterplot,
- 'stackedarea' : stackedarea
+ 'nvd3_bardiagram' : nvd3_bardiagram,
+ 'nvd3_horizontal' : nvd3_horizontal,
+ 'nvd3_histogram' : nvd3_histogram,
+ 'nvd3_line' : nvd3_line,
+ 'nvd3_linewithfocus' : nvd3_linewithfocus,
+ 'nvd3_piechart' : nvd3_piechart,
+ 'nvd3_scatterplot' : nvd3_scatterplot,
+ 'nvd3_stackedarea' : nvd3_stackedarea,
+ 'highcharts_boxplot' : highcharts_boxplot
}
});
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: davebgx: Fix functional tests for library features.
by commits-noreply@bitbucket.org 21 Apr '14
by commits-noreply@bitbucket.org 21 Apr '14
21 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/46f414686496/
Changeset: 46f414686496
User: davebgx
Date: 2014-04-21 22:51:05
Summary: Fix functional tests for library features.
Affected #: 2 files
diff -r 646c971309f3fce14d5e95a709e9ca881e0ca0f0 -r 46f414686496939dab3337ec4301e41973b1af56 templates/webapps/galaxy/library/common/move_library_item.mako
--- a/templates/webapps/galaxy/library/common/move_library_item.mako
+++ b/templates/webapps/galaxy/library/common/move_library_item.mako
@@ -67,7 +67,7 @@
<div class="toolFormBody">
%if target_library:
<div class="form-row">
- %if len( target_folder_id_select_field.options ) > 1:
+ %if len( target_folder_id_select_field.options ) >= 1:
${target_folder_id_select_field.get_html()}
%else:
%if source_library and source_library.id == target_library.id:
diff -r 646c971309f3fce14d5e95a709e9ca881e0ca0f0 -r 46f414686496939dab3337ec4301e41973b1af56 test/base/twilltestcase.py
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -194,6 +194,12 @@
file_dir = self.file_dir
return os.path.abspath( os.path.join( file_dir, filename ) )
+ def get_form_controls( self, form ):
+ formcontrols = []
+ for i, control in enumerate( form.controls ):
+ formcontrols.append( "control %d: %s" % ( i, str( control ) ) )
+ return formcontrols
+
def save_log( *path ):
"""Saves the log to a file"""
filename = os.path.join( *path )
@@ -1234,11 +1240,9 @@
# To help with debugging a tool, print out the form controls when the test fails
print "form '%s' contains the following controls ( note the values )" % f.name
controls = {}
- formcontrols = []
+ formcontrols = self.get_form_controls( f )
hc_prefix = '<HiddenControl('
for i, control in enumerate( f.controls ):
- formcontrols.append( "control %d: %s" % ( i, str( control ) ) )
- for i, control in enumerate( f.controls ):
if not hc_prefix in str( control ):
try:
#check if a repeat element needs to be added
@@ -1353,16 +1357,18 @@
pass
tc.submit( button )
- def refresh_form( self, control_name, value, form_no=0, form_id=None, **kwd ):
+ def refresh_form( self, control_name, value, form_no=0, form_id=None, form_name=None, **kwd ):
"""Handles Galaxy's refresh_on_change for forms without ultimately submitting the form"""
# control_name is the name of the form field that requires refresh_on_change, and value is
# the value to which that field is being set.
for i, f in enumerate( self.showforms() ):
- if i == form_no or ( form_id is not None and f.id == form_id ):
+ if i == form_no or ( form_id is not None and f.id == form_id ) or ( form_name is not None and f.name == form_name ):
break
+ formcontrols = self.get_form_controls( f )
try:
control = f.find_control( name=control_name )
except:
+ log.debug( '\n'.join( formcontrols ) )
# This assumes we always want the first control of the given name, which may not be ideal...
control = f.find_control( name=control_name, nr=0 )
# Check for refresh_on_change attribute, submit a change if required
@@ -2534,13 +2540,22 @@
errmsg += 'Unpacked archive remains in: %s\n' % tmpd
raise AssertionError( errmsg )
shutil.rmtree( tmpd )
+
def move_library_item( self, cntrller, item_type, item_id, source_library_id, make_target_current,
- target_library_id='', target_folder_id='', strings_displayed=[], strings_displayed_after_submit=[] ):
+ target_library_id=None, target_folder_id=None, strings_displayed=[], strings_displayed_after_submit=[] ):
self.home()
- self.visit_url( "%s/library_common/move_library_item?cntrller=%s&item_type=%s&item_id=%s&source_library_id=%s&make_target_current=%s" \
- % ( self.url, cntrller, item_type, item_id, source_library_id, make_target_current ) )
+ params = dict( cntrller=cntrller,
+ item_type=item_type,
+ item_id=item_id,
+ source_library_id=source_library_id,
+ make_target_current=make_target_current )
+ if target_library_id is not None:
+ params[ 'target_library_id' ] = target_library_id
+ if target_folder_id is not None:
+ params[ 'target_folder_id' ] = target_folder_id
+ self.visit_url( "%s/library_common/move_library_item?%s" % ( self.url, urllib.urlencode( params ) ) )
if target_library_id:
- self.refresh_form( 'target_library_id', target_library_id )
+ self.refresh_form( 'target_library_id', target_library_id, form_name='move_library_item' )
if target_folder_id:
tc.fv( '1', 'target_folder_id', target_folder_id )
for check_str in strings_displayed:
@@ -2549,6 +2564,7 @@
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
self.home()
+
def delete_library_item( self, cntrller, library_id, item_id, item_name, item_type='library_dataset' ):
"""Mark a library item as deleted"""
self.home()
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: martenson: data libraries: don't allow adding dataset within deleted library; better error message handling
by commits-noreply@bitbucket.org 21 Apr '14
by commits-noreply@bitbucket.org 21 Apr '14
21 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/646c971309f3/
Changeset: 646c971309f3
User: martenson
Date: 2014-04-21 22:28:14
Summary: data libraries: don't allow adding dataset within deleted library; better error message handling
Affected #: 3 files
diff -r bba79c0856b4d195187f1883d5dc356b1b088c90 -r 646c971309f3fce14d5e95a709e9ca881e0ca0f0 lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -174,8 +174,11 @@
hda = self.get_dataset( trans, from_hda_id, check_ownership=True, check_accessible=True, check_state=True )
folder = self.get_library_folder( trans, encoded_folder_id_16, check_accessible=True )
+ library = folder.parent_library
+ if library.deleted:
+ raise exceptions.ObjectAttributeInvalidException()
if not self.can_current_user_add_to_library_item( trans, folder ):
- raise exceptions.InsufficientPermissionsException( 'You do not have proper permissions to add a dataset to a folder with id (%s)' % ( encoded_folder_id ) )
+ raise exceptions.InsufficientPermissionsException()
ldda = self.copy_hda_to_library_folder( trans, hda, folder, ldda_message=ldda_message )
update_time = ldda.update_time.strftime( "%Y-%m-%d %I:%M %p" )
@@ -183,6 +186,10 @@
rval = trans.security.encode_dict_ids( ldda_dict )
rval['update_time'] = update_time
+ except exceptions.ObjectAttributeInvalidException:
+ raise exceptions.ObjectAttributeInvalidException( 'You cannot add datasets into deleted library. Undelete it first.' )
+ except exceptions.InsufficientPermissionsException:
+ raise exceptions.exceptions.InsufficientPermissionsException( 'You do not have proper permissions to add a dataset to a folder with id (%s)' % ( encoded_folder_id ) )
except Exception, exc:
# TODO handle exceptions better within the mixins
if ( ( 'not accessible to the current user' in str( exc ) ) or ( 'You are not allowed to access this dataset' in str( exc ) ) ):
diff -r bba79c0856b4d195187f1883d5dc356b1b088c90 -r 646c971309f3fce14d5e95a709e9ca881e0ca0f0 static/scripts/mvc/library/library-foldertoolbar-view.js
--- a/static/scripts/mvc/library/library-foldertoolbar-view.js
+++ b/static/scripts/mvc/library/library-foldertoolbar-view.js
@@ -19,7 +19,11 @@
defaults: {
'can_add_library_item' : false,
- 'contains_file' : false
+ 'contains_file' : false,
+ 'adding_datasets' : {
+ 'total_number' : 0,
+ 'failed_number' : 0
+ }
},
modal : null,
@@ -301,8 +305,11 @@
// add all selected datasets from history into current folder
addAllDatasetsFromHistory : function (){
- //disable the button to prevent multiple submission
+ // disable the button to prevent multiple submission
this.modal.disableButton('Add');
+ // init the counters
+ this.options.adding_datasets.total_number = 0;
+ this.options.adding_datasets.failed_number = 0;
var history_dataset_ids = [];
this.modal.$el.find('#selected_history_content').find(':checked').each(function(){
@@ -328,6 +335,7 @@
folder_item.set({'from_hda_id':history_dataset_id});
hdas_to_add.push(folder_item);
}
+ this.options.adding_datasets.total_number = hdas_to_add.length;
// call the recursive function to call ajax one after each other (request FIFO queue)
this.chainCallAddingHdas(hdas_to_add);
},
@@ -337,8 +345,14 @@
this.added_hdas = new mod_library_model.Folder();
var popped_item = hdas_set.pop();
if (typeof popped_item === "undefined") {
- mod_toastr.success('Datasets from history added to the folder');
- this.modal.hide();
+ if (this.options.adding_datasets.failed_number === 0){
+ mod_toastr.success('Selected datasets from history added to the folder');
+ } else if (this.options.adding_datasets.failed_number === this.options.adding_datasets.total_number){
+ mod_toastr.error('There was an error and no datasets were added to the folder.');
+ } else if (this.options.adding_datasets.failed_number < this.options.adding_datasets.total_number){
+ mod_toastr.warning('Some of the datasets could not be added to the folder');
+ }
+ Galaxy.modal.hide();
return this.added_hdas;
}
var promise = $.when(popped_item.save({from_hda_id: popped_item.get('from_hda_id')}));
@@ -351,11 +365,12 @@
})
.fail(function(data){
// we have a problem
- if (data.status===403){
- mod_toastr.error('You are not allowed to access a dataset');
- } else {
- mod_toastr.error('An error occured :(');
- }
+ self.options.adding_datasets.failed_number += 1;
+ // if (typeof data.responseJSON !== "undefined"){
+ // mod_toastr.error(data.responseJSON.err_msg);
+ // } else {
+ // mod_toastr.error('An error ocurred :(');
+ // }
self.updateProgress();
self.chainCallAddingHdas(hdas_set);
});
diff -r bba79c0856b4d195187f1883d5dc356b1b088c90 -r 646c971309f3fce14d5e95a709e9ca881e0ca0f0 static/scripts/packed/mvc/library/library-foldertoolbar-view.js
--- a/static/scripts/packed/mvc/library/library-foldertoolbar-view.js
+++ b/static/scripts/packed/mvc/library/library-foldertoolbar-view.js
@@ -1,1 +1,1 @@
-define(["galaxy.masthead","utils/utils","libs/toastr","mvc/library/library-model"],function(b,d,e,c){var a=Backbone.View.extend({el:"#center",events:{"click #toolbtn_create_folder":"createFolderFromModal","click #toolbtn_bulk_import":"modalBulkImport","click .toolbtn_add_files":"addFilesToFolderModal"},defaults:{can_add_library_item:false,contains_file:false},modal:null,histories:null,initialize:function(f){this.options=_.defaults(f||{},this.defaults);this.render()},render:function(f){this.options=_.extend(this.options,f);var h=this.templateToolBar();var g=false;if(Galaxy.currUser){g=Galaxy.currUser.isAdmin()}this.$el.html(h({id:this.options.id,admin_user:g}))},configureElements:function(f){this.options=_.extend(this.options,f);if(this.options.can_add_library_item===true){$("#toolbtn_create_folder").show();$(".toolbtn_add_files").show()}if(this.options.contains_file===true){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}this.$el.find("[data-toggle]").tooltip()},createFolderFromModal:function(){event.preventDefault();event.stopPropagation();var f=this;var g=this.templateNewFolderInModal();this.modal=Galaxy.modal;this.modal.show({closing_events:true,title:"Create New Folder",body:g(),buttons:{Create:function(){f.create_new_folder_event()},Close:function(){Galaxy.modal.hide()}}})},create_new_folder_event:function(){var f=this.serialize_new_folder();if(this.validate_new_folder(f)){var g=new c.FolderAsModel();url_items=Backbone.history.fragment.split("/");current_folder_id=url_items[url_items.length-1];g.url=g.urlRoot+"/"+current_folder_id;g.save(f,{success:function(h){Galaxy.modal.hide();e.success("Folder created");h.set({type:"folder"});Galaxy.libraries.folderListView.collection.add(h)},error:function(i,h){Galaxy.modal.hide();if(typeof h.responseJSON!=="undefined"){e.error(h.responseJSON.err_msg)}else{e.error("An error ocurred :(")}}})}else{e.error("Folder's name is missing")}return false},serialize_new_folder:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val()}},validate_new_folder:function(f){return f.name!==""},modalBulkImport:function(){var f=$("#folder_table").find(":checked");if(f.length===0){e.info("You have to select some datasets first")}else{this.refreshUserHistoriesList(function(g){var h=g.templateBulkImportInModal();g.modal=Galaxy.modal;g.modal.show({closing_events:true,title:"Import into History",body:h({histories:g.histories.models}),buttons:{Import:function(){g.importAllIntoHistory()},Close:function(){Galaxy.modal.hide()}}})})}},refreshUserHistoriesList:function(g){var f=this;this.histories=new c.GalaxyHistories();this.histories.fetch({success:function(){g(f)},error:function(){}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var j=$("select[name=dataset_import_bulk] option:selected").val();this.options.last_used_history_id=j;var m=$("select[name=dataset_import_bulk] option:selected").text();var o=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){o.push(this.parentElement.parentElement.id)}});var n=this.templateImportIntoHistoryProgressBar();this.modal.$el.find(".modal-body").html(n({history_name:m}));var k=100/o.length;this.initProgress(k);var f=[];for(var g=o.length-1;g>=0;g--){var h=o[g];var l=new c.HistoryItem();l.url=l.urlRoot+j+"/contents";l.content=h;l.source="library";f.push(l)}this.chainCall(f,m)},chainCall:function(g,j){var f=this;var h=g.pop();if(typeof h==="undefined"){e.success("Datasets were imported to history: "+j);this.modal.hide();return}var i=$.when(h.save({content:h.content,source:h.source}));i.done(function(k){f.updateProgress();f.chainCall(g,j)}).fail(function(k){e.error("An error occured :(");f.updateProgress();f.chainCall(g,j)})},initProgress:function(f){this.progress=0;this.progressStep=f},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},download:function(f,j){var h=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){h.push(this.parentElement.parentElement.id)}});var g="/api/libraries/datasets/download/"+j;var i={ldda_ids:h};this.processDownload(g,i,"get")},processDownload:function(g,h,i){if(g&&h){h=typeof h==="string"?h:$.param(h);var f="";$.each(h.split("&"),function(){var j=this.split("=");f+='<input type="hidden" name="'+j[0]+'" value="'+j[1]+'" />'});$('<form action="'+g+'" method="'+(i||"post")+'">'+f+"</form>").appendTo("body").submit().remove();e.info("Your download will begin soon")}},addFilesToFolderModal:function(){this.refreshUserHistoriesList(function(f){f.modal=Galaxy.modal;var g=f.templateAddFilesInModal();f.modal.show({closing_events:true,title:"Add datasets from history to "+f.options.folder_name,body:g({histories:f.histories.models}),buttons:{Add:function(){f.addAllDatasetsFromHistory()},Close:function(){Galaxy.modal.hide()}}});if(f.histories.models.length>0){f.fetchAndDisplayHistoryContents(f.histories.models[0].id);$("#dataset_add_bulk").change(function(h){f.fetchAndDisplayHistoryContents(h.target.value)})}else{e.error("An error ocurred :(")}})},fetchAndDisplayHistoryContents:function(h){var g=new c.HistoryContents({id:h});var f=this;g.fetch({success:function(j){var i=f.templateHistoryContents();f.histories.get(h).set({contents:j});f.modal.$el.find("#selected_history_content").html(i({history_contents:j.models.reverse()}))},error:function(j,i){e.error("An error ocurred :(")}})},addAllDatasetsFromHistory:function(){this.modal.disableButton("Add");var f=[];this.modal.$el.find("#selected_history_content").find(":checked").each(function(){var i=$(this.parentElement).data("id");if(i){f.push(i)}});var l=this.options.folder_name;var k=this.templateAddingDatasetsProgressBar();this.modal.$el.find(".modal-body").html(k({folder_name:l}));this.progressStep=100/f.length;this.progress=0;var j=[];for(var h=f.length-1;h>=0;h--){history_dataset_id=f[h];var g=new c.Item();g.url="/api/folders/"+this.options.id+"/contents";g.set({from_hda_id:history_dataset_id});j.push(g)}this.chainCallAddingHdas(j)},chainCallAddingHdas:function(g){var f=this;this.added_hdas=new c.Folder();var h=g.pop();if(typeof h==="undefined"){e.success("Datasets from history added to the folder");this.modal.hide();return this.added_hdas}var i=$.when(h.save({from_hda_id:h.get("from_hda_id")}));i.done(function(j){Galaxy.libraries.folderListView.collection.add(j);f.updateProgress();f.chainCallAddingHdas(g)}).fail(function(j){if(j.status===403){e.error("You are not allowed to access a dataset")}else{e.error("An error occured :(")}f.updateProgress();f.chainCallAddingHdas(g)})},templateToolBar:function(){tmpl_array=[];tmpl_array.push('<div class="library_style_container">');tmpl_array.push('<h3>Data Libraries Beta Test. This is work in progress. Please report problems & ideas via <a href="mailto:galaxy-bugs@bx.psu.edu?Subject=DataLibrariesBeta_Feedback" target="_blank">email</a> and <a href="https://trello.com/c/nwYQNFPK/56-data-library-ui-progressive-display-of-fol…" target="_blank">Trello</a>.</h3>');tmpl_array.push('<div id="library_folder_toolbar" >');tmpl_array.push('<div class="btn-group">');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Create New Folder" id="toolbtn_create_folder" class="btn btn-default primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-folder"></span></button>');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Add Datasets to Current Folder" id="toolbtn_add_files" class="btn btn-default toolbtn_add_files primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-file"></span></span></button>');tmpl_array.push("</div>");tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Import selected datasets into history" id="toolbtn_bulk_import" class="primary-button" style="margin-left: 0.5em; " type="button"><span class="fa fa-book"></span> to history</button>');tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; ">');tmpl_array.push(' <button title="Download selected datasets" id="drop_toggle" type="button" class="primary-button dropdown-toggle" data-toggle="dropdown">');tmpl_array.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');tmpl_array.push(" </button>");tmpl_array.push(' <ul class="dropdown-menu" role="menu">');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');tmpl_array.push(" </ul>");tmpl_array.push(" </div>");tmpl_array.push(" </div>");tmpl_array.push(' <div id="folder_items_element">');tmpl_array.push(" </div>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateNewFolderInModal:function(){tmpl_array=[];tmpl_array.push('<div id="new_folder_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push("</form>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateBulkImportInModal:function(){var f=[];f.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');f.push("Select history: ");f.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</span>");return _.template(f.join(""))},templateImportIntoHistoryProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddingDatasetsProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Adding selected datasets from history to library folder <b><%= _.escape(folder_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddFilesInModal:function(){var f=[];f.push('<div id="add_files_modal">');f.push('<div id="history_modal_combo_bulk">');f.push("Select history: ");f.push('<select id="dataset_add_bulk" name="dataset_add_bulk" style="width:66%; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</div>");f.push('<div id="selected_history_content">');f.push("</div>");f.push("</div>");return _.template(f.join(""))},templateHistoryContents:function(){var f=[];f.push("Choose the datasets to import:");f.push("<ul>");f.push(" <% _.each(history_contents, function(history_item) { %>");f.push(' <li data-id="<%= _.escape(history_item.get("id")) %>">');f.push(' <input style="margin: 0;" type="checkbox"><%= _.escape(history_item.get("hid")) %>: <%= _.escape(history_item.get("name")) %>');f.push(" </li>");f.push(" <% }); %>");f.push("</ul>");return _.template(f.join(""))}});return{FolderToolbarView:a}});
\ No newline at end of file
+define(["galaxy.masthead","utils/utils","libs/toastr","mvc/library/library-model"],function(b,d,e,c){var a=Backbone.View.extend({el:"#center",events:{"click #toolbtn_create_folder":"createFolderFromModal","click #toolbtn_bulk_import":"modalBulkImport","click .toolbtn_add_files":"addFilesToFolderModal"},defaults:{can_add_library_item:false,contains_file:false,adding_datasets:{total_number:0,failed_number:0}},modal:null,histories:null,initialize:function(f){this.options=_.defaults(f||{},this.defaults);this.render()},render:function(f){this.options=_.extend(this.options,f);var h=this.templateToolBar();var g=false;if(Galaxy.currUser){g=Galaxy.currUser.isAdmin()}this.$el.html(h({id:this.options.id,admin_user:g}))},configureElements:function(f){this.options=_.extend(this.options,f);if(this.options.can_add_library_item===true){$("#toolbtn_create_folder").show();$(".toolbtn_add_files").show()}if(this.options.contains_file===true){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}this.$el.find("[data-toggle]").tooltip()},createFolderFromModal:function(){event.preventDefault();event.stopPropagation();var f=this;var g=this.templateNewFolderInModal();this.modal=Galaxy.modal;this.modal.show({closing_events:true,title:"Create New Folder",body:g(),buttons:{Create:function(){f.create_new_folder_event()},Close:function(){Galaxy.modal.hide()}}})},create_new_folder_event:function(){var f=this.serialize_new_folder();if(this.validate_new_folder(f)){var g=new c.FolderAsModel();url_items=Backbone.history.fragment.split("/");current_folder_id=url_items[url_items.length-1];g.url=g.urlRoot+"/"+current_folder_id;g.save(f,{success:function(h){Galaxy.modal.hide();e.success("Folder created");h.set({type:"folder"});Galaxy.libraries.folderListView.collection.add(h)},error:function(i,h){Galaxy.modal.hide();if(typeof h.responseJSON!=="undefined"){e.error(h.responseJSON.err_msg)}else{e.error("An error ocurred :(")}}})}else{e.error("Folder's name is missing")}return false},serialize_new_folder:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val()}},validate_new_folder:function(f){return f.name!==""},modalBulkImport:function(){var f=$("#folder_table").find(":checked");if(f.length===0){e.info("You have to select some datasets first")}else{this.refreshUserHistoriesList(function(g){var h=g.templateBulkImportInModal();g.modal=Galaxy.modal;g.modal.show({closing_events:true,title:"Import into History",body:h({histories:g.histories.models}),buttons:{Import:function(){g.importAllIntoHistory()},Close:function(){Galaxy.modal.hide()}}})})}},refreshUserHistoriesList:function(g){var f=this;this.histories=new c.GalaxyHistories();this.histories.fetch({success:function(){g(f)},error:function(){}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var j=$("select[name=dataset_import_bulk] option:selected").val();this.options.last_used_history_id=j;var m=$("select[name=dataset_import_bulk] option:selected").text();var o=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){o.push(this.parentElement.parentElement.id)}});var n=this.templateImportIntoHistoryProgressBar();this.modal.$el.find(".modal-body").html(n({history_name:m}));var k=100/o.length;this.initProgress(k);var f=[];for(var g=o.length-1;g>=0;g--){var h=o[g];var l=new c.HistoryItem();l.url=l.urlRoot+j+"/contents";l.content=h;l.source="library";f.push(l)}this.chainCall(f,m)},chainCall:function(g,j){var f=this;var h=g.pop();if(typeof h==="undefined"){e.success("Datasets were imported to history: "+j);this.modal.hide();return}var i=$.when(h.save({content:h.content,source:h.source}));i.done(function(k){f.updateProgress();f.chainCall(g,j)}).fail(function(k){e.error("An error occured :(");f.updateProgress();f.chainCall(g,j)})},initProgress:function(f){this.progress=0;this.progressStep=f},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},download:function(f,j){var h=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){h.push(this.parentElement.parentElement.id)}});var g="/api/libraries/datasets/download/"+j;var i={ldda_ids:h};this.processDownload(g,i,"get")},processDownload:function(g,h,i){if(g&&h){h=typeof h==="string"?h:$.param(h);var f="";$.each(h.split("&"),function(){var j=this.split("=");f+='<input type="hidden" name="'+j[0]+'" value="'+j[1]+'" />'});$('<form action="'+g+'" method="'+(i||"post")+'">'+f+"</form>").appendTo("body").submit().remove();e.info("Your download will begin soon")}},addFilesToFolderModal:function(){this.refreshUserHistoriesList(function(f){f.modal=Galaxy.modal;var g=f.templateAddFilesInModal();f.modal.show({closing_events:true,title:"Add datasets from history to "+f.options.folder_name,body:g({histories:f.histories.models}),buttons:{Add:function(){f.addAllDatasetsFromHistory()},Close:function(){Galaxy.modal.hide()}}});if(f.histories.models.length>0){f.fetchAndDisplayHistoryContents(f.histories.models[0].id);$("#dataset_add_bulk").change(function(h){f.fetchAndDisplayHistoryContents(h.target.value)})}else{e.error("An error ocurred :(")}})},fetchAndDisplayHistoryContents:function(h){var g=new c.HistoryContents({id:h});var f=this;g.fetch({success:function(j){var i=f.templateHistoryContents();f.histories.get(h).set({contents:j});f.modal.$el.find("#selected_history_content").html(i({history_contents:j.models.reverse()}))},error:function(j,i){e.error("An error ocurred :(")}})},addAllDatasetsFromHistory:function(){this.modal.disableButton("Add");this.options.adding_datasets.total_number=0;this.options.adding_datasets.failed_number=0;var f=[];this.modal.$el.find("#selected_history_content").find(":checked").each(function(){var i=$(this.parentElement).data("id");if(i){f.push(i)}});var l=this.options.folder_name;var k=this.templateAddingDatasetsProgressBar();this.modal.$el.find(".modal-body").html(k({folder_name:l}));this.progressStep=100/f.length;this.progress=0;var j=[];for(var h=f.length-1;h>=0;h--){history_dataset_id=f[h];var g=new c.Item();g.url="/api/folders/"+this.options.id+"/contents";g.set({from_hda_id:history_dataset_id});j.push(g)}this.options.adding_datasets.total_number=j.length;this.chainCallAddingHdas(j)},chainCallAddingHdas:function(g){var f=this;this.added_hdas=new c.Folder();var h=g.pop();if(typeof h==="undefined"){if(this.options.adding_datasets.failed_number===0){e.success("Selected datasets from history added to the folder")}else{if(this.options.adding_datasets.failed_number===this.options.adding_datasets.total_number){e.error("There was an error and no datasets were added to the folder.")}else{if(this.options.adding_datasets.failed_number<this.options.adding_datasets.total_number){e.warning("Some of the datasets could not be added to the folder")}}}Galaxy.modal.hide();return this.added_hdas}var i=$.when(h.save({from_hda_id:h.get("from_hda_id")}));i.done(function(j){Galaxy.libraries.folderListView.collection.add(j);f.updateProgress();f.chainCallAddingHdas(g)}).fail(function(j){f.options.adding_datasets.failed_number+=1;f.updateProgress();f.chainCallAddingHdas(g)})},templateToolBar:function(){tmpl_array=[];tmpl_array.push('<div class="library_style_container">');tmpl_array.push('<h3>Data Libraries Beta Test. This is work in progress. Please report problems & ideas via <a href="mailto:galaxy-bugs@bx.psu.edu?Subject=DataLibrariesBeta_Feedback" target="_blank">email</a> and <a href="https://trello.com/c/nwYQNFPK/56-data-library-ui-progressive-display-of-fol…" target="_blank">Trello</a>.</h3>');tmpl_array.push('<div id="library_folder_toolbar" >');tmpl_array.push('<div class="btn-group">');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Create New Folder" id="toolbtn_create_folder" class="btn btn-default primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-folder"></span></button>');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Add Datasets to Current Folder" id="toolbtn_add_files" class="btn btn-default toolbtn_add_files primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-file"></span></span></button>');tmpl_array.push("</div>");tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Import selected datasets into history" id="toolbtn_bulk_import" class="primary-button" style="margin-left: 0.5em; " type="button"><span class="fa fa-book"></span> to history</button>');tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; ">');tmpl_array.push(' <button title="Download selected datasets" id="drop_toggle" type="button" class="primary-button dropdown-toggle" data-toggle="dropdown">');tmpl_array.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');tmpl_array.push(" </button>");tmpl_array.push(' <ul class="dropdown-menu" role="menu">');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');tmpl_array.push(" </ul>");tmpl_array.push(" </div>");tmpl_array.push(" </div>");tmpl_array.push(' <div id="folder_items_element">');tmpl_array.push(" </div>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateNewFolderInModal:function(){tmpl_array=[];tmpl_array.push('<div id="new_folder_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push("</form>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateBulkImportInModal:function(){var f=[];f.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');f.push("Select history: ");f.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</span>");return _.template(f.join(""))},templateImportIntoHistoryProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddingDatasetsProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Adding selected datasets from history to library folder <b><%= _.escape(folder_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddFilesInModal:function(){var f=[];f.push('<div id="add_files_modal">');f.push('<div id="history_modal_combo_bulk">');f.push("Select history: ");f.push('<select id="dataset_add_bulk" name="dataset_add_bulk" style="width:66%; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</div>");f.push('<div id="selected_history_content">');f.push("</div>");f.push("</div>");return _.template(f.join(""))},templateHistoryContents:function(){var f=[];f.push("Choose the datasets to import:");f.push("<ul>");f.push(" <% _.each(history_contents, function(history_item) { %>");f.push(' <li data-id="<%= _.escape(history_item.get("id")) %>">');f.push(' <input style="margin: 0;" type="checkbox"><%= _.escape(history_item.get("hid")) %>: <%= _.escape(history_item.get("name")) %>');f.push(" </li>");f.push(" <% }); %>");f.push("</ul>");return _.template(f.join(""))}});return{FolderToolbarView:a}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: martenson: data libraries: don't allow creating folders within deleted library
by commits-noreply@bitbucket.org 21 Apr '14
by commits-noreply@bitbucket.org 21 Apr '14
21 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/bba79c0856b4/
Changeset: bba79c0856b4
User: martenson
Date: 2014-04-21 21:48:19
Summary: data libraries: don't allow creating folders within deleted library
Affected #: 3 files
diff -r d2717865114c0c577fcd508201acf5eeac2cc54a -r bba79c0856b4d195187f1883d5dc356b1b088c90 lib/galaxy/webapps/galaxy/api/folders.py
--- a/lib/galaxy/webapps/galaxy/api/folders.py
+++ b/lib/galaxy/webapps/galaxy/api/folders.py
@@ -7,10 +7,8 @@
from galaxy.web import _future_expose_api as expose_api
from galaxy.web import _future_expose_api_anonymous as expose_api_anonymous
from galaxy.web.base.controller import BaseAPIController,UsesLibraryMixin,UsesLibraryMixinItems
-# from galaxy.util.sanitize_html import sanitize_html
-
-# from cgi import escape, FieldStorage
-# from paste.httpexceptions import HTTPBadRequest
+from sqlalchemy.orm.exc import MultipleResultsFound
+from sqlalchemy.orm.exc import NoResultFound
import logging
log = logging.getLogger( __name__ )
@@ -47,7 +45,7 @@
check_accessible=True )
return self.encode_all_ids( trans, content.to_dict( view='element' ) )
- @web.expose_api
+ @expose_api
def create( self, trans, encoded_parent_folder_id, **kwd ):
"""
@@ -83,12 +81,24 @@
# encoded_parent_folder_id may be prefixed by 'F'
encoded_parent_folder_id = self.__cut_the_prefix( encoded_parent_folder_id )
-
try:
decoded_parent_folder_id = trans.security.decode_id( encoded_parent_folder_id )
except ValueError:
raise exceptions.MalformedId( "Malformed folder id ( %s ) specified, unable to decode" % ( str( id ) ) )
+ try:
+ parent_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).filter( trans.app.model.LibraryFolder.table.c.id == decoded_parent_folder_id ).one()
+ except MultipleResultsFound:
+ raise exceptions.InconsistentDatabase( 'Multiple folders found with the same id.' )
+ except NoResultFound:
+ raise exceptions.RequestParameterInvalidException( 'No folder found with the id provided.' )
+ except Exception, e:
+ raise exceptions.InternalServerError( 'Error loading from the database.' + str(e))
+
+ library = parent_folder.parent_library
+ if library.deleted:
+ raise exceptions.ObjectAttributeInvalidException( 'You cannot create folder within a deleted library. Undelete it first.' )
+
# TODO: refactor the functionality for use here instead of calling another controller
params = dict( [ ( "name", name ), ( "description", description ) ] )
status, output = trans.webapp.controllers['library_common'].create_folder( trans, 'api', encoded_parent_folder_id, '', **params )
diff -r d2717865114c0c577fcd508201acf5eeac2cc54a -r bba79c0856b4d195187f1883d5dc356b1b088c90 static/scripts/mvc/library/library-foldertoolbar-view.js
--- a/static/scripts/mvc/library/library-foldertoolbar-view.js
+++ b/static/scripts/mvc/library/library-foldertoolbar-view.js
@@ -84,16 +84,20 @@
current_folder_id = url_items[url_items.length-1];
folder.url = folder.urlRoot + '/' + current_folder_id ;
- var self = this;
folder.save(folderDetails, {
success: function (folder) {
- self.modal.hide();
+ Galaxy.modal.hide();
mod_toastr.success('Folder created');
folder.set({'type' : 'folder'});
Galaxy.libraries.folderListView.collection.add(folder);
},
- error: function(){
- mod_toastr.error('An error occured :(');
+ error: function(model, response){
+ Galaxy.modal.hide();
+ if (typeof response.responseJSON !== "undefined"){
+ mod_toastr.error(response.responseJSON.err_msg);
+ } else {
+ mod_toastr.error('An error ocurred :(');
+ }
}
});
} else {
@@ -289,7 +293,7 @@
self.histories.get(history_id).set({'contents' : history_contents});
self.modal.$el.find('#selected_history_content').html(history_contents_template({history_contents: history_contents.models.reverse()}));
},
- error: function(){
+ error: function(model, response){
mod_toastr.error('An error ocurred :(');
}
});
diff -r d2717865114c0c577fcd508201acf5eeac2cc54a -r bba79c0856b4d195187f1883d5dc356b1b088c90 static/scripts/packed/mvc/library/library-foldertoolbar-view.js
--- a/static/scripts/packed/mvc/library/library-foldertoolbar-view.js
+++ b/static/scripts/packed/mvc/library/library-foldertoolbar-view.js
@@ -1,1 +1,1 @@
-define(["galaxy.masthead","utils/utils","libs/toastr","mvc/library/library-model"],function(b,d,e,c){var a=Backbone.View.extend({el:"#center",events:{"click #toolbtn_create_folder":"createFolderFromModal","click #toolbtn_bulk_import":"modalBulkImport","click .toolbtn_add_files":"addFilesToFolderModal"},defaults:{can_add_library_item:false,contains_file:false},modal:null,histories:null,initialize:function(f){this.options=_.defaults(f||{},this.defaults);this.render()},render:function(f){this.options=_.extend(this.options,f);var h=this.templateToolBar();var g=false;if(Galaxy.currUser){g=Galaxy.currUser.isAdmin()}this.$el.html(h({id:this.options.id,admin_user:g}))},configureElements:function(f){this.options=_.extend(this.options,f);if(this.options.can_add_library_item===true){$("#toolbtn_create_folder").show();$(".toolbtn_add_files").show()}if(this.options.contains_file===true){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}this.$el.find("[data-toggle]").tooltip()},createFolderFromModal:function(){event.preventDefault();event.stopPropagation();var f=this;var g=this.templateNewFolderInModal();this.modal=Galaxy.modal;this.modal.show({closing_events:true,title:"Create New Folder",body:g(),buttons:{Create:function(){f.create_new_folder_event()},Close:function(){Galaxy.modal.hide()}}})},create_new_folder_event:function(){var f=this.serialize_new_folder();if(this.validate_new_folder(f)){var h=new c.FolderAsModel();url_items=Backbone.history.fragment.split("/");current_folder_id=url_items[url_items.length-1];h.url=h.urlRoot+"/"+current_folder_id;var g=this;h.save(f,{success:function(i){g.modal.hide();e.success("Folder created");i.set({type:"folder"});Galaxy.libraries.folderListView.collection.add(i)},error:function(){e.error("An error occured :(")}})}else{e.error("Folder's name is missing")}return false},serialize_new_folder:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val()}},validate_new_folder:function(f){return f.name!==""},modalBulkImport:function(){var f=$("#folder_table").find(":checked");if(f.length===0){e.info("You have to select some datasets first")}else{this.refreshUserHistoriesList(function(g){var h=g.templateBulkImportInModal();g.modal=Galaxy.modal;g.modal.show({closing_events:true,title:"Import into History",body:h({histories:g.histories.models}),buttons:{Import:function(){g.importAllIntoHistory()},Close:function(){Galaxy.modal.hide()}}})})}},refreshUserHistoriesList:function(g){var f=this;this.histories=new c.GalaxyHistories();this.histories.fetch({success:function(){g(f)},error:function(){}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var j=$("select[name=dataset_import_bulk] option:selected").val();this.options.last_used_history_id=j;var m=$("select[name=dataset_import_bulk] option:selected").text();var o=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){o.push(this.parentElement.parentElement.id)}});var n=this.templateImportIntoHistoryProgressBar();this.modal.$el.find(".modal-body").html(n({history_name:m}));var k=100/o.length;this.initProgress(k);var f=[];for(var g=o.length-1;g>=0;g--){var h=o[g];var l=new c.HistoryItem();l.url=l.urlRoot+j+"/contents";l.content=h;l.source="library";f.push(l)}this.chainCall(f,m)},chainCall:function(g,j){var f=this;var h=g.pop();if(typeof h==="undefined"){e.success("Datasets were imported to history: "+j);this.modal.hide();return}var i=$.when(h.save({content:h.content,source:h.source}));i.done(function(k){f.updateProgress();f.chainCall(g,j)}).fail(function(k){e.error("An error occured :(");f.updateProgress();f.chainCall(g,j)})},initProgress:function(f){this.progress=0;this.progressStep=f},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},download:function(f,j){var h=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){h.push(this.parentElement.parentElement.id)}});var g="/api/libraries/datasets/download/"+j;var i={ldda_ids:h};this.processDownload(g,i,"get")},processDownload:function(g,h,i){if(g&&h){h=typeof h==="string"?h:$.param(h);var f="";$.each(h.split("&"),function(){var j=this.split("=");f+='<input type="hidden" name="'+j[0]+'" value="'+j[1]+'" />'});$('<form action="'+g+'" method="'+(i||"post")+'">'+f+"</form>").appendTo("body").submit().remove();e.info("Your download will begin soon")}},addFilesToFolderModal:function(){this.refreshUserHistoriesList(function(f){f.modal=Galaxy.modal;var g=f.templateAddFilesInModal();f.modal.show({closing_events:true,title:"Add datasets from history to "+f.options.folder_name,body:g({histories:f.histories.models}),buttons:{Add:function(){f.addAllDatasetsFromHistory()},Close:function(){Galaxy.modal.hide()}}});if(f.histories.models.length>0){f.fetchAndDisplayHistoryContents(f.histories.models[0].id);$("#dataset_add_bulk").change(function(h){f.fetchAndDisplayHistoryContents(h.target.value)})}else{e.error("An error ocurred :(")}})},fetchAndDisplayHistoryContents:function(h){var g=new c.HistoryContents({id:h});var f=this;g.fetch({success:function(j){var i=f.templateHistoryContents();f.histories.get(h).set({contents:j});f.modal.$el.find("#selected_history_content").html(i({history_contents:j.models.reverse()}))},error:function(){e.error("An error ocurred :(")}})},addAllDatasetsFromHistory:function(){this.modal.disableButton("Add");var f=[];this.modal.$el.find("#selected_history_content").find(":checked").each(function(){var i=$(this.parentElement).data("id");if(i){f.push(i)}});var l=this.options.folder_name;var k=this.templateAddingDatasetsProgressBar();this.modal.$el.find(".modal-body").html(k({folder_name:l}));this.progressStep=100/f.length;this.progress=0;var j=[];for(var h=f.length-1;h>=0;h--){history_dataset_id=f[h];var g=new c.Item();g.url="/api/folders/"+this.options.id+"/contents";g.set({from_hda_id:history_dataset_id});j.push(g)}this.chainCallAddingHdas(j)},chainCallAddingHdas:function(g){var f=this;this.added_hdas=new c.Folder();var h=g.pop();if(typeof h==="undefined"){e.success("Datasets from history added to the folder");this.modal.hide();return this.added_hdas}var i=$.when(h.save({from_hda_id:h.get("from_hda_id")}));i.done(function(j){Galaxy.libraries.folderListView.collection.add(j);f.updateProgress();f.chainCallAddingHdas(g)}).fail(function(j){if(j.status===403){e.error("You are not allowed to access a dataset")}else{e.error("An error occured :(")}f.updateProgress();f.chainCallAddingHdas(g)})},templateToolBar:function(){tmpl_array=[];tmpl_array.push('<div class="library_style_container">');tmpl_array.push('<h3>Data Libraries Beta Test. This is work in progress. Please report problems & ideas via <a href="mailto:galaxy-bugs@bx.psu.edu?Subject=DataLibrariesBeta_Feedback" target="_blank">email</a> and <a href="https://trello.com/c/nwYQNFPK/56-data-library-ui-progressive-display-of-fol…" target="_blank">Trello</a>.</h3>');tmpl_array.push('<div id="library_folder_toolbar" >');tmpl_array.push('<div class="btn-group">');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Create New Folder" id="toolbtn_create_folder" class="btn btn-default primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-folder"></span></button>');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Add Datasets to Current Folder" id="toolbtn_add_files" class="btn btn-default toolbtn_add_files primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-file"></span></span></button>');tmpl_array.push("</div>");tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Import selected datasets into history" id="toolbtn_bulk_import" class="primary-button" style="margin-left: 0.5em; " type="button"><span class="fa fa-book"></span> to history</button>');tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; ">');tmpl_array.push(' <button title="Download selected datasets" id="drop_toggle" type="button" class="primary-button dropdown-toggle" data-toggle="dropdown">');tmpl_array.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');tmpl_array.push(" </button>");tmpl_array.push(' <ul class="dropdown-menu" role="menu">');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');tmpl_array.push(" </ul>");tmpl_array.push(" </div>");tmpl_array.push(" </div>");tmpl_array.push(' <div id="folder_items_element">');tmpl_array.push(" </div>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateNewFolderInModal:function(){tmpl_array=[];tmpl_array.push('<div id="new_folder_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push("</form>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateBulkImportInModal:function(){var f=[];f.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');f.push("Select history: ");f.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</span>");return _.template(f.join(""))},templateImportIntoHistoryProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddingDatasetsProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Adding selected datasets from history to library folder <b><%= _.escape(folder_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddFilesInModal:function(){var f=[];f.push('<div id="add_files_modal">');f.push('<div id="history_modal_combo_bulk">');f.push("Select history: ");f.push('<select id="dataset_add_bulk" name="dataset_add_bulk" style="width:66%; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</div>");f.push('<div id="selected_history_content">');f.push("</div>");f.push("</div>");return _.template(f.join(""))},templateHistoryContents:function(){var f=[];f.push("Choose the datasets to import:");f.push("<ul>");f.push(" <% _.each(history_contents, function(history_item) { %>");f.push(' <li data-id="<%= _.escape(history_item.get("id")) %>">');f.push(' <input style="margin: 0;" type="checkbox"><%= _.escape(history_item.get("hid")) %>: <%= _.escape(history_item.get("name")) %>');f.push(" </li>");f.push(" <% }); %>");f.push("</ul>");return _.template(f.join(""))}});return{FolderToolbarView:a}});
\ No newline at end of file
+define(["galaxy.masthead","utils/utils","libs/toastr","mvc/library/library-model"],function(b,d,e,c){var a=Backbone.View.extend({el:"#center",events:{"click #toolbtn_create_folder":"createFolderFromModal","click #toolbtn_bulk_import":"modalBulkImport","click .toolbtn_add_files":"addFilesToFolderModal"},defaults:{can_add_library_item:false,contains_file:false},modal:null,histories:null,initialize:function(f){this.options=_.defaults(f||{},this.defaults);this.render()},render:function(f){this.options=_.extend(this.options,f);var h=this.templateToolBar();var g=false;if(Galaxy.currUser){g=Galaxy.currUser.isAdmin()}this.$el.html(h({id:this.options.id,admin_user:g}))},configureElements:function(f){this.options=_.extend(this.options,f);if(this.options.can_add_library_item===true){$("#toolbtn_create_folder").show();$(".toolbtn_add_files").show()}if(this.options.contains_file===true){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}this.$el.find("[data-toggle]").tooltip()},createFolderFromModal:function(){event.preventDefault();event.stopPropagation();var f=this;var g=this.templateNewFolderInModal();this.modal=Galaxy.modal;this.modal.show({closing_events:true,title:"Create New Folder",body:g(),buttons:{Create:function(){f.create_new_folder_event()},Close:function(){Galaxy.modal.hide()}}})},create_new_folder_event:function(){var f=this.serialize_new_folder();if(this.validate_new_folder(f)){var g=new c.FolderAsModel();url_items=Backbone.history.fragment.split("/");current_folder_id=url_items[url_items.length-1];g.url=g.urlRoot+"/"+current_folder_id;g.save(f,{success:function(h){Galaxy.modal.hide();e.success("Folder created");h.set({type:"folder"});Galaxy.libraries.folderListView.collection.add(h)},error:function(i,h){Galaxy.modal.hide();if(typeof h.responseJSON!=="undefined"){e.error(h.responseJSON.err_msg)}else{e.error("An error ocurred :(")}}})}else{e.error("Folder's name is missing")}return false},serialize_new_folder:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val()}},validate_new_folder:function(f){return f.name!==""},modalBulkImport:function(){var f=$("#folder_table").find(":checked");if(f.length===0){e.info("You have to select some datasets first")}else{this.refreshUserHistoriesList(function(g){var h=g.templateBulkImportInModal();g.modal=Galaxy.modal;g.modal.show({closing_events:true,title:"Import into History",body:h({histories:g.histories.models}),buttons:{Import:function(){g.importAllIntoHistory()},Close:function(){Galaxy.modal.hide()}}})})}},refreshUserHistoriesList:function(g){var f=this;this.histories=new c.GalaxyHistories();this.histories.fetch({success:function(){g(f)},error:function(){}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var j=$("select[name=dataset_import_bulk] option:selected").val();this.options.last_used_history_id=j;var m=$("select[name=dataset_import_bulk] option:selected").text();var o=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){o.push(this.parentElement.parentElement.id)}});var n=this.templateImportIntoHistoryProgressBar();this.modal.$el.find(".modal-body").html(n({history_name:m}));var k=100/o.length;this.initProgress(k);var f=[];for(var g=o.length-1;g>=0;g--){var h=o[g];var l=new c.HistoryItem();l.url=l.urlRoot+j+"/contents";l.content=h;l.source="library";f.push(l)}this.chainCall(f,m)},chainCall:function(g,j){var f=this;var h=g.pop();if(typeof h==="undefined"){e.success("Datasets were imported to history: "+j);this.modal.hide();return}var i=$.when(h.save({content:h.content,source:h.source}));i.done(function(k){f.updateProgress();f.chainCall(g,j)}).fail(function(k){e.error("An error occured :(");f.updateProgress();f.chainCall(g,j)})},initProgress:function(f){this.progress=0;this.progressStep=f},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},download:function(f,j){var h=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!==""){h.push(this.parentElement.parentElement.id)}});var g="/api/libraries/datasets/download/"+j;var i={ldda_ids:h};this.processDownload(g,i,"get")},processDownload:function(g,h,i){if(g&&h){h=typeof h==="string"?h:$.param(h);var f="";$.each(h.split("&"),function(){var j=this.split("=");f+='<input type="hidden" name="'+j[0]+'" value="'+j[1]+'" />'});$('<form action="'+g+'" method="'+(i||"post")+'">'+f+"</form>").appendTo("body").submit().remove();e.info("Your download will begin soon")}},addFilesToFolderModal:function(){this.refreshUserHistoriesList(function(f){f.modal=Galaxy.modal;var g=f.templateAddFilesInModal();f.modal.show({closing_events:true,title:"Add datasets from history to "+f.options.folder_name,body:g({histories:f.histories.models}),buttons:{Add:function(){f.addAllDatasetsFromHistory()},Close:function(){Galaxy.modal.hide()}}});if(f.histories.models.length>0){f.fetchAndDisplayHistoryContents(f.histories.models[0].id);$("#dataset_add_bulk").change(function(h){f.fetchAndDisplayHistoryContents(h.target.value)})}else{e.error("An error ocurred :(")}})},fetchAndDisplayHistoryContents:function(h){var g=new c.HistoryContents({id:h});var f=this;g.fetch({success:function(j){var i=f.templateHistoryContents();f.histories.get(h).set({contents:j});f.modal.$el.find("#selected_history_content").html(i({history_contents:j.models.reverse()}))},error:function(j,i){e.error("An error ocurred :(")}})},addAllDatasetsFromHistory:function(){this.modal.disableButton("Add");var f=[];this.modal.$el.find("#selected_history_content").find(":checked").each(function(){var i=$(this.parentElement).data("id");if(i){f.push(i)}});var l=this.options.folder_name;var k=this.templateAddingDatasetsProgressBar();this.modal.$el.find(".modal-body").html(k({folder_name:l}));this.progressStep=100/f.length;this.progress=0;var j=[];for(var h=f.length-1;h>=0;h--){history_dataset_id=f[h];var g=new c.Item();g.url="/api/folders/"+this.options.id+"/contents";g.set({from_hda_id:history_dataset_id});j.push(g)}this.chainCallAddingHdas(j)},chainCallAddingHdas:function(g){var f=this;this.added_hdas=new c.Folder();var h=g.pop();if(typeof h==="undefined"){e.success("Datasets from history added to the folder");this.modal.hide();return this.added_hdas}var i=$.when(h.save({from_hda_id:h.get("from_hda_id")}));i.done(function(j){Galaxy.libraries.folderListView.collection.add(j);f.updateProgress();f.chainCallAddingHdas(g)}).fail(function(j){if(j.status===403){e.error("You are not allowed to access a dataset")}else{e.error("An error occured :(")}f.updateProgress();f.chainCallAddingHdas(g)})},templateToolBar:function(){tmpl_array=[];tmpl_array.push('<div class="library_style_container">');tmpl_array.push('<h3>Data Libraries Beta Test. This is work in progress. Please report problems & ideas via <a href="mailto:galaxy-bugs@bx.psu.edu?Subject=DataLibrariesBeta_Feedback" target="_blank">email</a> and <a href="https://trello.com/c/nwYQNFPK/56-data-library-ui-progressive-display-of-fol…" target="_blank">Trello</a>.</h3>');tmpl_array.push('<div id="library_folder_toolbar" >');tmpl_array.push('<div class="btn-group">');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Create New Folder" id="toolbtn_create_folder" class="btn btn-default primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-folder"></span></button>');tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Add Datasets to Current Folder" id="toolbtn_add_files" class="btn btn-default toolbtn_add_files primary-button" type="button" style="display:none;"><span class="fa fa-plus"></span><span class="fa fa-file"></span></span></button>');tmpl_array.push("</div>");tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Import selected datasets into history" id="toolbtn_bulk_import" class="primary-button" style="margin-left: 0.5em; " type="button"><span class="fa fa-book"></span> to history</button>');tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; ">');tmpl_array.push(' <button title="Download selected datasets" id="drop_toggle" type="button" class="primary-button dropdown-toggle" data-toggle="dropdown">');tmpl_array.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');tmpl_array.push(" </button>");tmpl_array.push(' <ul class="dropdown-menu" role="menu">');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');tmpl_array.push(' <li id="download_archive"><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');tmpl_array.push(" </ul>");tmpl_array.push(" </div>");tmpl_array.push(" </div>");tmpl_array.push(' <div id="folder_items_element">');tmpl_array.push(" </div>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateNewFolderInModal:function(){tmpl_array=[];tmpl_array.push('<div id="new_folder_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push("</form>");tmpl_array.push("</div>");return _.template(tmpl_array.join(""))},templateBulkImportInModal:function(){var f=[];f.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');f.push("Select history: ");f.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</span>");return _.template(f.join(""))},templateImportIntoHistoryProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddingDatasetsProgressBar:function(){var f=[];f.push('<div class="import_text">');f.push("Adding selected datasets from history to library folder <b><%= _.escape(folder_name) %></b>");f.push("</div>");f.push('<div class="progress">');f.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');f.push(' <span class="completion_span">0% Complete</span>');f.push(" </div>");f.push("</div>");f.push("");return _.template(f.join(""))},templateAddFilesInModal:function(){var f=[];f.push('<div id="add_files_modal">');f.push('<div id="history_modal_combo_bulk">');f.push("Select history: ");f.push('<select id="dataset_add_bulk" name="dataset_add_bulk" style="width:66%; "> ');f.push(" <% _.each(histories, function(history) { %>");f.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');f.push(" <% }); %>");f.push("</select>");f.push("</div>");f.push('<div id="selected_history_content">');f.push("</div>");f.push("</div>");return _.template(f.join(""))},templateHistoryContents:function(){var f=[];f.push("Choose the datasets to import:");f.push("<ul>");f.push(" <% _.each(history_contents, function(history_item) { %>");f.push(' <li data-id="<%= _.escape(history_item.get("id")) %>">');f.push(' <input style="margin: 0;" type="checkbox"><%= _.escape(history_item.get("hid")) %>: <%= _.escape(history_item.get("name")) %>');f.push(" </li>");f.push(" <% }); %>");f.push("</ul>");return _.template(f.join(""))}});return{FolderToolbarView:a}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: davebgx: Fix functional tests for history functions.
by commits-noreply@bitbucket.org 21 Apr '14
by commits-noreply@bitbucket.org 21 Apr '14
21 Apr '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/d2717865114c/
Changeset: d2717865114c
User: davebgx
Date: 2014-04-21 21:42:22
Summary: Fix functional tests for history functions.
Affected #: 2 files
diff -r 9856484a0dcf784ff4bf9e087c14de2e4d6d9506 -r d2717865114c0c577fcd508201acf5eeac2cc54a test/base/twilltestcase.py
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -270,11 +270,31 @@
return from_json_string( self.last_page() )
# Functions associated with histories
- def get_history_from_api( self, encoded_history_id=None ):
+ def get_history_from_api( self, encoded_history_id=None, show_deleted=None, show_details=False ):
if encoded_history_id is None:
history = self.get_latest_history()
encoded_history_id = history[ 'id' ]
- return self.json_from_url( '/api/histories/%s/contents' % encoded_history_id )
+ params = dict()
+ if show_deleted is not None:
+ params[ 'deleted' ] = show_deleted
+ api_url = '/api/histories/%s/contents?%s' % ( encoded_history_id, urllib.urlencode( params ) )
+ json_data = self.json_from_url( api_url )
+ if show_deleted is not None:
+ hdas = []
+ for hda in json_data:
+ if show_deleted:
+ hdas.append( hda )
+ else:
+ if not hda[ 'deleted' ]:
+ hdas.append( hda )
+ json_data = hdas
+ if show_details:
+ params[ 'details' ] = ','.join( [ hda[ 'id' ] for hda in json_data ] )
+ api_url = '/api/histories/%s/contents?%s' % ( encoded_history_id, urllib.urlencode( params ) )
+ json_data = self.json_from_url( api_url )
+ log.debug( 'detailed url: %s' % api_url )
+ log.debug( 'detailed json data: %s' % json_data )
+ return json_data
def get_latest_history( self ):
return self.json_from_url( '/api/histories' )[ 0 ]
@@ -325,33 +345,21 @@
raise AssertionError( errmsg )
self.home()
- def check_history_json( self, pattern, check_fn, show_deleted=None, multiline=True ):
+ def check_history_json( self, check_fn, show_deleted=None ):
"""
Tries to find a JSON string in the history page using the regex pattern,
parse it, and assert check_fn returns True when called on that parsed
data.
"""
- self.home()
- if show_deleted:
- self.visit_page( "history?show_deleted=True" )
- elif show_deleted == False:
- self.visit_page( "history?show_deleted=False" )
- else:
- self.visit_page( "history" )
- json_data = {}
try:
- tc.find( pattern, flags=( 'm' if multiline else '' ) )
- # twill stores the regex match in a special stack variable
- match = twill.namespaces.get_twill_glocals()[1][ '__match__' ]
- json_data = from_json_string( match )
- assert check_fn( json_data ), 'failed check_fn: %s' % ( check_fn.func_name )
-
- except Exception, exc:
- log.error( exc, exc_info=True )
+ json_data = self.get_history_from_api( show_deleted=show_deleted, show_details=True )
+ check_result = check_fn( json_data )
+ assert check_result, 'failed check_fn: %s (got %s)' % ( check_fn.func_name, str( check_result ) )
+ except Exception, e:
+ log.exception( e )
log.debug( 'json_data: %s', ( '\n' + pprint.pformat( json_data ) if json_data else '(no match)' ) )
fname = self.write_temp_file( tc.browser.get_html() )
- errmsg = ( "json '%s' could not be found or failed check_fn" % ( pattern ) +
- "\npage content written to '%s'" % ( fname ) )
+ errmsg = ( "json could not be read\npage content written to '%s'" % ( fname ) )
raise AssertionError( errmsg )
self.home()
diff -r 9856484a0dcf784ff4bf9e087c14de2e4d6d9506 -r d2717865114c0c577fcd508201acf5eeac2cc54a test/functional/test_history_functions.py
--- a/test/functional/test_history_functions.py
+++ b/test/functional/test_history_functions.py
@@ -665,7 +665,7 @@
if hda[ 'id' ] == self.security.encode_id( hda_2_bed.id ):
return ( not hda[ 'accessible' ] )
return False
- self.check_history_json( r'\bhdaJSON\s*=\s*(.*);', hda_2_bed_is_inaccessible )
+ self.check_history_json( hda_2_bed_is_inaccessible )
# Admin users can view all datasets ( using the history/view feature ), so make sure 2.bed is accessible to the admin
self.logout()
@@ -738,28 +738,26 @@
.order_by( desc( galaxy.model.HistoryDatasetAssociation.table.c.create_time ) )
.first() )
- # delete that item and make sure the 'history empty' message shows
+ # delete that item and make sure the history is not empty
self.home()
log.info( 'deleting last hda' )
self.delete_history_item( str( latest_hda.id ) )
- # check the historyPanel settings.show_deleted for a null json value (no show_deleted in query string)
- self.check_history_json( r'\bshow_deleted\s*:\s*(.*),', lambda x: x == None )
+ # check the number of items returned from the api
+ self.check_history_json( lambda x: len( x ) != 0 )
# reload this history with the show_deleted flag set in the query string
# the deleted dataset should be there with the proper 'deleted' text
- self.home()
log.info( 'turning show_deleted on' )
- #self.visit_url( "%s/history/?show_deleted=True" % self.url )
- # check the historyPanel settings.show_deleted for a true json value
- self.check_history_json( r'\bshow_deleted\s*:\s*(.*),', lambda x: x == True, show_deleted=True )
+ #self.visit_url( "/api/histories/<id>/contents?deleted=True" )
+ # check the number of items returned from the api
+ self.check_history_json( lambda x: len( x ) != 0, show_deleted=True )
# reload this history again with the show_deleted flag set TO FALSE in the query string
- # make sure the 'history empty' message shows
- self.home()
+ # make sure the history is empty
log.info( 'turning show_deleted off' )
- #self.visit_url( "%s/history/?show_deleted=False" % self.url )
- # check the historyPanel settings.show_deleted for a false json value
- self.check_history_json( r'\bshow_deleted\s*:\s*(.*),', lambda x: x == False, show_deleted=False )
+ #self.visit_url( "/api/histories/<id>/contents?deleted=False" )
+ # check the number of items returned from the api
+ self.check_history_json( lambda x: len( x ) == 0, show_deleted=False )
# delete this history
self.delete_history( self.security.encode_id( latest_history.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