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
11 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/26e7555cd499/
Changeset: 26e7555cd499
User: fubar
Date: 2013-10-08 01:24:59
Summary: hg backout -r 1092b0d
backout unintended commits to tophat and bowtie2 wrappers
Affected #: 3 files
diff -r f76c280cdee539b65315ac583f93bfa52feb790d -r 26e7555cd49967ab01f003abc6168f885bbe0dd1 tools/ngs_rna/tophat2_wrapper.xml
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -126,14 +126,6 @@
</command><inputs>
- <param name="jobname" type="text" value="Tophat2" size="80" label="Job title for outputs"
- help="Output name to remind you what this was for">
- <sanitizer invalid_char="">
- <valid initial="string.letters,string.digits">
- <add value="_" />
- </valid>
- </sanitizer>
- </param><conditional name="singlePaired"><param name="sPaired" type="select" label="Is this library mate-paired?"><option value="single">Single-end</option>
@@ -281,19 +273,19 @@
</stdio><outputs>
- <data format="tabular" name="fusions" label="${on_string}_${jobname}_fusions.xls" from_work_dir="tophat_out/fusions.out">
+ <data format="tabular" name="fusions" label="${tool.name} on ${on_string}: fusions" from_work_dir="tophat_out/fusions.out"><filter>(params['settingsType'] == 'full' and params['fusion_search']['do_search'] == 'Yes')</filter></data>
- <data format="bed" name="insertions" label="${on_string}_${jobname}_ins.bed" from_work_dir="tophat_out/insertions.bed">
+ <data format="bed" name="insertions" label="${tool.name} on ${on_string}: insertions" from_work_dir="tophat_out/insertions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="deletions" label="${on_string}_${jobname}_del.bed" from_work_dir="tophat_out/deletions.bed">
+ <data format="bed" name="deletions" label="${tool.name} on ${on_string}: deletions" from_work_dir="tophat_out/deletions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="junctions" label="${on_string}_${jobname}_splicejunc.bed" from_work_dir="tophat_out/junctions.bed">
+ <data format="bed" name="junctions" label="${tool.name} on ${on_string}: splice junctions" from_work_dir="tophat_out/junctions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bam" name="accepted_hits" label="${on_string}_${jobname}_hits.bam" from_work_dir="tophat_out/accepted_hits.bam">
+ <data format="bam" name="accepted_hits" label="${tool.name} on ${on_string}: accepted_hits" from_work_dir="tophat_out/accepted_hits.bam"><expand macro="dbKeyActions" /></data></outputs>
diff -r f76c280cdee539b65315ac583f93bfa52feb790d -r 26e7555cd49967ab01f003abc6168f885bbe0dd1 tools/rgenetics/rgFastQC.py
--- a/tools/rgenetics/rgFastQC.py
+++ b/tools/rgenetics/rgFastQC.py
@@ -27,7 +27,6 @@
from rgutils import getFileString
import zipfile
import gzip
-import bz2
class FastQC():
"""wrapper
@@ -63,43 +62,27 @@
infname = self.opts.inputfilename
linf = infname.lower()
trimext = False
- isgz = False
- isbz2 = False
- iszip = False
# decompression at upload currently does NOT remove this now bogus ending - fastqc will barf
# patched may 29 2013 until this is fixed properly
- f = gzip.open(self.opts.input)
- try:
+ if ( linf.endswith('.gz') or linf.endswith('.gzip') ):
+ f = gzip.open(self.opts.input)
+ try:
testrow = f.readline()
- if not (linf.endswith('.gz') or linf.endswith('.gzip')):
- isgz = True
- except:
- if linf.endswith('.gz') or linf.endswith('.gzip'):
- trimext = True
- f.close()
- f = bz2.BZ2File(self.opts.input)
- try:
- x = read(f,2) # will work if a real bz2
- if not linf.endswith('bz2'):
- isbz2 = True
- except:
- if linf.endswith('bz2'):
- trimext = True
- f.close()
- if zipfile.is_zipfile(self.opts.input):
- if not linf.endswith('.zip'):
- iszip = True
- else:
- if linf.endswith('.zip'):
- trimext = True
+ except:
+ trimext = True
+ f.close()
+ elif linf.endswith('bz2'):
+ f = bz2.open(self.opts.input,'rb')
+ try:
+ f.readline()
+ except:
+ trimext = True
+ f.close()
+ elif linf.endswith('.zip'):
+ if not zipfile.is_zipfile(self.opts.input):
+ trimext = True
if trimext:
infname = os.path.splitext(infname)[0]
- elif isgz:
- infname = '%s.gz' % infname
- elif isbz2:
- infname = '%s.bzip2' % infname
- elif iszip:
- infname = '%s.zip' % infname
fastqinfilename = re.sub(ur'[^a-zA-Z0-9_\-\.]', '_', os.path.basename(infname))
link_name = os.path.join(self.opts.outputdir, fastqinfilename)
os.symlink(self.opts.input, link_name)
diff -r f76c280cdee539b65315ac583f93bfa52feb790d -r 26e7555cd49967ab01f003abc6168f885bbe0dd1 tools/sr_mapping/bowtie2_wrapper.xml
--- a/tools/sr_mapping/bowtie2_wrapper.xml
+++ b/tools/sr_mapping/bowtie2_wrapper.xml
@@ -87,22 +87,23 @@
## read group information
#if str($read_group.selection) == "yes":
#if $read_group.rgid and $read_group.rglb and $read_group.rgpl and $read_group.rgsm:
- --rg-id "$read_group.rgid"
- --rg "LB:$read_group.rglb"
- --rg "PL:$read_group.rgpl"
- --rg "SM:$read_group.rgsm"
+ --rg-id $read_group.rgid
+ --rg LB:$read_group.rglb
+ --rg PL:$read_group.rgpl
+ --rg SM:$read_group.rgsm
#end if
#end if
## view/sort and output file
- | samtools view -Su - | samtools sort -o - - > $output
+ | samtools view -Su - | samtools sort -o - - > $output;
## rename unaligned sequence files
#if $library.type == "paired" and $output_unaligned_reads_l and $output_unaligned_reads_r:
#set left = str($output_unaligned_reads_l).replace( '.dat', '.1.dat' )
#set right = str($output_unaligned_reads_l).replace( '.dat', '.2.dat' )
- ;mv $left $output_unaligned_reads_l;
- mv $right $output_unaligned_reads_r
+
+ mv $left $output_unaligned_reads_l;
+ mv $right $output_unaligned_reads_r;
#end if
</command>
@@ -112,8 +113,6 @@
</stdio><inputs>
- <param name="jobtitle" type="text" value="bowtie2" label="Memorable short reminder of this job's importance for outputs" />
-
<!-- single/paired --><conditional name="library"><param name="type" type="select" label="Is this library mate-paired?">
@@ -217,7 +216,7 @@
<!-- define outputs --><outputs>
- <data format="fastqsanger" name="output_unaligned_reads_l" label="${on_string}_${jobtitle}_unaligL.fastq" >
+ <data format="fastqsanger" name="output_unaligned_reads_l" label="${tool.name} on ${on_string}: unaligned reads (L)" ><filter>unaligned_file is True</filter><actions><action type="format">
@@ -225,7 +224,7 @@
</action></actions></data>
- <data format="fastqsanger" name="output_unaligned_reads_r" label="${on_string}_${jobtitle}_unaligR.fastq)">
+ <data format="fastqsanger" name="output_unaligned_reads_r" label="${tool.name} on ${on_string}: unaligned reads (R)"><filter>library['type'] == "paired" and unaligned_file is True</filter><actions><action type="format">
@@ -233,7 +232,7 @@
</action></actions></data>
- <data format="bam" name="output" label="${on_string}_${jobtitle}_aligned.bam">
+ <data format="bam" name="output" label="${tool.name} on ${on_string}: aligned reads"><actions><conditional name="reference_genome.source"><when value="indexed">
@@ -255,17 +254,6 @@
</outputs><tests>
- <test>
- <!-- basic test on single paired default run -->
- <param name="type" value="single"/>
- <param name="selection" value="no"/>
- <param name="full" value="no"/>
- <param name="unaligned_file" value="false"/>
- <param name="source" value="history" />
- <param name="input_1" value="bowtie2/phix_reads.fastq" ftype="fastqsanger"/>
- <param name="own_file" value="bowtie2/phix_genome.fasta" />
- <output name="output" file="bowtie2/phix_mapped.bam" />
- </test></tests><help>
https://bitbucket.org/galaxy/galaxy-central/commits/962d981799cf/
Changeset: 962d981799cf
User: fubar
Date: 2013-10-08 01:50:10
Summary: backout -r 10799
Affected #: 191 files
diff -r 26e7555cd49967ab01f003abc6168f885bbe0dd1 -r 962d981799cf7db1fbc5dbd0e964a93771703bb7 tools/rgedgeR/rgToolFactory.py
--- a/tools/rgedgeR/rgToolFactory.py
+++ /dev/null
@@ -1,605 +0,0 @@
-# rgToolFactory.py
-# see https://bitbucket.org/fubar/galaxytoolfactory/wiki/Home
-#
-# copyright ross lazarus (ross stop lazarus at gmail stop com) May 2012
-#
-# all rights reserved
-# Licensed under the LGPL
-# suggestions for improvement and bug fixes welcome at https://bitbucket.org/fubar/galaxytoolfactory/wiki/Home
-#
-# july 2013
-# added ability to combine images and individual log files into html output
-# just make sure there's a log file foo.log and it will be output
-# together with all images named like "foo_*.pdf
-# otherwise old format for html
-#
-# January 2013
-# problem pointed out by Carlos Borroto
-# added escaping for <>$ - thought I did that ages ago...
-#
-# August 11 2012
-# changed to use shell=False and cl as a sequence
-
-# This is a Galaxy tool factory for simple scripts in python, R or whatever ails ye.
-# It also serves as the wrapper for the new tool.
-#
-# you paste and run your script
-# Only works for simple scripts that read one input from the history.
-# Optionally can write one new history dataset,
-# and optionally collect any number of outputs into links on an autogenerated HTML page.
-
-# DO NOT install on a public or important site - please.
-
-# installed generated tools are fine if the script is safe.
-# They just run normally and their user cannot do anything unusually insecure
-# but please, practice safe toolshed.
-# Read the fucking code before you install any tool
-# especially this one
-
-# After you get the script working on some test data, you can
-# optionally generate a toolshed compatible gzip file
-# containing your script safely wrapped as an ordinary Galaxy script in your local toolshed for
-# safe and largely automated installation in a production Galaxy.
-
-# If you opt for an HTML output, you get all the script outputs arranged
-# as a single Html history item - all output files are linked, thumbnails for all the pdfs.
-# Ugly but really inexpensive.
-#
-# Patches appreciated please.
-#
-#
-# long route to June 2012 product
-# Behold the awesome power of Galaxy and the toolshed with the tool factory to bind them
-# derived from an integrated script model
-# called rgBaseScriptWrapper.py
-# Note to the unwary:
-# This tool allows arbitrary scripting on your Galaxy as the Galaxy user
-# There is nothing stopping a malicious user doing whatever they choose
-# Extremely dangerous!!
-# Totally insecure. So, trusted users only
-#
-# preferred model is a developer using their throw away workstation instance - ie a private site.
-# no real risk. The universe_wsgi.ini admin_users string is checked - only admin users are permitted to run this tool.
-#
-
-import sys
-import shutil
-import subprocess
-import os
-import time
-import tempfile
-import optparse
-import tarfile
-import re
-import shutil
-import math
-
-progname = os.path.split(sys.argv[0])[1]
-myversion = 'V000.2 June 2012'
-verbose = False
-debug = False
-toolFactoryURL = 'https://bitbucket.org/fubar/galaxytoolfactory'
-
-def timenow():
- """return current time as a string
- """
- return time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(time.time()))
-
-html_escape_table = {
- "&": "&",
- ">": ">",
- "<": "<",
- "$": "\$"
- }
-
-def html_escape(text):
- """Produce entities within text."""
- return "".join(html_escape_table.get(c,c) for c in text)
-
-def cmd_exists(cmd):
- return subprocess.call("type " + cmd, shell=True,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
-
-
-class ScriptRunner:
- """class is a wrapper for an arbitrary script
- """
-
- def __init__(self,opts=None,treatbashSpecial=True):
- """
- cleanup inputs, setup some outputs
-
- """
- self.useGM = cmd_exists('gm')
- self.useIM = cmd_exists('convert')
- self.useGS = cmd_exists('gs')
- self.treatbashSpecial = treatbashSpecial
- if opts.output_dir: # simplify for the tool tarball
- os.chdir(opts.output_dir)
- self.thumbformat = 'png'
- self.opts = opts
- self.toolname = re.sub('[^a-zA-Z0-9_]+', '', opts.tool_name) # a sanitizer now does this but..
- self.toolid = self.toolname
- self.myname = sys.argv[0] # get our name because we write ourselves out as a tool later
- self.pyfile = self.myname # crude but efficient - the cruft won't hurt much
- self.xmlfile = '%s.xml' % self.toolname
- s = open(self.opts.script_path,'r').readlines()
- s = [x.rstrip() for x in s] # remove pesky dos line endings if needed
- self.script = '\n'.join(s)
- fhandle,self.sfile = tempfile.mkstemp(prefix=self.toolname,suffix=".%s" % (opts.interpreter))
- tscript = open(self.sfile,'w') # use self.sfile as script source for Popen
- tscript.write(self.script)
- tscript.close()
- self.indentedScript = '\n'.join([' %s' % x for x in s]) # for restructured text in help
- self.escapedScript = '\n'.join([html_escape(x) for x in s])
- self.elog = os.path.join(self.opts.output_dir,"%s_error.log" % self.toolname)
- if opts.output_dir: # may not want these complexities
- self.tlog = os.path.join(self.opts.output_dir,"%s_runner.log" % self.toolname)
- art = '%s.%s' % (self.toolname,opts.interpreter)
- artpath = os.path.join(self.opts.output_dir,art) # need full path
- artifact = open(artpath,'w') # use self.sfile as script source for Popen
- artifact.write(self.script)
- artifact.close()
- self.cl = []
- self.html = []
- a = self.cl.append
- a(opts.interpreter)
- if self.treatbashSpecial and opts.interpreter in ['bash','sh']:
- a(self.sfile)
- else:
- a('-') # stdin
- a(opts.input_tab)
- a(opts.output_tab)
- self.outFormats = 'tabular' # TODO make this an option at tool generation time
- self.inputFormats = 'tabular' # TODO make this an option at tool generation time
- self.test1Input = '%s_test1_input.xls' % self.toolname
- self.test1Output = '%s_test1_output.xls' % self.toolname
- self.test1HTML = '%s_test1_output.html' % self.toolname
-
- def makeXML(self):
- """
- Create a Galaxy xml tool wrapper for the new script as a string to write out
- fixme - use templating or something less fugly than this example of what we produce
-
- <tool id="reverse" name="reverse" version="0.01">
- <description>a tabular file</description>
- <command interpreter="python">
- reverse.py --script_path "$runMe" --interpreter "python"
- --tool_name "reverse" --input_tab "$input1" --output_tab "$tab_file"
- </command>
- <inputs>
- <param name="input1" type="data" format="tabular" label="Select a suitable input file from your history"/><param name="job_name" type="text" label="Supply a name for the outputs to remind you what they contain" value="reverse"/>
-
- </inputs>
- <outputs>
- <data format="tabular" name="tab_file" label="${job_name}"/>
-
- </outputs>
- <help>
-
-**What it Does**
-
-Reverse the columns in a tabular file
-
- </help>
- <configfiles>
- <configfile name="runMe">
-
-# reverse order of columns in a tabular file
-import sys
-inp = sys.argv[1]
-outp = sys.argv[2]
-i = open(inp,'r')
-o = open(outp,'w')
-for row in i:
- rs = row.rstrip().split('\t')
- rs.reverse()
- o.write('\t'.join(rs))
- o.write('\n')
-i.close()
-o.close()
-
-
- </configfile>
- </configfiles>
- </tool>
-
- """
- newXML="""<tool id="%(toolid)s" name="%(toolname)s" version="%(tool_version)s">
- %(tooldesc)s
- %(command)s
- <inputs>
- %(inputs)s
- </inputs>
- <outputs>
- %(outputs)s
- </outputs>
- <configfiles>
- <configfile name="runMe">
- %(script)s
- </configfile>
- </configfiles>
- %(tooltests)s
- <help>
- %(help)s
- </help>
- </tool>""" # needs a dict with toolname, toolid, interpreter, scriptname, command, inputs as a multi line string ready to write, outputs ditto, help ditto
-
- newCommand="""<command interpreter="python">
- %(toolname)s.py --script_path "$runMe" --interpreter "%(interpreter)s"
- --tool_name "%(toolname)s" %(command_inputs)s %(command_outputs)s
- </command>""" # may NOT be an input or htmlout
- tooltestsTabOnly = """<tests><test>
- <param name="input1" value="%(test1Input)s" ftype="tabular"/>
- <param name="job_name" value="test1"/>
- <param name="runMe" value="$runMe"/>
- <output name="tab_file" file="%(test1Output)s" ftype="tabular"/>
- </test></tests>"""
- tooltestsHTMLOnly = """<tests><test>
- <param name="input1" value="%(test1Input)s" ftype="tabular"/>
- <param name="job_name" value="test1"/>
- <param name="runMe" value="$runMe"/>
- <output name="html_file" file="%(test1HTML)s" ftype="html" lines_diff="5"/>
- </test></tests>"""
- tooltestsBoth = """<tests><test>
- <param name="input1" value="%(test1Input)s" ftype="tabular"/>
- <param name="job_name" value="test1"/>
- <param name="runMe" value="$runMe"/>
- <output name="tab_file" file="%(test1Output)s" ftype="tabular" />
- <output name="html_file" file="%(test1HTML)s" ftype="html" lines_diff="10"/>
- </test></tests>"""
- xdict = {}
- xdict['tool_version'] = self.opts.tool_version
- xdict['test1Input'] = self.test1Input
- xdict['test1HTML'] = self.test1HTML
- xdict['test1Output'] = self.test1Output
- if self.opts.make_HTML and self.opts.output_tab <> 'None':
- xdict['tooltests'] = tooltestsBoth % xdict
- elif self.opts.make_HTML:
- xdict['tooltests'] = tooltestsHTMLOnly % xdict
- else:
- xdict['tooltests'] = tooltestsTabOnly % xdict
- xdict['script'] = self.escapedScript
- # configfile is least painful way to embed script to avoid external dependencies
- # but requires escaping of <, > and $ to avoid Mako parsing
- if self.opts.help_text:
- xdict['help'] = open(self.opts.help_text,'r').read()
- else:
- xdict['help'] = 'Please ask the tool author for help as none was supplied at tool generation'
- coda = ['**Script**','Pressing execute will run the following code over your input file and generate some outputs in your history::']
- coda.append(self.indentedScript)
- coda.append('**Attribution** This Galaxy tool was created by %s at %s\nusing the Galaxy Tool Factory.' % (self.opts.user_email,timenow()))
- coda.append('See %s for details of that project' % (toolFactoryURL))
- coda.append('Please cite: Creating re-usable tools from scripts: The Galaxy Tool Factory. Ross Lazarus; Antony Kaspi; Mark Ziemann; The Galaxy Team. ')
- coda.append('Bioinformatics 2012; doi: 10.1093/bioinformatics/bts573')
- xdict['help'] = '%s\n%s' % (xdict['help'],'\n'.join(coda))
- if self.opts.tool_desc:
- xdict['tooldesc'] = '<description>%s</description>' % self.opts.tool_desc
- else:
- xdict['tooldesc'] = ''
- xdict['command_outputs'] = ''
- xdict['outputs'] = ''
- if self.opts.input_tab <> 'None':
- xdict['command_inputs'] = '--input_tab "$input1" ' # the space may matter a lot if we append something
- xdict['inputs'] = '<param name="input1" type="data" format="%s" label="Select a suitable input file from your history"/> \n' % self.inputFormats
- else:
- xdict['command_inputs'] = '' # assume no input - eg a random data generator
- xdict['inputs'] = ''
- xdict['inputs'] += '<param name="job_name" type="text" label="Supply a name for the outputs to remind you what they contain" value="%s"/> \n' % self.toolname
- xdict['toolname'] = self.toolname
- xdict['toolid'] = self.toolid
- xdict['interpreter'] = self.opts.interpreter
- xdict['scriptname'] = self.sfile
- if self.opts.make_HTML:
- xdict['command_outputs'] += ' --output_dir "$html_file.files_path" --output_html "$html_file" --make_HTML "yes" '
- xdict['outputs'] += ' <data format="html" name="html_file" label="${job_name}.html"/>\n'
- if self.opts.output_tab <> 'None':
- xdict['command_outputs'] += ' --output_tab "$tab_file"'
- xdict['outputs'] += ' <data format="%s" name="tab_file" label="${job_name}"/>\n' % self.outFormats
- xdict['command'] = newCommand % xdict
- xmls = newXML % xdict
- xf = open(self.xmlfile,'w')
- xf.write(xmls)
- xf.write('\n')
- xf.close()
- # ready for the tarball
-
-
- def makeTooltar(self):
- """
- a tool is a gz tarball with eg
- /toolname/tool.xml /toolname/tool.py /toolname/test-data/test1_in.foo ...
- """
- retval = self.run()
- if retval:
- print >> sys.stderr,'## Run failed. Cannot build yet. Please fix and retry'
- sys.exit(1)
- self.makeXML()
- tdir = self.toolname
- os.mkdir(tdir)
- if self.opts.input_tab <> 'None': # no reproducible test otherwise? TODO: maybe..
- testdir = os.path.join(tdir,'test-data')
- os.mkdir(testdir) # make tests directory
- shutil.copyfile(self.opts.input_tab,os.path.join(testdir,self.test1Input))
- if self.opts.output_tab <> 'None':
- shutil.copyfile(self.opts.output_tab,os.path.join(testdir,self.test1Output))
- if self.opts.make_HTML:
- shutil.copyfile(self.opts.output_html,os.path.join(testdir,self.test1HTML))
- if self.opts.output_dir:
- shutil.copyfile(self.tlog,os.path.join(testdir,'test1_out.log'))
- op = '%s.py' % self.toolname # new name
- outpiname = os.path.join(tdir,op) # path for the tool tarball
- pyin = os.path.basename(self.pyfile) # our name - we rewrite ourselves (TM)
- notes = ['# %s - a self annotated version of %s generated by running %s\n' % (op,pyin,pyin),]
- notes.append('# to make a new Galaxy tool called %s\n' % self.toolname)
- notes.append('# User %s at %s\n' % (self.opts.user_email,timenow()))
- pi = open(self.pyfile,'r').readlines() # our code becomes new tool wrapper (!) - first Galaxy worm
- notes += pi
- outpi = open(outpiname,'w')
- outpi.write(''.join(notes))
- outpi.write('\n')
- outpi.close()
- stname = os.path.join(tdir,self.sfile)
- if not os.path.exists(stname):
- shutil.copyfile(self.sfile, stname)
- xtname = os.path.join(tdir,self.xmlfile)
- if not os.path.exists(xtname):
- shutil.copyfile(self.xmlfile,xtname)
- tarpath = "%s.gz" % self.toolname
- tar = tarfile.open(tarpath, "w:gz")
- tar.add(tdir,arcname=self.toolname)
- tar.close()
- shutil.copyfile(tarpath,self.opts.new_tool)
- shutil.rmtree(tdir)
- ## TODO: replace with optional direct upload to local toolshed?
- return retval
-
-
- def compressPDF(self,inpdf=None,thumbformat='png'):
- """need absolute path to pdf
- """
- assert os.path.isfile(inpdf), "## Input %s supplied to %s compressPDF not found" % (inpdf,self.myName)
- hf,hlog = tempfile.mkstemp(suffix="%s.log" % self.toolname)
- sto = open(hlog,'w')
- outpdf = '%s_compressed' % inpdf
- cl = ["gs", "-sDEVICE=pdfwrite", "-dNOPAUSE", "-dBATCH","-dPDFSETTINGS=/printer", "-sOutputFile=%s" % outpdf,inpdf]
- x = subprocess.Popen(cl,stdout=sto,stderr=sto,cwd=self.opts.output_dir)
- retval1 = x.wait()
- if retval1 == 0:
- os.unlink(inpdf)
- shutil.move(outpdf,inpdf)
- outpng = '%s.%s' % (os.path.splitext(inpdf)[0],thumbformat)
- if self.useGM:
- cl2 = ['gm convert', inpdf, outpng]
- else: # assume imagemagick
- cl2 = ['convert', inpdf, outpng]
- x = subprocess.Popen(cl2,stdout=sto,stderr=sto,cwd=self.opts.output_dir)
- retval2 = x.wait()
- sto.close()
- retval = retval1 or retval2
- return retval
-
-
- def getfSize(self,fpath,outpath):
- """
- format a nice file size string
- """
- size = ''
- fp = os.path.join(outpath,fpath)
- if os.path.isfile(fp):
- size = '0 B'
- n = float(os.path.getsize(fp))
- if n > 2**20:
- size = '%1.1f MB' % (n/2**20)
- elif n > 2**10:
- size = '%1.1f KB' % (n/2**10)
- elif n > 0:
- size = '%d B' % (int(n))
- return size
-
- def makeHtml(self):
- """ Create an HTML file content to list all the artifacts found in the output_dir
- """
-
- galhtmlprefix = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
- <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <meta name="generator" content="Galaxy %s tool output - see http://g2.trac.bx.psu.edu/" />
- <title></title>
- <link rel="stylesheet" href="/static/style/base.css" type="text/css" />
- </head>
- <body>
- <div class="toolFormBody">
- """
- galhtmlattr = """<hr/><div class="infomessage">This tool (%s) was generated by the <a href="https://bitbucket.org/fubar/galaxytoolfactory/overview">Galaxy Tool Factory</a></div><br/>"""
- galhtmlpostfix = """</div></body></html>\n"""
-
- flist = os.listdir(self.opts.output_dir)
- flist = [x for x in flist if x <> 'Rplots.pdf']
- flist.sort()
- html = []
- html.append(galhtmlprefix % progname)
- html.append('<div class="infomessage">Galaxy Tool "%s" run at %s</div><br/>' % (self.toolname,timenow()))
- fhtml = []
- if len(flist) > 0:
- logfiles = [x for x in flist if x.lower().endswith('.log')] # log file names determine sections
- logfiles.sort()
- logfiles = [x for x in logfiles if os.path.abspath(x) <> os.path.abspath(self.tlog)]
- logfiles.append(os.path.abspath(self.tlog)) # make it the last one
- pdflist = []
- npdf = len([x for x in flist if os.path.splitext(x)[-1].lower() == '.pdf'])
- for rownum,fname in enumerate(flist):
- dname,e = os.path.splitext(fname)
- sfsize = self.getfSize(fname,self.opts.output_dir)
- if e.lower() == '.pdf' : # compress and make a thumbnail
- thumb = '%s.%s' % (dname,self.thumbformat)
- pdff = os.path.join(self.opts.output_dir,fname)
- retval = self.compressPDF(inpdf=pdff,thumbformat=self.thumbformat)
- if retval == 0:
- pdflist.append((fname,thumb))
- if (rownum+1) % 2 == 0:
- fhtml.append('<tr class="odd_row"><td><a href="%s">%s</a></td><td>%s</td></tr>' % (fname,fname,sfsize))
- else:
- fhtml.append('<tr><td><a href="%s">%s</a></td><td>%s</td></tr>' % (fname,fname,sfsize))
- for logfname in logfiles: # expect at least tlog - if more
- if os.path.abspath(logfname) == os.path.abspath(self.tlog): # handled later
- sectionname = 'All tool run'
- if (len(logfiles) > 1):
- sectionname = 'Other'
- ourpdfs = pdflist
- else:
- realname = os.path.basename(logfname)
- sectionname = os.path.splitext(realname)[0].split('_')[0] # break in case _ added to log
- ourpdfs = [x for x in pdflist if os.path.basename(x[0]).split('_')[0] == sectionname]
- pdflist = [x for x in pdflist if os.path.basename(x[0]).split('_')[0] <> sectionname] # remove
- nacross = 1
- npdf = len(ourpdfs)
-
- if npdf > 0:
- nacross = math.sqrt(npdf) ## int(round(math.log(npdf,2)))
- if int(nacross)**2 != npdf:
- nacross += 1
- nacross = int(nacross)
- width = min(400,int(1200/nacross))
- html.append('<div class="toolFormTitle">%s images and outputs</div>' % sectionname)
- html.append('(Click on a thumbnail image to download the corresponding original PDF image)<br/>')
- ntogo = nacross # counter for table row padding with empty cells
- html.append('<div><table class="simple" cellpadding="2" cellspacing="2">\n<tr>')
- for i,paths in enumerate(ourpdfs):
- fname,thumb = paths
- s= """<td><a href="%s"><img src="%s" title="Click to download a PDF of %s" hspace="5" width="%d"
- alt="Image called %s"/></a></td>\n""" % (fname,thumb,fname,width,fname)
- if ((i+1) % nacross == 0):
- s += '</tr>\n'
- ntogo = 0
- if i < (npdf - 1): # more to come
- s += '<tr>'
- ntogo = nacross
- else:
- ntogo -= 1
- html.append(s)
- if html[-1].strip().endswith('</tr>'):
- html.append('</table></div>\n')
- else:
- if ntogo > 0: # pad
- html.append('<td> </td>'*ntogo)
- html.append('</tr></table></div>\n')
- logt = open(logfname,'r').readlines()
- logtext = [x for x in logt if x.strip() > '']
- html.append('<div class="toolFormTitle">%s log output</div>' % sectionname)
- if len(logtext) > 1:
- html.append('\n<pre>\n')
- html += logtext
- html.append('\n</pre>\n')
- else:
- html.append('%s is empty<br/>' % logfname)
- if len(fhtml) > 0:
- fhtml.insert(0,'<div><table class="colored" cellpadding="3" cellspacing="3"><tr><th>Output File Name (click to view)</th><th>Size</th></tr>\n')
- fhtml.append('</table></div><br/>')
- html.append('<div class="toolFormTitle">All output files available for downloading</div>\n')
- html += fhtml # add all non-pdf files to the end of the display
- else:
- html.append('<div class="warningmessagelarge">### Error - %s returned no files - please confirm that parameters are sane</div>' % self.opts.interpreter)
- html.append(galhtmlpostfix)
- htmlf = file(self.opts.output_html,'w')
- htmlf.write('\n'.join(html))
- htmlf.write('\n')
- htmlf.close()
- self.html = html
-
-
- def run(self):
- """
- scripts must be small enough not to fill the pipe!
- """
- if self.treatbashSpecial and self.opts.interpreter in ['bash','sh']:
- retval = self.runBash()
- else:
- if self.opts.output_dir:
- ste = open(self.elog,'w')
- sto = open(self.tlog,'w')
- sto.write('## Toolfactory generated command line = %s\n' % ' '.join(self.cl))
- sto.flush()
- p = subprocess.Popen(self.cl,shell=False,stdout=sto,stderr=ste,stdin=subprocess.PIPE,cwd=self.opts.output_dir)
- else:
- p = subprocess.Popen(self.cl,shell=False,stdin=subprocess.PIPE)
- p.stdin.write(self.script)
- p.stdin.close()
- retval = p.wait()
- if self.opts.output_dir:
- sto.close()
- ste.close()
- err = open(self.elog,'r').readlines()
- if retval <> 0 and err: # problem
- print >> sys.stderr,err
- if self.opts.make_HTML:
- self.makeHtml()
- return retval
-
- def runBash(self):
- """
- cannot use - for bash so use self.sfile
- """
- if self.opts.output_dir:
- s = '## Toolfactory generated command line = %s\n' % ' '.join(self.cl)
- sto = open(self.tlog,'w')
- sto.write(s)
- sto.flush()
- p = subprocess.Popen(self.cl,shell=False,stdout=sto,stderr=sto,cwd=self.opts.output_dir)
- else:
- p = subprocess.Popen(self.cl,shell=False)
- retval = p.wait()
- if self.opts.output_dir:
- sto.close()
- if self.opts.make_HTML:
- self.makeHtml()
- return retval
-
-
-def main():
- u = """
- This is a Galaxy wrapper. It expects to be called by a special purpose tool.xml as:
- <command interpreter="python">rgBaseScriptWrapper.py --script_path "$scriptPath" --tool_name "foo" --interpreter "Rscript"
- </command>
- """
- op = optparse.OptionParser()
- a = op.add_option
- a('--script_path',default=None)
- a('--tool_name',default=None)
- a('--interpreter',default=None)
- a('--output_dir',default=None)
- a('--output_html',default=None)
- a('--input_tab',default="None")
- a('--output_tab',default="None")
- a('--user_email',default='Unknown')
- a('--bad_user',default=None)
- a('--make_Tool',default=None)
- a('--make_HTML',default=None)
- a('--help_text',default=None)
- a('--tool_desc',default=None)
- a('--new_tool',default=None)
- a('--tool_version',default=None)
- opts, args = op.parse_args()
- assert not opts.bad_user,'UNAUTHORISED: %s is NOT authorized to use this tool until Galaxy admin adds %s to admin_users in universe_wsgi.ini' % (opts.bad_user,opts.bad_user)
- assert opts.tool_name,'## Tool Factory expects a tool name - eg --tool_name=DESeq'
- assert opts.interpreter,'## Tool Factory wrapper expects an interpreter - eg --interpreter=Rscript'
- assert os.path.isfile(opts.script_path),'## Tool Factory wrapper expects a script path - eg --script_path=foo.R'
- if opts.output_dir:
- try:
- os.makedirs(opts.output_dir)
- except:
- pass
- r = ScriptRunner(opts)
- if opts.make_Tool:
- retcode = r.makeTooltar()
- else:
- retcode = r.run()
- os.unlink(r.sfile)
- if retcode:
- sys.exit(retcode) # indicate failure to job runner
-
-
-if __name__ == "__main__":
- main()
-
-
diff -r 26e7555cd49967ab01f003abc6168f885bbe0dd1 -r 962d981799cf7db1fbc5dbd0e964a93771703bb7 tools/rgedgeR/rgedgeRpaired.xml
--- a/tools/rgedgeR/rgedgeRpaired.xml
+++ /dev/null
@@ -1,1080 +0,0 @@
-<tool id="rgDifferentialCount" name="Differential_Count" version="0.20">
- <description>models using BioConductor packages</description>
- <requirements>
- <requirement type="package" version="2.12">biocbasics</requirement>
- <requirement type="package" version="3.0.1">r3</requirement>
- <requirement type="package" version="1.3.18">graphicsmagick</requirement>
- <requirement type="package" version="9.07">ghostscript</requirement>
- </requirements>
-
- <command interpreter="python">
- rgToolFactory.py --script_path "$runme" --interpreter "Rscript" --tool_name "DifferentialCounts"
- --output_dir "$html_file.files_path" --output_html "$html_file" --make_HTML "yes"
- </command>
- <inputs>
- <param name="input1" type="data" format="tabular" label="Select an input matrix - rows are contigs, columns are counts for each sample"
- help="Use the HTSeq based count matrix preparation tool to create these matrices from BAM/SAM files and a GTF file of genomic features"/>
- <param name="title" type="text" value="Differential Counts" size="80" label="Title for job outputs"
- help="Supply a meaningful name here to remind you what the outputs contain">
- <sanitizer invalid_char="">
- <valid initial="string.letters,string.digits"><add value="_" /></valid>
- </sanitizer>
- </param>
- <param name="treatment_name" type="text" value="Treatment" size="50" label="Treatment Name"/>
- <param name="Treat_cols" label="Select columns containing treatment." type="data_column" data_ref="input1" numerical="True"
- multiple="true" use_header_names="true" size="120" display="checkboxes">
- <validator type="no_options" message="Please select at least one column."/>
- </param>
- <param name="control_name" type="text" value="Control" size="50" label="Control Name"/>
- <param name="Control_cols" label="Select columns containing control." type="data_column" data_ref="input1" numerical="True"
- multiple="true" use_header_names="true" size="120" display="checkboxes" optional="true">
- </param>
- <param name="subjectids" type="text" optional="true" size="120" value = ""
- label="IF SUBJECTS NOT ALL INDEPENDENT! Enter comma separated strings to indicate sample labels for (eg) pairing - must be one for every column in input"
- help="Leave blank if no pairing, but eg if data from sample id A99 is in columns 2,4 and id C21 is in 3,5 then enter 'A99,C21,A99,C21'">
- <sanitizer>
- <valid initial="string.letters,string.digits"><add value="," /></valid>
- </sanitizer>
- </param>
- <param name="fQ" type="float" value="0.3" size="5" label="Non-differential contig count quantile threshold - zero to analyze all non-zero read count contigs"
- help="May be a good or a bad idea depending on the biology and the question. EG 0.3 = sparsest 30% of contigs with at least one read are removed before analysis"/>
- <param name="useNDF" type="boolean" truevalue="T" falsevalue="F" checked="false" size="1"
- label="Non differential filter - remove contigs below a threshold (1 per million) for half or more samples"
- help="May be a good or a bad idea depending on the biology and the question. This was the old default. Quantile based is available as an alternative"/>
-
- <conditional name="edgeR">
- <param name="doedgeR" type="select"
- label="Run this model using edgeR"
- help="edgeR uses a negative binomial model and seems to be powerful, even with few replicates">
- <option value="F">Do not run edgeR</option>
- <option value="T" selected="true">Run edgeR</option>
- </param>
- <when value="T">
- <param name="edgeR_priordf" type="integer" value="20" size="3"
- label="prior.df for tagwise dispersion - lower value = more emphasis on each tag's variance. Replaces prior.n and prior.df = prior.n * residual.df"
- help="0 = Use edgeR default. Use a small value to 'smooth' small samples. See edgeR docs and note below"/>
- </when>
- <when value="F"></when>
- </conditional>
- <conditional name="DESeq2">
- <param name="doDESeq2" type="select"
- label="Run the same model with DESeq2 and compare findings"
- help="DESeq2 is an update to the DESeq package. It uses different assumptions and methods to edgeR">
- <option value="F" selected="true">Do not run DESeq2</option>
- <option value="T">Run DESeq2</option>
- </param>
- <when value="T">
- <param name="DESeq_fitType" type="select">
- <option value="parametric" selected="true">Parametric (default) fit for dispersions</option>
- <option value="local">Local fit - this will automagically be used if parametric fit fails</option>
- <option value="mean">Mean dispersion fit- use this if you really understand what you're doing - read the fine manual linked below in the documentation</option>
- </param>
- </when>
- <when value="F"></when>
- </conditional>
- <param name="doVoom" type="select"
- label="Run the same model with Voom/limma and compare findings"
- help="Voom uses counts per million and a precise transformation of variance so count data can be analysed using limma">
- <option value="F" selected="true">Do not run VOOM</option>
- <option value="T">Run VOOM</option>
- </param>
- <conditional name="camera">
- <param name="doCamera" type="select" label="Run the edgeR implementation of Camera GSEA for up/down gene sets"
- help="If yes, you can choose a set of genesets to test and/or supply a gmt format geneset collection from your history">
- <option value="F" selected="true">Do not run GSEA tests with the Camera algorithm</option>
- <option value="T">Run GSEA tests with the Camera algorithm</option>
- </param>
- <when value="T">
- <conditional name="gmtSource">
- <param name="refgmtSource" type="select"
- label="Use a gene set (.gmt) from your history and/or use a built-in (MSigDB etc) gene set">
- <option value="indexed" selected="true">Use a built-in gene set</option>
- <option value="history">Use a gene set from my history</option>
- <option value="both">Add a gene set from my history to a built in gene set</option>
- </param>
- <when value="indexed">
- <param name="builtinGMT" type="select" label="Select a gene set matrix (.gmt) file to use for the analysis">
- <options from_data_table="gseaGMT_3.1">
- <filter type="sort_by" column="2" />
- <validator type="no_options" message="No GMT v3.1 files are available - please install them"/>
- </options>
- </param>
- </when>
- <when value="history">
- <param name="ownGMT" type="data" format="gmt" label="Select a Gene Set from your history" />
- </when>
- <when value="both">
- <param name="ownGMT" type="data" format="gseagmt" label="Select a Gene Set from your history" />
- <param name="builtinGMT" type="select" label="Select a gene set matrix (.gmt) file to use for the analysis">
- <options from_data_table="gseaGMT_4">
- <filter type="sort_by" column="2" />
- <validator type="no_options" message="No GMT v4 files are available - please fix tool_data_table and loc files"/>
- </options>
- </param>
- </when>
- </conditional>
- </when>
- <when value="F">
- </when>
- </conditional>
- <param name="fdrthresh" type="float" value="0.05" size="5" label="P value threshold for FDR filtering for amily wise error rate control"
- help="Conventional default value of 0.05 recommended"/>
- <param name="fdrtype" type="select" label="FDR (Type II error) control method"
- help="Use fdr or bh typically to control for the number of tests in a reliable way">
- <option value="fdr" selected="true">fdr</option>
- <option value="BH">Benjamini Hochberg</option>
- <option value="BY">Benjamini Yukateli</option>
- <option value="bonferroni">Bonferroni</option>
- <option value="hochberg">Hochberg</option>
- <option value="holm">Holm</option>
- <option value="hommel">Hommel</option>
- <option value="none">no control for multiple tests</option>
- </param>
- </inputs>
- <outputs>
- <data format="tabular" name="out_edgeR" label="${title}_topTable_edgeR.xls">
- <filter>edgeR['doedgeR'] == "T"</filter>
- </data>
- <data format="tabular" name="out_DESeq2" label="${title}_topTable_DESeq2.xls">
- <filter>DESeq2['doDESeq2'] == "T"</filter>
- </data>
- <data format="tabular" name="out_VOOM" label="${title}_topTable_VOOM.xls">
- <filter>doVoom == "T"</filter>
- </data>
- <data format="html" name="html_file" label="${title}.html"/>
- </outputs>
- <stdio>
- <exit_code range="4" level="fatal" description="Number of subject ids must match total number of samples in the input matrix" />
- </stdio>
- <tests>
-<test>
-<param name='input1' value='test_bams2mx.xls' ftype='tabular' />
- <param name='treatment_name' value='case' />
- <param name='title' value='edgeRtest' />
- <param name='useNDF' value='' />
- <param name='doedgeR' value='T' />
- <param name='doVoom' value='T' />
- <param name='doDESeq2' value='T' />
- <param name='fdrtype' value='fdr' />
- <param name='edgeR_priordf' value="8" />
- <param name='fdrthresh' value="0.05" />
- <param name='control_name' value='control' />
- <param name='subjectids' value='' />
- <param name='Treat_cols' value='3,4,5,9' />
- <param name='Control_cols' value='2,6,7,8' />
- <output name='out_edgeR' file='edgeRtest1out.xls' compare='diff' />
- <output name='html_file' file='edgeRtest1out.html' compare='diff' lines_diff='20' />
-</test>
-</tests>
-
-<configfiles>
-<configfile name="runme">
-<![CDATA[
-#
-# edgeR.Rscript
-# updated npv 2011 for R 2.14.0 and edgeR 2.4.0 by ross
-# Performs DGE on a count table containing n replicates of two conditions
-#
-# Parameters
-#
-# 1 - Output Dir
-
-# Original edgeR code by: S.Lunke and A.Kaspi
-reallybig = log10(.Machine\$double.xmax)
-reallysmall = log10(.Machine\$double.xmin)
-library('stringr')
-library('gplots')
-library('edgeR')
-hmap2 = function(cmat,nsamp=100,outpdfname='heatmap2.pdf', TName='Treatment',group=NA,myTitle='title goes here')
-{
-# Perform clustering for significant pvalues after controlling FWER
- samples = colnames(cmat)
- gu = unique(group)
- gn = rownames(cmat)
- if (length(gu) == 2) {
- col.map = function(g) {if (g==gu[1]) "#FF0000" else "#0000FF"}
- pcols = unlist(lapply(group,col.map))
- } else {
- colours = rainbow(length(gu),start=0,end=4/6)
- pcols = colours[match(group,gu)] }
- dm = cmat[(! is.na(gn)),]
- # remove unlabelled hm rows
- nprobes = nrow(dm)
- # sub = paste('Showing',nprobes,'contigs ranked for evidence of differential abundance')
- if (nprobes > nsamp) {
- dm =dm[1:nsamp,]
- #sub = paste('Showing',nsamp,'contigs ranked for evidence for differential abundance out of',nprobes,'total')
- }
- newcolnames = substr(colnames(dm),1,20)
- colnames(dm) = newcolnames
- pdf(outpdfname)
- heatmap.2(dm,main=myTitle,ColSideColors=pcols,col=topo.colors(100),dendrogram="col",key=T,density.info='none',
- Rowv=F,scale='row',trace='none',margins=c(8,8),cexRow=0.4,cexCol=0.5)
- dev.off()
-}
-
-hmap = function(cmat,nmeans=4,outpdfname="heatMap.pdf",nsamp=250,TName='Treatment',group=NA,myTitle="Title goes here")
-{
- # for 2 groups only was
- #col.map = function(g) {if (g==TName) "#FF0000" else "#0000FF"}
- #pcols = unlist(lapply(group,col.map))
- gu = unique(group)
- colours = rainbow(length(gu),start=0.3,end=0.6)
- pcols = colours[match(group,gu)]
- nrows = nrow(cmat)
- mtitle = paste(myTitle,'Heatmap: n contigs =',nrows)
- if (nrows > nsamp) {
- cmat = cmat[c(1:nsamp),]
- mtitle = paste('Heatmap: Top ',nsamp,' DE contigs (of ',nrows,')',sep='')
- }
- newcolnames = substr(colnames(cmat),1,20)
- colnames(cmat) = newcolnames
- pdf(outpdfname)
- heatmap(cmat,scale='row',main=mtitle,cexRow=0.3,cexCol=0.4,Rowv=NA,ColSideColors=pcols)
- dev.off()
-}
-
-qqPlot = function(descr='qqplot',pvector, outpdf='qqplot.pdf',...)
-# stolen from https://gist.github.com/703512
-{
- o = -log10(sort(pvector,decreasing=F))
- e = -log10( 1:length(o)/length(o) )
- o[o==-Inf] = reallysmall
- o[o==Inf] = reallybig
- maint = descr
- pdf(outpdf)
- plot(e,o,pch=19,cex=1, main=maint, ...,
- xlab=expression(Expected~~-log[10](italic(p))),
- ylab=expression(Observed~~-log[10](italic(p))),
- xlim=c(0,max(e)), ylim=c(0,max(o)))
- lines(e,e,col="red")
- grid(col = "lightgray", lty = "dotted")
- dev.off()
-}
-
-smearPlot = function(DGEList,deTags, outSmear, outMain)
- {
- pdf(outSmear)
- plotSmear(DGEList,de.tags=deTags,main=outMain)
- grid(col="lightgray", lty="dotted")
- dev.off()
- }
-
-boxPlot = function(rawrs,cleanrs,maint,myTitle,pdfname)
-{ #
- nc = ncol(rawrs)
- for (i in c(1:nc)) {rawrs[(rawrs[,i] < 0),i] = NA}
- fullnames = colnames(rawrs)
- newcolnames = substr(colnames(rawrs),1,20)
- colnames(rawrs) = newcolnames
- newcolnames = substr(colnames(cleanrs),1,20)
- colnames(cleanrs) = newcolnames
- defpar = par(no.readonly=T)
- print.noquote('raw contig counts by sample:')
- print.noquote(summary(rawrs))
- print.noquote('normalised contig counts by sample:')
- print.noquote(summary(cleanrs))
- pdf(pdfname)
- par(mfrow=c(1,2))
- boxplot(rawrs,varwidth=T,notch=T,ylab='log contig count',col="maroon",las=3,cex.axis=0.35,main=paste('Raw:',maint))
- grid(col="lightgray",lty="dotted")
- boxplot(cleanrs,varwidth=T,notch=T,ylab='log contig count',col="maroon",las=3,cex.axis=0.35,main=paste('After ',maint))
- grid(col="lightgray",lty="dotted")
- dev.off()
- pdfname = "sample_counts_histogram.pdf"
- nc = ncol(rawrs)
- print.noquote(paste('Using ncol rawrs=',nc))
- ncroot = round(sqrt(nc))
- if (ncroot*ncroot < nc) { ncroot = ncroot + 1 }
- m = c()
- for (i in c(1:nc)) {
- rhist = hist(rawrs[,i],breaks=100,plot=F)
- m = append(m,max(rhist\$counts))
- }
- ymax = max(m)
- ncols = length(fullnames)
- if (ncols > 20)
- {
- scale = 7*ncols/20
- pdf(pdfname,width=scale,height=scale)
- } else {
- pdf(pdfname)
- }
- par(mfrow=c(ncroot,ncroot))
- for (i in c(1:nc)) {
- hist(rawrs[,i], main=paste("Contig logcount",i), xlab='log raw count', col="maroon",
- breaks=100,sub=fullnames[i],cex=0.8,ylim=c(0,ymax))
- }
- dev.off()
- par(defpar)
-
-}
-
-cumPlot = function(rawrs,cleanrs,maint,myTitle)
-{ # updated to use ecdf
- pdfname = "Filtering_rowsum_bar_charts.pdf"
- defpar = par(no.readonly=T)
- lrs = log(rawrs,10)
- lim = max(lrs)
- pdf(pdfname)
- par(mfrow=c(2,1))
- hist(lrs,breaks=100,main=paste('Before:',maint),xlab="# Reads (log)",
- ylab="Count",col="maroon",sub=myTitle, xlim=c(0,lim),las=1)
- grid(col="lightgray", lty="dotted")
- lrs = log(cleanrs,10)
- hist(lrs,breaks=100,main=paste('After:',maint),xlab="# Reads (log)",
- ylab="Count",col="maroon",sub=myTitle,xlim=c(0,lim),las=1)
- grid(col="lightgray", lty="dotted")
- dev.off()
- par(defpar)
-}
-
-cumPlot1 = function(rawrs,cleanrs,maint,myTitle)
-{ # updated to use ecdf
- pdfname = paste(gsub(" ","", myTitle , fixed=TRUE),"RowsumCum.pdf",sep='_')
- pdf(pdfname)
- par(mfrow=c(2,1))
- lastx = max(rawrs)
- rawe = knots(ecdf(rawrs))
- cleane = knots(ecdf(cleanrs))
- cy = 1:length(cleane)/length(cleane)
- ry = 1:length(rawe)/length(rawe)
- plot(rawe,ry,type='l',main=paste('Before',maint),xlab="Log Contig Total Reads",
- ylab="Cumulative proportion",col="maroon",log='x',xlim=c(1,lastx),sub=myTitle)
- grid(col="blue")
- plot(cleane,cy,type='l',main=paste('After',maint),xlab="Log Contig Total Reads",
- ylab="Cumulative proportion",col="maroon",log='x',xlim=c(1,lastx),sub=myTitle)
- grid(col="blue")
- dev.off()
-}
-
-
-
-doGSEAold = function(y=NULL,design=NULL,histgmt="",
- bigmt="/data/genomes/gsea/3.1/Abetterchoice_nocgp_c2_c3_c5_symbols_all.gmt",
- ntest=0, myTitle="myTitle", outfname="GSEA.xls", minnin=5, maxnin=2000,fdrthresh=0.05,fdrtype="BH")
-{
- sink('Camera.log')
- genesets = c()
- if (bigmt > "")
- {
- bigenesets = readLines(bigmt)
- genesets = bigenesets
- }
- if (histgmt > "")
- {
- hgenesets = readLines(histgmt)
- if (bigmt > "") {
- genesets = rbind(genesets,hgenesets)
- } else {
- genesets = hgenesets
- } # use only history if no bi
- }
- print.noquote(paste("@@@read",length(genesets), 'genesets from',histgmt,bigmt))
- genesets = strsplit(genesets,'\t') # tabular. genesetid\tURLorwhatever\tgene_1\t..\tgene_n
- outf = outfname
- head=paste(myTitle,'edgeR GSEA')
- write(head,file=outfname,append=F)
- ntest=length(genesets)
- urownames = toupper(rownames(y))
- upcam = c()
- downcam = c()
- for (i in 1:ntest) {
- gs = unlist(genesets[i])
- g = gs[1] # geneset_id
- u = gs[2]
- if (u > "") { u = paste("<a href=\'",u,"\'>",u,"</a>",sep="") }
- glist = gs[3:length(gs)] # member gene symbols
- glist = toupper(glist)
- inglist = urownames %in% glist
- nin = sum(inglist)
- if ((nin > minnin) && (nin < maxnin)) {
- ### print(paste('@@found',sum(inglist),'genes in glist'))
- camres = camera(y=y,index=inglist,design=design)
- if (! is.null(camres)) {
- rownames(camres) = g # gene set name
- camres = cbind(GeneSet=g,URL=u,camres)
- if (camres\$Direction == "Up")
- {
- upcam = rbind(upcam,camres) } else {
- downcam = rbind(downcam,camres)
- }
- }
- }
- }
- uscam = upcam[order(upcam\$PValue),]
- unadjp = uscam\$PValue
- uscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
- nup = max(10,sum((uscam\$adjPValue < fdrthresh)))
- dscam = downcam[order(downcam\$PValue),]
- unadjp = dscam\$PValue
- dscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
- ndown = max(10,sum((dscam\$adjPValue < fdrthresh)))
- write.table(uscam,file=paste('camera_up',outfname,sep='_'),quote=F,sep='\t',row.names=F)
- write.table(dscam,file=paste('camera_down',outfname,sep='_'),quote=F,sep='\t',row.names=F)
- print.noquote(paste('@@@@@ Camera up top',nup,'gene sets:'))
- write.table(head(uscam,nup),file="",quote=F,sep='\t',row.names=F)
- print.noquote(paste('@@@@@ Camera down top',ndown,'gene sets:'))
- write.table(head(dscam,ndown),file="",quote=F,sep='\t',row.names=F)
- sink()
-}
-
-
-
-
-doGSEA = function(y=NULL,design=NULL,histgmt="",
- bigmt="/data/genomes/gsea/3.1/Abetterchoice_nocgp_c2_c3_c5_symbols_all.gmt",
- ntest=0, myTitle="myTitle", outfname="GSEA.xls", minnin=5, maxnin=2000,fdrthresh=0.05,fdrtype="BH")
-{
- sink('Camera.log')
- genesets = c()
- if (bigmt > "")
- {
- bigenesets = readLines(bigmt)
- genesets = bigenesets
- }
- if (histgmt > "")
- {
- hgenesets = readLines(histgmt)
- if (bigmt > "") {
- genesets = rbind(genesets,hgenesets)
- } else {
- genesets = hgenesets
- } # use only history if no bi
- }
- print.noquote(paste("@@@read",length(genesets), 'genesets from',histgmt,bigmt))
- genesets = strsplit(genesets,'\t') # tabular. genesetid\tURLorwhatever\tgene_1\t..\tgene_n
- outf = outfname
- head=paste(myTitle,'edgeR GSEA')
- write(head,file=outfname,append=F)
- ntest=length(genesets)
- urownames = toupper(rownames(y))
- upcam = c()
- downcam = c()
- incam = c()
- urls = c()
- gsids = c()
- for (i in 1:ntest) {
- gs = unlist(genesets[i])
- gsid = gs[1] # geneset_id
- url = gs[2]
- if (url > "") { url = paste("<a href=\'",url,"\'>",url,"</a>",sep="") }
- glist = gs[3:length(gs)] # member gene symbols
- glist = toupper(glist)
- inglist = urownames %in% glist
- nin = sum(inglist)
- if ((nin > minnin) && (nin < maxnin)) {
- incam = c(incam,inglist)
- gsids = c(gsids,gsid)
- urls = c(urls,url)
- }
- }
- incam = as.list(incam)
- names(incam) = gsids
- allcam = camera(y=y,index=incam,design=design)
- allcamres = cbind(geneset=gsids,allcam,URL=urls)
- for (i in 1:ntest) {
- camres = allcamres[i]
- res = try(test = (camres\$Direction == "Up"))
- if ("try-error" %in% class(res)) {
- cat("test failed, camres = :")
- print.noquote(camres)
- } else { if (camres\$Direction == "Up")
- { upcam = rbind(upcam,camres)
- } else { downcam = rbind(downcam,camres)
- }
-
- }
- }
- uscam = upcam[order(upcam\$PValue),]
- unadjp = uscam\$PValue
- uscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
- nup = max(10,sum((uscam\$adjPValue < fdrthresh)))
- dscam = downcam[order(downcam\$PValue),]
- unadjp = dscam\$PValue
- dscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
- ndown = max(10,sum((dscam\$adjPValue < fdrthresh)))
- write.table(uscam,file=paste('camera_up',outfname,sep='_'),quote=F,sep='\t',row.names=F)
- write.table(dscam,file=paste('camera_down',outfname,sep='_'),quote=F,sep='\t',row.names=F)
- print.noquote(paste('@@@@@ Camera up top',nup,'gene sets:'))
- write.table(head(uscam,nup),file="",quote=F,sep='\t',row.names=F)
- print.noquote(paste('@@@@@ Camera down top',ndown,'gene sets:'))
- write.table(head(dscam,ndown),file="",quote=F,sep='\t',row.names=F)
- sink()
- }
-
-
-edgeIt = function (Count_Matrix=c(),group=c(),out_edgeR=F,out_VOOM=F,out_DESeq2=F,fdrtype='fdr',priordf=5,
- fdrthresh=0.05,outputdir='.', myTitle='Differential Counts',libSize=c(),useNDF=F,
- filterquantile=0.2, subjects=c(),mydesign=NULL,
- doDESeq2=T,doVoom=T,doCamera=T,doedgeR=T,org='hg19',
- histgmt="", bigmt="/data/genomes/gsea/3.1/Abetterchoice_nocgp_c2_c3_c5_symbols_all.gmt",
- doCook=F,DESeq_fitType="parameteric")
-{
- # Error handling
- if (length(unique(group))!=2){
- print("Number of conditions identified in experiment does not equal 2")
- q()
- }
- require(edgeR)
- options(width = 512)
- mt = paste(unlist(strsplit(myTitle,'_')),collapse=" ")
- allN = nrow(Count_Matrix)
- nscut = round(ncol(Count_Matrix)/2)
- colTotmillionreads = colSums(Count_Matrix)/1e6
- counts.dataframe = as.data.frame(c())
- rawrs = rowSums(Count_Matrix)
- nonzerod = Count_Matrix[(rawrs > 0),] # remove all zero count genes
- nzN = nrow(nonzerod)
- nzrs = rowSums(nonzerod)
- zN = allN - nzN
- print('# Quantiles for non-zero row counts:',quote=F)
- print(quantile(nzrs,probs=seq(0,1,0.1)),quote=F)
- if (useNDF == T)
- {
- gt1rpin3 = rowSums(Count_Matrix/expandAsMatrix(colTotmillionreads,dim(Count_Matrix)) >= 1) >= nscut
- lo = colSums(Count_Matrix[!gt1rpin3,])
- workCM = Count_Matrix[gt1rpin3,]
- cleanrs = rowSums(workCM)
- cleanN = length(cleanrs)
- meth = paste( "After removing",length(lo),"contigs with fewer than ",nscut," sample read counts >= 1 per million, there are",sep="")
- print(paste("Read",allN,"contigs. Removed",zN,"contigs with no reads.",meth,cleanN,"contigs"),quote=F)
- maint = paste('Filter >=1/million reads in >=',nscut,'samples')
- } else {
- useme = (nzrs > quantile(nzrs,filterquantile))
- workCM = nonzerod[useme,]
- lo = colSums(nonzerod[!useme,])
- cleanrs = rowSums(workCM)
- cleanN = length(cleanrs)
- meth = paste("After filtering at count quantile =",filterquantile,", there are",sep="")
- print(paste('Read',allN,"contigs. Removed",zN,"with no reads.",meth,cleanN,"contigs"),quote=F)
- maint = paste('Filter below',filterquantile,'quantile')
- }
- cumPlot(rawrs=rawrs,cleanrs=cleanrs,maint=maint,myTitle=myTitle)
- allgenes = rownames(workCM)
- reg = "^chr([0-9]+):([0-9]+)-([0-9]+)"
- genecards="<a href=\'http://www.genecards.org/index.php?path=/Search/keyword/"
- ucsc = paste("<a href=\'http://genome.ucsc.edu/cgi-bin/hgTracks?db=",org,sep='')
- testreg = str_match(allgenes,reg)
- if (sum(!is.na(testreg[,1]))/length(testreg[,1]) > 0.8) # is ucsc style string
- {
- print("@@ using ucsc substitution for urls")
- contigurls = paste0(ucsc,"&position=chr",testreg[,2],":",testreg[,3],"-",testreg[,4],"\'>",allgenes,"</a>")
- } else {
- print("@@ using genecards substitution for urls")
- contigurls = paste0(genecards,allgenes,"\'>",allgenes,"</a>")
- }
- print.noquote("# urls")
- print.noquote(head(contigurls))
- print(paste("# Total low count contigs per sample = ",paste(lo,collapse=',')),quote=F)
- cmrowsums = rowSums(workCM)
- TName=unique(group)[1]
- CName=unique(group)[2]
- if (is.null(mydesign)) {
- if (length(subjects) == 0)
- {
- mydesign = model.matrix(~group)
- }
- else {
- subjf = factor(subjects)
- mydesign = model.matrix(~subjf+group) # we block on subject so make group last to simplify finding it
- }
- }
- print.noquote(paste('Using samples:',paste(colnames(workCM),collapse=',')))
- print.noquote('Using design matrix:')
- print.noquote(mydesign)
- if (doedgeR) {
- sink('edgeR.log')
- #### Setup DGEList object
- DGEList = DGEList(counts=workCM, group = group)
- DGEList = calcNormFactors(DGEList)
-
- DGEList = estimateGLMCommonDisp(DGEList,mydesign)
- comdisp = DGEList\$common.dispersion
- DGEList = estimateGLMTrendedDisp(DGEList,mydesign)
- if (edgeR_priordf > 0) {
- print.noquote(paste("prior.df =",edgeR_priordf))
- DGEList = estimateGLMTagwiseDisp(DGEList,mydesign,prior.df = edgeR_priordf)
- } else {
- DGEList = estimateGLMTagwiseDisp(DGEList,mydesign)
- }
- DGLM = glmFit(DGEList,design=mydesign)
- DE = glmLRT(DGLM,coef=ncol(DGLM\$design)) # always last one - subject is first if needed
- efflib = DGEList\$samples\$lib.size*DGEList\$samples\$norm.factors
- normData = (1e+06*DGEList\$counts/efflib)
- uoutput = cbind(
- Name=as.character(rownames(DGEList\$counts)),
- DE\$table,
- adj.p.value=p.adjust(DE\$table\$PValue, method=fdrtype),
- Dispersion=DGEList\$tagwise.dispersion,totreads=cmrowsums,normData,
- DGEList\$counts
- )
- soutput = uoutput[order(DE\$table\$PValue),] # sorted into p value order - for quick toptable
- goodness = gof(DGLM, pcutoff=fdrthresh)
- if (sum(goodness\$outlier) > 0) {
- print.noquote('GLM outliers:')
- print(paste(rownames(DGLM)[(goodness\$outlier)],collapse=','),quote=F)
- } else {
- print('No GLM fit outlier genes found\n')
- }
- z = limma::zscoreGamma(goodness\$gof.statistic, shape=goodness\$df/2, scale=2)
- pdf("edgeR_GoodnessofFit.pdf")
- qq = qqnorm(z, panel.first=grid(), main="tagwise dispersion")
- abline(0,1,lwd=3)
- points(qq\$x[goodness\$outlier],qq\$y[goodness\$outlier], pch=16, col="maroon")
- dev.off()
- estpriorn = getPriorN(DGEList)
- print(paste("Common Dispersion =",comdisp,"CV = ",sqrt(comdisp),"getPriorN = ",estpriorn),quote=F)
- efflib = DGEList\$samples\$lib.size*DGEList\$samples\$norm.factors
- normData = (1e+06*DGEList\$counts/efflib)
- uniqueg = unique(group)
- #### Plot MDS
- sample_colors = match(group,levels(group))
- sampleTypes = levels(factor(group))
- print.noquote(sampleTypes)
- pdf("edgeR_MDSplot.pdf")
- plotMDS.DGEList(DGEList,main=paste("edgeR MDS for",myTitle),cex=0.5,col=sample_colors,pch=sample_colors)
- legend(x="topleft", legend = sampleTypes,col=c(1:length(sampleTypes)), pch=19)
- grid(col="blue")
- dev.off()
- colnames(normData) = paste( colnames(normData),'N',sep="_")
- print(paste('Raw sample read totals',paste(colSums(nonzerod,na.rm=T),collapse=',')))
- nzd = data.frame(log(nonzerod + 1e-2,10))
- try( boxPlot(rawrs=nzd,cleanrs=log(normData,10),maint='TMM Normalisation',myTitle=myTitle,pdfname="edgeR_raw_norm_counts_box.pdf") )
- write.table(soutput,file=out_edgeR, quote=FALSE, sep="\t",row.names=F)
- tt = cbind(
- Name=as.character(rownames(DGEList\$counts)),
- DE\$table,
- adj.p.value=p.adjust(DE\$table\$PValue, method=fdrtype),
- Dispersion=DGEList\$tagwise.dispersion,totreads=cmrowsums
- )
- print.noquote("# edgeR Top tags\n")
- tt = cbind(tt,URL=contigurls) # add to end so table isn't laid out strangely
- tt = tt[order(DE\$table\$PValue),]
- print.noquote(tt[1:50,])
- deTags = rownames(uoutput[uoutput\$adj.p.value < fdrthresh,])
- nsig = length(deTags)
- print(paste('#',nsig,'tags significant at adj p=',fdrthresh),quote=F)
- deColours = ifelse(deTags,'red','black')
- pdf("edgeR_BCV_vs_abundance.pdf")
- plotBCV(DGEList, cex=0.3, main="Biological CV vs abundance")
- dev.off()
- dg = DGEList[order(DE\$table\$PValue),]
- #normData = (1e+06 * dg\$counts/expandAsMatrix(dg\$samples\$lib.size, dim(dg)))
- efflib = dg\$samples\$lib.size*dg\$samples\$norm.factors
- normData = (1e+06*dg\$counts/efflib)
- outpdfname="edgeR_top_100_heatmap.pdf"
- hmap2(normData,nsamp=100,TName=TName,group=group,outpdfname=outpdfname,myTitle=paste('edgeR Heatmap',myTitle))
- outSmear = "edgeR_smearplot.pdf"
- outMain = paste("Smear Plot for ",TName,' Vs ',CName,' (FDR@',fdrthresh,' N = ',nsig,')',sep='')
- smearPlot(DGEList=DGEList,deTags=deTags, outSmear=outSmear, outMain = outMain)
- qqPlot(descr=paste(myTitle,'edgeR adj p QQ plot'),pvector=tt\$adj.p.value,outpdf='edgeR_qqplot.pdf')
- norm.factor = DGEList\$samples\$norm.factors
- topresults.edgeR = soutput[which(soutput\$adj.p.value < fdrthresh), ]
- edgeRcountsindex = which(allgenes %in% rownames(topresults.edgeR))
- edgeRcounts = rep(0, length(allgenes))
- edgeRcounts[edgeRcountsindex] = 1 # Create venn diagram of hits
- sink()
- } ### doedgeR
- if (doDESeq2 == T)
- {
- sink("DESeq2.log")
- # DESeq2
- require('DESeq2')
- library('RColorBrewer')
- if (length(subjects) == 0)
- {
- pdata = data.frame(Name=colnames(workCM),Rx=group,row.names=colnames(workCM))
- deSEQds = DESeqDataSetFromMatrix(countData = workCM, colData = pdata, design = formula(~ Rx))
- } else {
- pdata = data.frame(Name=colnames(workCM),Rx=group,subjects=subjects,row.names=colnames(workCM))
- deSEQds = DESeqDataSetFromMatrix(countData = workCM, colData = pdata, design = formula(~ subjects + Rx))
- }
- #DESeq2 = DESeq(deSEQds,fitType='local',pAdjustMethod=fdrtype)
- #rDESeq = results(DESeq2)
- #newCountDataSet(workCM, group)
- deSeqDatsizefac = estimateSizeFactors(deSEQds)
- deSeqDatdisp = estimateDispersions(deSeqDatsizefac,fitType=DESeq_fitType)
- resDESeq = nbinomWaldTest(deSeqDatdisp, pAdjustMethod=fdrtype)
- rDESeq = as.data.frame(results(resDESeq))
- rDESeq = cbind(Contig=rownames(workCM),rDESeq,NReads=cmrowsums,URL=contigurls)
- srDESeq = rDESeq[order(rDESeq\$pvalue),]
- qqPlot(descr=paste(myTitle,'DESeq2 adj p qq plot'),pvector=rDESeq\$padj,outpdf='DESeq2_qqplot.pdf')
- cat("# DESeq top 50\n")
- print.noquote(srDESeq[1:50,])
- write.table(srDESeq,file=out_DESeq2, quote=FALSE, sep="\t",row.names=F)
- topresults.DESeq = rDESeq[which(rDESeq\$padj < fdrthresh), ]
- DESeqcountsindex = which(allgenes %in% rownames(topresults.DESeq))
- DESeqcounts = rep(0, length(allgenes))
- DESeqcounts[DESeqcountsindex] = 1
- pdf("DESeq2_dispersion_estimates.pdf")
- plotDispEsts(resDESeq)
- dev.off()
- ysmall = abs(min(rDESeq\$log2FoldChange))
- ybig = abs(max(rDESeq\$log2FoldChange))
- ylimit = min(4,ysmall,ybig)
- pdf("DESeq2_MA_plot.pdf")
- plotMA(resDESeq,main=paste(myTitle,"DESeq2 MA plot"),ylim=c(-ylimit,ylimit))
- dev.off()
- rlogres = rlogTransformation(resDESeq)
- sampledists = dist( t( assay(rlogres) ) )
- sdmat = as.matrix(sampledists)
- pdf("DESeq2_sample_distance_plot.pdf")
- heatmap.2(sdmat,trace="none",main=paste(myTitle,"DESeq2 sample distances"),
- col = colorRampPalette( rev(brewer.pal(9, "RdBu")) )(255))
- dev.off()
- ###outpdfname="DESeq2_top50_heatmap.pdf"
- ###hmap2(sresDESeq,nsamp=50,TName=TName,group=group,outpdfname=outpdfname,myTitle=paste('DESeq2 vst rlog Heatmap',myTitle))
- sink()
- result = try( (ppca = plotPCA( varianceStabilizingTransformation(deSeqDatdisp,blind=T), intgroup=c("Rx","Name")) ) )
- if ("try-error" %in% class(result)) {
- print.noquote('DESeq2 plotPCA failed.')
- } else {
- pdf("DESeq2_PCA_plot.pdf")
- #### wtf - print? Seems needed to get this to work
- print(ppca)
- dev.off()
- }
- }
-
- if (doVoom == T) {
- sink('VOOM.log')
- if (doedgeR == F) {
- #### Setup DGEList object
- DGEList = DGEList(counts=workCM, group = group)
- DGEList = calcNormFactors(DGEList)
- DGEList = estimateGLMCommonDisp(DGEList,mydesign)
- DGEList = estimateGLMTrendedDisp(DGEList,mydesign)
- DGEList = estimateGLMTagwiseDisp(DGEList,mydesign)
- DGEList = estimateGLMTagwiseDisp(DGEList,mydesign)
- norm.factor = DGEList\$samples\$norm.factors
- }
- pdf("VOOM_mean_variance_plot.pdf")
- dat.voomed = voom(DGEList, mydesign, plot = TRUE, lib.size = colSums(workCM) * norm.factor)
- dev.off()
- # Use limma to fit data
- fit = lmFit(dat.voomed, mydesign)
- fit = eBayes(fit)
- rvoom = topTable(fit, coef = length(colnames(mydesign)), adj = fdrtype, n = Inf, sort="none")
- qqPlot(descr=paste(myTitle,'VOOM-limma adj p QQ plot'),pvector=rvoom\$adj.P.Val,outpdf='VOOM_qqplot.pdf')
- rownames(rvoom) = rownames(workCM)
- rvoom = cbind(rvoom,NReads=cmrowsums,URL=contigurls)
- srvoom = rvoom[order(rvoom\$P.Value),]
- cat("# VOOM top 50\n")
- print(srvoom[1:50,])
- write.table(srvoom,file=out_VOOM, quote=FALSE, sep="\t",row.names=F)
- # Use an FDR cutoff to find interesting samples for edgeR, DESeq and voom/limma
- topresults.voom = rvoom[which(rvoom\$adj.P.Val < fdrthresh), ]
- voomcountsindex = which(allgenes %in% topresults.voom\$ID)
- voomcounts = rep(0, length(allgenes))
- voomcounts[voomcountsindex] = 1
- sink()
- }
-
- if (doCamera) {
- doGSEA(y=DGEList,design=mydesign,histgmt=histgmt,bigmt=bigmt,ntest=20,myTitle=myTitle,
- outfname=paste(mt,"GSEA.xls",sep="_"),fdrthresh=fdrthresh,fdrtype=fdrtype)
- }
-
- if ((doDESeq2==T) || (doVoom==T) || (doedgeR==T)) {
- if ((doVoom==T) && (doDESeq2==T) && (doedgeR==T)) {
- vennmain = paste(mt,'Voom,edgeR and DESeq2 overlap at FDR=',fdrthresh)
- counts.dataframe = data.frame(edgeR = edgeRcounts, DESeq2 = DESeqcounts,
- VOOM_limma = voomcounts, row.names = allgenes)
- } else if ((doDESeq2==T) && (doedgeR==T)) {
- vennmain = paste(mt,'DESeq2 and edgeR overlap at FDR=',fdrthresh)
- counts.dataframe = data.frame(edgeR = edgeRcounts, DESeq2 = DESeqcounts, row.names = allgenes)
- } else if ((doVoom==T) && (doedgeR==T)) {
- vennmain = paste(mt,'Voom and edgeR overlap at FDR=',fdrthresh)
- counts.dataframe = data.frame(edgeR = edgeRcounts, VOOM_limma = voomcounts, row.names = allgenes)
- }
-
- if (nrow(counts.dataframe > 1)) {
- counts.venn = vennCounts(counts.dataframe)
- vennf = "Venn_significant_genes_overlap.pdf"
- pdf(vennf)
- vennDiagram(counts.venn,main=vennmain,col="maroon")
- dev.off()
- }
- } #### doDESeq2 or doVoom
-
-}
-#### Done
-
-###sink(stdout(),append=T,type="message")
-builtin_gmt = ""
-history_gmt = ""
-history_gmt_name = ""
-out_edgeR = F
-out_DESeq2 = F
-out_VOOM = "$out_VOOM"
-doDESeq2 = $DESeq2.doDESeq2 # make these T or F
-doVoom = $doVoom
-doCamera = $camera.doCamera
-doedgeR = $edgeR.doedgeR
-edgeR_priordf = 0
-
-
-#if $doVoom == "T":
- out_VOOM = "$out_VOOM"
-#end if
-
-#if $DESeq2.doDESeq2 == "T":
- out_DESeq2 = "$out_DESeq2"
- DESeq_fitType = "$DESeq2.DESeq_fitType"
-#end if
-
-#if $edgeR.doedgeR == "T":
- out_edgeR = "$out_edgeR"
- edgeR_priordf = $edgeR.edgeR_priordf
-#end if
-
-#if $camera.doCamera == 'T'
- #if $camera.gmtSource.refgmtSource == "indexed" or $camera.gmtSource.refgmtSource == "both":
- builtin_gmt = "${camera.gmtSource.builtinGMT.fields.path}"
- #end if
- #if $camera.gmtSource.refgmtSource == "history" or $camera.gmtSource.refgmtSource == "both":
- history_gmt = "${camera.gmtSource.ownGMT}"
- history_gmt_name = "${camera.gmtSource.ownGMT.name}"
- #end if
-#end if
-
-
-if (sum(c(doedgeR,doVoom,doDESeq2)) == 0)
-{
-write("No methods chosen - nothing to do! Please try again after choosing one or more methods", stderr())
-quit(save="no",status=2)
-}
-
-Out_Dir = "$html_file.files_path"
-Input = "$input1"
-TreatmentName = "$treatment_name"
-TreatmentCols = "$Treat_cols"
-ControlName = "$control_name"
-ControlCols= "$Control_cols"
-org = "$input1.dbkey"
-if (org == "") { org = "hg19"}
-fdrtype = "$fdrtype"
-fdrthresh = $fdrthresh
-useNDF = $useNDF
-fQ = $fQ # non-differential centile cutoff
-myTitle = "$title"
-sids = strsplit("$subjectids",',')
-subjects = unlist(sids)
-nsubj = length(subjects)
-TCols = as.numeric(strsplit(TreatmentCols,",")[[1]])-1
-CCols = as.numeric(strsplit(ControlCols,",")[[1]])-1
-cat('Got TCols=')
-cat(TCols)
-cat('; CCols=')
-cat(CCols)
-cat('\n')
-useCols = c(TCols,CCols)
-if (file.exists(Out_Dir) == F) dir.create(Out_Dir)
-Count_Matrix = read.table(Input,header=T,row.names=1,sep='\t') #Load tab file assume header
-snames = colnames(Count_Matrix)
-nsamples = length(snames)
-if (nsubj > 0 & nsubj != nsamples) {
-options("show.error.messages"=T)
-mess = paste('Fatal error: Supplied subject id list',paste(subjects,collapse=','),
- 'has length',nsubj,'but there are',nsamples,'samples',paste(snames,collapse=','))
-write(mess, stderr())
-quit(save="no",status=4)
-}
-if (length(subjects) != 0) {subjects = subjects[useCols]}
-Count_Matrix = Count_Matrix[,useCols] ### reorder columns
-rn = rownames(Count_Matrix)
-islib = rn %in% c('librarySize','NotInBedRegions')
-LibSizes = Count_Matrix[subset(rn,islib),][1] # take first
-Count_Matrix = Count_Matrix[subset(rn,! islib),]
-group = c(rep(TreatmentName,length(TCols)), rep(ControlName,length(CCols)) ) #Build a group descriptor
-group = factor(group, levels=c(ControlName,TreatmentName))
-colnames(Count_Matrix) = paste(group,colnames(Count_Matrix),sep="_") #Relable columns
-results = edgeIt(Count_Matrix=Count_Matrix,group=group, out_edgeR=out_edgeR, out_VOOM=out_VOOM, out_DESeq2=out_DESeq2,
- fdrtype='BH',mydesign=NULL,priordf=edgeR_priordf,fdrthresh=fdrthresh,outputdir='.',
- myTitle=myTitle,useNDF=F,libSize=c(),filterquantile=fQ,subjects=subjects,
- doDESeq2=doDESeq2,doVoom=doVoom,doCamera=doCamera,doedgeR=doedgeR,org=org,
- histgmt=history_gmt,bigmt=builtin_gmt,DESeq_fitType=DESeq_fitType)
-sessionInfo()
-]]>
-</configfile>
-</configfiles>
-<help>
-
-**What it does**
-
-Allows short read sequence counts from controlled experiments to be analysed for differentially expressed genes.
-Optionally adds a term for subject if not all samples are independent or if some other factor needs to be blocked in the design.
-
-**Input**
-
-Requires a count matrix as a tabular file. These are best made using the companion HTSeq_ based counter Galaxy wrapper
-and your fave gene model to generate inputs. Each row is a genomic feature (gene or exon eg) and each column the
-non-negative integer count of reads from one sample overlapping the feature.
-The matrix must have a header row uniquely identifying the source samples, and unique row names in
-the first column. Typically the row names are gene symbols or probe ids for downstream use in GSEA and other methods.
-
-**Specifying comparisons**
-
-This is basically dumbed down for two factors - case vs control.
-
-More complex interfaces are possible but painful at present.
-Probably need to specify a phenotype file to do this better.
-Work in progress. Send code.
-
-If you have (eg) paired samples and wish to include a term in the GLM to account for some other factor (subject in the case of paired samples),
-put a comma separated list of indicators for every sample (whether modelled or not!) indicating (eg) the subject number or
-A list of integers, one for each subject or an empty string if samples are all independent.
-If not empty, there must be exactly as many integers in the supplied integer list as there are columns (samples) in the count matrix.
-Integers for samples that are not in the analysis *must* be present in the string as filler even if not used.
-
-So if you have 2 pairs out of 6 samples, you need to put in unique integers for the unpaired ones
-eg if you had 6 samples with the first two independent but the second and third pairs each being from independent subjects. you might use
-8,9,1,1,2,2
-as subject IDs to indicate two paired samples from the same subject in columns 3/4 and 5/6
-
-**Methods available**
-
-You can run 3 popular Bioconductor packages available for count data.
-
-edgeR - see edgeR_ for details
-
-VOOM/limma - see limma_VOOM_ for details
-
-DESeq2 - see DESeq2_ for details
-
-and optionally camera in edgeR which works better if MSigDB is installed.
-
-**Outputs**
-
-Some helpful plots and analysis results. Note that most of these are produced using R code
-suggested by the excellent documentation and vignettes for the Bioconductor
-packages invoked. The Tool Factory is used to automatically lay these out for you to enjoy.
-
-**Note on Voom**
-
-The voom from limma version 3.16.6 help in R includes this from the authors - but you should read the paper to interpret this method.
-
-This function is intended to process RNA-Seq or ChIP-Seq data prior to linear modelling in limma.
-
-voom is an acronym for mean-variance modelling at the observational level.
-The key concern is to estimate the mean-variance relationship in the data, then use this to compute appropriate weights for each observation.
-Count data almost show non-trivial mean-variance relationships. Raw counts show increasing variance with increasing count size, while log-counts typically show a decreasing mean-variance trend.
-This function estimates the mean-variance trend for log-counts, then assigns a weight to each observation based on its predicted variance.
-The weights are then used in the linear modelling process to adjust for heteroscedasticity.
-
-In an experiment, a count value is observed for each tag in each sample. A tag-wise mean-variance trend is computed using lowess.
-The tag-wise mean is the mean log2 count with an offset of 0.5, across samples for a given tag.
-The tag-wise variance is the quarter-root-variance of normalized log2 counts per million values with an offset of 0.5, across samples for a given tag.
-Tags with zero counts across all samples are not included in the lowess fit. Optional normalization is performed using normalizeBetweenArrays.
-Using fitted values of log2 counts from a linear model fit by lmFit, variances from the mean-variance trend were interpolated for each observation.
-This was carried out by approxfun. Inverse variance weights can be used to correct for mean-variance trend in the count data.
-
-
-Author(s)
-
-Charity Law and Gordon Smyth
-
-References
-
-Law, CW (2013). Precision weights for gene expression analysis. PhD Thesis. University of Melbourne, Australia.
-
-Law, CW, Chen, Y, Shi, W, Smyth, GK (2013). Voom! Precision weights unlock linear model analysis tools for RNA-seq read counts.
-Technical Report 1 May 2013, Bioinformatics Division, Walter and Eliza Hall Institute of Medical Reseach, Melbourne, Australia.
-http://www.statsci.org/smyth/pubs/VoomPreprint.pdf
-
-See Also
-
-A voom case study is given in the edgeR User's Guide.
-
-vooma is a similar function but for microarrays instead of RNA-seq.
-
-
-***old rant on changes to Bioconductor package variable names between versions***
-
-The edgeR authors made a small cosmetic change in the name of one important variable (from p.value to PValue)
-breaking this and all other code that assumed the old name for this variable,
-between edgeR2.4.4 and 2.4.6 (the version for R 2.14 as at the time of writing).
-This means that all code using edgeR is sensitive to the version. I think this was a very unwise thing
-to do because it wasted hours of my time to track down and will similarly cost other edgeR users dearly
-when their old scripts break. This tool currently now works with 2.4.6.
-
-**Note on prior.N**
-
-http://seqanswers.com/forums/showthread.php?t=5591 says:
-
-*prior.n*
-
-The value for prior.n determines the amount of smoothing of tagwise dispersions towards the common dispersion.
-You can think of it as like a "weight" for the common value. (It is actually the weight for the common likelihood
-in the weighted likelihood equation). The larger the value for prior.n, the more smoothing, i.e. the closer your
-tagwise dispersion estimates will be to the common dispersion. If you use a prior.n of 1, then that gives the
-common likelihood the weight of one observation.
-
-In answer to your question, it is a good thing to squeeze the tagwise dispersions towards a common value,
-or else you will be using very unreliable estimates of the dispersion. I would not recommend using the value that
-you obtained from estimateSmoothing()---this is far too small and would result in virtually no moderation
-(squeezing) of the tagwise dispersions. How many samples do you have in your experiment?
-What is the experimental design? If you have few samples (less than 6) then I would suggest a prior.n of at least 10.
-If you have more samples, then the tagwise dispersion estimates will be more reliable,
-so you could consider using a smaller prior.n, although I would hesitate to use a prior.n less than 5.
-
-
-From Bioconductor Digest, Vol 118, Issue 5, Gordon writes:
-
-Dear Dorota,
-
-The important settings are prior.df and trend.
-
-prior.n and prior.df are related through prior.df = prior.n * residual.df,
-and your experiment has residual.df = 36 - 12 = 24. So the old setting of
-prior.n=10 is equivalent for your data to prior.df = 240, a very large
-value. Going the other way, the new setting of prior.df=10 is equivalent
-to prior.n=10/24.
-
-To recover old results with the current software you would use
-
- estimateTagwiseDisp(object, prior.df=240, trend="none")
-
-To get the new default from old software you would use
-
- estimateTagwiseDisp(object, prior.n=10/24, trend=TRUE)
-
-Actually the old trend method is equivalent to trend="loess" in the new
-software. You should use plotBCV(object) to see whether a trend is
-required.
-
-Note you could also use
-
- prior.n = getPriorN(object, prior.df=10)
-
-to map between prior.df and prior.n.
-
-----
-
-**Attributions**
-
-edgeR - edgeR_
-
-VOOM/limma - limma_VOOM_
-
-DESeq2 - DESeq2_ for details
-
-See above for Bioconductor package documentation for packages exposed in Galaxy by this tool and app store package.
-
-Galaxy_ (that's what you are using right now!) for gluing everything together
-
-Otherwise, all code and documentation comprising this tool was written by Ross Lazarus and is
-licensed to you under the LGPL_ like other rgenetics artefacts
-
-.. _LGPL: http://www.gnu.org/copyleft/lesser.html
-.. _HTSeq: http://www-huber.embl.de/users/anders/HTSeq/doc/index.html
-.. _edgeR: http://www.bioconductor.org/packages/release/bioc/html/edgeR.html
-.. _DESeq2: http://www.bioconductor.org/packages/release/bioc/html/DESeq2.html
-.. _limma_VOOM: http://www.bioconductor.org/packages/release/bioc/html/limma.html
-.. _Galaxy: http://getgalaxy.org
-</help>
-
-</tool>
-
-
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/2c281898882a/
Changeset: 2c281898882a
User: fubar
Date: 2013-10-08 01:52:21
Summary: backout -r 10800
Affected #: 4 files
diff -r 962d981799cf7db1fbc5dbd0e964a93771703bb7 -r 2c281898882a3c35713166e489667fe084ce5413 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -109,7 +109,6 @@
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
self.allow_user_deletion = string_as_bool( kwargs.get( "allow_user_deletion", "False" ) )
self.allow_user_dataset_purge = string_as_bool( kwargs.get( "allow_user_dataset_purge", "False" ) )
- self.use_data_id_on_string = string_as_bool( kwargs.get( "use_data_id_on_string", "False" ) )
self.allow_user_impersonation = string_as_bool( kwargs.get( "allow_user_impersonation", "False" ) )
self.new_user_dataset_access_role_default_private = string_as_bool( kwargs.get( "new_user_dataset_access_role_default_private", "False" ) )
self.collect_outputs_from = [ x.strip() for x in kwargs.get( 'collect_outputs_from', 'new_file_path,job_working_directory' ).lower().split(',') ]
diff -r 962d981799cf7db1fbc5dbd0e964a93771703bb7 -r 2c281898882a3c35713166e489667fe084ce5413 lib/galaxy/tools/actions/__init__.py
--- a/lib/galaxy/tools/actions/__init__.py
+++ b/lib/galaxy/tools/actions/__init__.py
@@ -1,6 +1,5 @@
import os
import galaxy.tools
-import re
from galaxy.exceptions import ObjectInvalid
from galaxy.model import LibraryDatasetDatasetAssociation
@@ -192,20 +191,14 @@
data = data.to_history_dataset_association( None )
inp_data[name] = data
-# else: # HDA
-# if data.hid:
-# input_names.append( 'data %s' % data.hid )
+ else: # HDA
+ if data.hid:
+ input_names.append( 'data %s' % data.hid )
input_ext = data.ext
if data.dbkey not in [None, '?']:
input_dbkey = data.dbkey
- data_name_sane = re.sub('[^a-zA-Z0-9_]+', '', data.name)
- if not trans.app.config.use_data_id_on_string:
- # we want names in our on_strings not numbers
- input_names.append(data_name_sane)
- else:
- if data.hid:
- input_names.append('data %s' % data.hid)
+
# Collect chromInfo dataset and add as parameters to incoming
db_datasets = {}
db_dataset = trans.db_dataset_for( input_dbkey )
@@ -239,14 +232,11 @@
if len( input_names ) == 1:
on_text = input_names[0]
elif len( input_names ) == 2:
- #on_text = '%s and %s' % tuple(input_names[0:2])
- on_text = '%s_%s' % tuple(input_names[0:2])
+ on_text = '%s and %s' % tuple(input_names[0:2])
elif len( input_names ) == 3:
- #on_text = '%s, %s, and %s' % tuple(input_names[0:3])
- on_text = '%s_%s_%s' % tuple(input_names[0:3])
+ on_text = '%s, %s, and %s' % tuple(input_names[0:3])
elif len( input_names ) > 3:
- #on_text = '%s, %s, and others' % tuple(input_names[0:2])
- on_text = '%s_%s_and_others' % tuple(input_names[0:2])
+ on_text = '%s, %s, and others' % tuple(input_names[0:2])
else:
on_text = ""
# Add the dbkey to the incoming parameters
diff -r 962d981799cf7db1fbc5dbd0e964a93771703bb7 -r 2c281898882a3c35713166e489667fe084ce5413 tools/ngs_rna/tophat2_wrapper.xml
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -273,19 +273,19 @@
</stdio><outputs>
- <data format="tabular" name="fusions" label="${tool.name} on ${on_string}: fusions" from_work_dir="tophat_out/fusions.out">
+ <data format="tabular" name="fusions" label="${on_string}_tophat2fus.xls" from_work_dir="tophat_out/fusions.out"><filter>(params['settingsType'] == 'full' and params['fusion_search']['do_search'] == 'Yes')</filter></data>
- <data format="bed" name="insertions" label="${tool.name} on ${on_string}: insertions" from_work_dir="tophat_out/insertions.bed">
+ <data format="bed" name="insertions" label="${on_string}_tophat2ins.bed" from_work_dir="tophat_out/insertions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="deletions" label="${tool.name} on ${on_string}: deletions" from_work_dir="tophat_out/deletions.bed">
+ <data format="bed" name="deletions" label="${on_string}_tophhat2del.bed" from_work_dir="tophat_out/deletions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="junctions" label="${tool.name} on ${on_string}: splice junctions" from_work_dir="tophat_out/junctions.bed">
+ <data format="bed" name="junctions" label="${on_string}tophat2sj.bed" from_work_dir="tophat_out/junctions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bam" name="accepted_hits" label="${tool.name} on ${on_string}: accepted_hits" from_work_dir="tophat_out/accepted_hits.bam">
+ <data format="bam" name="accepted_hits" label="${on_string}tophat2hits.bam" from_work_dir="tophat_out/accepted_hits.bam"><expand macro="dbKeyActions" /></data></outputs>
diff -r 962d981799cf7db1fbc5dbd0e964a93771703bb7 -r 2c281898882a3c35713166e489667fe084ce5413 tools/sr_mapping/bowtie2_wrapper.xml
--- a/tools/sr_mapping/bowtie2_wrapper.xml
+++ b/tools/sr_mapping/bowtie2_wrapper.xml
@@ -113,6 +113,8 @@
</stdio><inputs>
+ <param name="jobtitle" type="text" value="bowtie2" label="Memorable short reminder of this job's importance for outputs" />
+
<!-- single/paired --><conditional name="library"><param name="type" type="select" label="Is this library mate-paired?">
@@ -216,7 +218,7 @@
<!-- define outputs --><outputs>
- <data format="fastqsanger" name="output_unaligned_reads_l" label="${tool.name} on ${on_string}: unaligned reads (L)" >
+ <data format="fastqsanger" name="output_unaligned_reads_l" label="${on_string}_{jobtitle}_unaligL.fastq" ><filter>unaligned_file is True</filter><actions><action type="format">
@@ -224,7 +226,7 @@
</action></actions></data>
- <data format="fastqsanger" name="output_unaligned_reads_r" label="${tool.name} on ${on_string}: unaligned reads (R)">
+ <data format="fastqsanger" name="output_unaligned_reads_r" label="${on_string}_{jobtitle}_unaligR.fastq)"><filter>library['type'] == "paired" and unaligned_file is True</filter><actions><action type="format">
@@ -232,7 +234,7 @@
</action></actions></data>
- <data format="bam" name="output" label="${tool.name} on ${on_string}: aligned reads">
+ <data format="bam" name="output" label="${tool.name} on ${on_string}_{jobtitle}_aligned.bam"><actions><conditional name="reference_genome.source"><when value="indexed">
https://bitbucket.org/galaxy/galaxy-central/commits/6a39fac59cc0/
Changeset: 6a39fac59cc0
User: fubar
Date: 2013-10-08 01:53:45
Summary: backout -r 10802
Affected #: 2 files
diff -r 2c281898882a3c35713166e489667fe084ce5413 -r 6a39fac59cc0cd2d0485ec5c6d5af273e8b7d78d lib/galaxy/tools/parameters/output.py
--- a/lib/galaxy/tools/parameters/output.py
+++ b/lib/galaxy/tools/parameters/output.py
@@ -65,7 +65,7 @@
def is_case( self, output_dataset, other_values ):
ref = self.get_ref( output_dataset, other_values )
return bool( str( ref ) == self.value )
-
+
class DatatypeIsInstanceToolOutputActionConditionalWhen( ToolOutputActionConditionalWhen ):
tag = "when datatype_isinstance"
def __init__( self, parent, config_elem, value ):
@@ -215,11 +215,12 @@
self.offset = elem.get( 'offset', -1 )
self.offset = int( self.offset )
else:
+ self.options = []
self.missing_tool_data_table_name = self.name
def get_value( self, other_values ):
- try:
+ if self.options:
options = self.options
- except:
+ else:
options = []
for filter in self.filters:
options = filter.filter_options( options, other_values )
@@ -248,7 +249,7 @@
def __init__( self, parent, elem ):
super( FormatToolOutputAction, self ).__init__( parent, elem )
self.default = elem.get( 'default', None )
-
+
def apply_action( self, output_dataset, other_values ):
value = self.option.get_value( other_values )
if value is None and self.default is not None:
@@ -431,7 +432,7 @@
value = str( getattr( ref.metadata, self.name ) )
rval = []
for fields in options:
- if self.keep == ( self.compare( fields[self.column], value ) ):
+ if self.keep == ( self.compare( fields[self.column], value ) ):
rval.append( fields )
return rval
@@ -452,7 +453,7 @@
value = self.cast( value )
except:
value = False #unable to cast or access value; treat as false
- if self.keep == bool( value ):
+ if self.keep == bool( value ):
rval.append( fields )
return rval
@@ -480,7 +481,7 @@
option_types = {}
for option_type in [ NullToolOutputActionOption, FromFileToolOutputActionOption, FromParamToolOutputActionOption, FromDataTableOutputActionOption ]:
option_types[ option_type.tag ] = option_type
-
+
filter_types = {}
for filter_type in [ ParamValueToolOutputActionOptionFilter, InsertColumnToolOutputActionOptionFilter, MultipleSplitterFilter, ColumnStripFilter, MetadataValueFilter, BooleanFilter, StringFunctionFilter, ColumnReplaceFilter ]:
filter_types[ filter_type.tag ] = filter_type
@@ -524,5 +525,5 @@
def compare_re_search( value1, value2 ):
#checks pattern=value2 in value1
return bool( re.search( value2, value1 ) )
-
+
compare_types = { 'eq':compare_eq, 'neq':compare_neq, 'gt':compare_gt, 'gte':compare_gte, 'lt':compare_lt, 'lte':compare_lte, 'in':compare_in, 'startswith':compare_startswith, 'endswith':compare_endswith, "re_search":compare_re_search }
diff -r 2c281898882a3c35713166e489667fe084ce5413 -r 6a39fac59cc0cd2d0485ec5c6d5af273e8b7d78d tool-data/bowtie2_indices.loc.sample
--- a/tool-data/bowtie2_indices.loc.sample
+++ b/tool-data/bowtie2_indices.loc.sample
@@ -1,37 +1,37 @@
-#This is a sample file distributed with Galaxy that enables tools
-#to use a directory of Bowtie2 indexed sequences data files. You will
-#need to create these data files and then create a bowtie_indices.loc
-#file similar to this one (store it in this directory) that points to
-#the directories in which those files are stored. The bowtie2_indices.loc
-#file has this format (longer white space characters are TAB characters):
+# bowtie2_indices.loc.sample
+# This is a *.loc.sample file distributed with Galaxy that enables tools
+# to use a directory of indexed data files. This one is for Bowtie2 and Tophat2.
+# See the wiki: http://wiki.galaxyproject.org/Admin/NGS%20Local%20Setup
+# First create these data files and save them in your own data directory structure.
+# Then, create a bowtie_indices.loc file to use those indexes with tools.
+# Copy this file, save it with the same name (minus the .sample),
+# follow the format examples, and store the result in this directory.
+# The file should include an one line entry for each index set.
+# The path points to the "basename" for the set, not a specific file.
+# It has four text columns seperated by TABS.
#
-#<unique_build_id><dbkey><display_name><file_base_path>
+# <unique_build_id><dbkey><display_name><file_base_path>
#
-#So, for example, if you had hg18 indexed stored in
-#/depot/data2/galaxy/bowtie2/hg18/,
-#then the bowtie2_indices.loc entry would look like this:
+# So, for example, if you had hg18 indexes stored in:
#
-#hg18 hg18 hg18 /depot/data2/galaxy/bowtie2/hg18/hg18
+# /depot/data2/galaxy/hg19/bowtie2/
#
-#and your /depot/data2/galaxy/bowtie2/hg18/ directory
-#would contain hg18.*.ebwt files:
+# containing hg19 genome and hg19.*.bt2 files, such as:
+# -rw-rw-r-- 1 james james 914M Feb 10 18:56 hg19canon.fa
+# -rw-rw-r-- 1 james james 914M Feb 10 18:56 hg19canon.1.bt2
+# -rw-rw-r-- 1 james james 683M Feb 10 18:56 hg19canon.2.bt2
+# -rw-rw-r-- 1 james james 3.3K Feb 10 16:54 hg19canon.3.bt2
+# -rw-rw-r-- 1 james james 683M Feb 10 16:54 hg19canon.4.bt2
+# -rw-rw-r-- 1 james james 914M Feb 10 20:45 hg19canon.rev.1.bt2
+# -rw-rw-r-- 1 james james 683M Feb 10 20:45 hg19canon.rev.2.bt2
#
-#-rw-r--r-- 1 james universe 830134 2005-09-13 10:12 hg18.1.ebwt
-#-rw-r--r-- 1 james universe 527388 2005-09-13 10:12 hg18.2.ebwt
-#-rw-r--r-- 1 james universe 269808 2005-09-13 10:12 hg18.3.ebwt
-#...etc...
+# then the bowtie2_indices.loc entry could look like this:
#
-#Your bowtie2_indices.loc file should include an entry per line for each
-#index set you have stored. The "file" in the path does not actually
-#exist, but it is the prefix for the actual index files. For example:
+#hg19 hg19 Human (hg19) /depot/data2/galaxy/hg19/bowtie2/hg19canon
#
-#hg18canon hg18 hg18 Canonical /depot/data2/galaxy/bowtie2/hg18/hg18canon
-#hg18full hg18 hg18 Full /depot/data2/galaxy/bowtie2/hg18/hg18full
-#/orig/path/hg19 hg19 hg19 /depot/data2/galaxy/bowtie2/hg19/hg19
-#...etc...
+#More examples:
#
-#Note that for backwards compatibility with workflows, the unique ID of
-#an entry must be the path that was in the original loc file, because that
-#is the value stored in the workflow for that parameter. That is why the
-#hg19 entry above looks odd. New genomes can be better-looking.
+#mm10 mm10 Mouse (mm10) /depot/data2/galaxy/mm10/bowtie2/mm10
+#dm3 dm3 D. melanogaster (dm3) /depot/data2/galaxy/mm10/bowtie2/dm3
#
+#
https://bitbucket.org/galaxy/galaxy-central/commits/99be46d34966/
Changeset: 99be46d34966
User: fubar
Date: 2013-10-08 01:54:45
Summary: backout -r 10806
Affected #: 1 file
diff -r 6a39fac59cc0cd2d0485ec5c6d5af273e8b7d78d -r 99be46d34966236412d0cf69198143ce71a8958d tools/sr_mapping/bowtie2_wrapper.xml
--- a/tools/sr_mapping/bowtie2_wrapper.xml
+++ b/tools/sr_mapping/bowtie2_wrapper.xml
@@ -87,23 +87,22 @@
## read group information
#if str($read_group.selection) == "yes":
#if $read_group.rgid and $read_group.rglb and $read_group.rgpl and $read_group.rgsm:
- --rg-id $read_group.rgid
- --rg LB:$read_group.rglb
- --rg PL:$read_group.rgpl
- --rg SM:$read_group.rgsm
+ --rg-id "$read_group.rgid"
+ --rg "LB:$read_group.rglb"
+ --rg "PL:$read_group.rgpl"
+ --rg "SM:$read_group.rgsm"
#end if
#end if
## view/sort and output file
- | samtools view -Su - | samtools sort -o - - > $output;
+ | samtools view -Su - | samtools sort -o - - > $output
## rename unaligned sequence files
#if $library.type == "paired" and $output_unaligned_reads_l and $output_unaligned_reads_r:
#set left = str($output_unaligned_reads_l).replace( '.dat', '.1.dat' )
#set right = str($output_unaligned_reads_l).replace( '.dat', '.2.dat' )
-
- mv $left $output_unaligned_reads_l;
- mv $right $output_unaligned_reads_r;
+ ;mv $left $output_unaligned_reads_l;
+ mv $right $output_unaligned_reads_r
#end if
</command>
@@ -218,7 +217,7 @@
<!-- define outputs --><outputs>
- <data format="fastqsanger" name="output_unaligned_reads_l" label="${on_string}_{jobtitle}_unaligL.fastq" >
+ <data format="fastqsanger" name="output_unaligned_reads_l" label="${on_string}_${jobtitle}_unaligL.fastq" ><filter>unaligned_file is True</filter><actions><action type="format">
@@ -226,7 +225,7 @@
</action></actions></data>
- <data format="fastqsanger" name="output_unaligned_reads_r" label="${on_string}_{jobtitle}_unaligR.fastq)">
+ <data format="fastqsanger" name="output_unaligned_reads_r" label="${on_string}_${jobtitle}_unaligR.fastq)"><filter>library['type'] == "paired" and unaligned_file is True</filter><actions><action type="format">
@@ -234,7 +233,7 @@
</action></actions></data>
- <data format="bam" name="output" label="${tool.name} on ${on_string}_{jobtitle}_aligned.bam">
+ <data format="bam" name="output" label="${tool.name} on ${on_string}_${jobtitle}_aligned.bam"><actions><conditional name="reference_genome.source"><when value="indexed">
@@ -256,6 +255,17 @@
</outputs><tests>
+ <test>
+ <!-- basic test on single paired default run -->
+ <param name="type" value="single"/>
+ <param name="selection" value="no"/>
+ <param name="full" value="no"/>
+ <param name="unaligned_file" value="false"/>
+ <param name="source" value="history" />
+ <param name="input_1" value="bowtie2/phix_reads.fastq" ftype="fastqsanger"/>
+ <param name="own_file" value="bowtie2/phix_genome.fasta" />
+ <output name="output" file="bowtie2/phix_mapped.bam" />
+ </test></tests><help>
https://bitbucket.org/galaxy/galaxy-central/commits/99404250db29/
Changeset: 99404250db29
User: fubar
Date: 2013-10-08 01:55:24
Summary: backout -r 10808
Affected #: 3 files
diff -r 99be46d34966236412d0cf69198143ce71a8958d -r 99404250db290eafa86f1e97e13100d9f705284a lib/galaxy/tools/actions/__init__.py
--- a/lib/galaxy/tools/actions/__init__.py
+++ b/lib/galaxy/tools/actions/__init__.py
@@ -1,5 +1,6 @@
import os
import galaxy.tools
+import re
from galaxy.exceptions import ObjectInvalid
from galaxy.model import LibraryDatasetDatasetAssociation
@@ -191,14 +192,20 @@
data = data.to_history_dataset_association( None )
inp_data[name] = data
- else: # HDA
- if data.hid:
- input_names.append( 'data %s' % data.hid )
+# else: # HDA
+# if data.hid:
+# input_names.append( 'data %s' % data.hid )
input_ext = data.ext
if data.dbkey not in [None, '?']:
input_dbkey = data.dbkey
-
+ data_name_sane = re.sub('[^a-zA-Z0-9_]+', '', data.name)
+ if trans.app.config.use_data_id_on_string:
+ # we want names in our on_strings not numbers
+ input_names.append(data_name_sane)
+ else:
+ if data.hid:
+ input_names.append('data %s' % data.hid)
# Collect chromInfo dataset and add as parameters to incoming
db_datasets = {}
db_dataset = trans.db_dataset_for( input_dbkey )
@@ -232,11 +239,14 @@
if len( input_names ) == 1:
on_text = input_names[0]
elif len( input_names ) == 2:
- on_text = '%s and %s' % tuple(input_names[0:2])
+ #on_text = '%s and %s' % tuple(input_names[0:2])
+ on_text = '%s_%s' % tuple(input_names[0:2])
elif len( input_names ) == 3:
- on_text = '%s, %s, and %s' % tuple(input_names[0:3])
+ #on_text = '%s, %s, and %s' % tuple(input_names[0:3])
+ on_text = '%s_%s_%s' % tuple(input_names[0:3])
elif len( input_names ) > 3:
- on_text = '%s, %s, and others' % tuple(input_names[0:2])
+ #on_text = '%s, %s, and others' % tuple(input_names[0:2])
+ on_text = '%s_%s_and_others' % tuple(input_names[0:2])
else:
on_text = ""
# Add the dbkey to the incoming parameters
diff -r 99be46d34966236412d0cf69198143ce71a8958d -r 99404250db290eafa86f1e97e13100d9f705284a scripts/functional_tests.py
--- a/scripts/functional_tests.py
+++ b/scripts/functional_tests.py
@@ -454,5 +454,4 @@
return 1
if __name__ == "__main__":
- print '\n\n\n\n#### SGE_ROOT=', os.environ.get('SGE_ROOT','##### no SGE_ROOT!'),'\n\n\n'
sys.exit( main() )
diff -r 99be46d34966236412d0cf69198143ce71a8958d -r 99404250db290eafa86f1e97e13100d9f705284a tool_conf.xml.sample
--- a/tool_conf.xml.sample
+++ b/tool_conf.xml.sample
@@ -1,7 +1,6 @@
<?xml version="1.0"?><toolbox><section name="Get Data" id="getext">
- <tool file="rlGAT/rlGAT.xml"/><tool file="data_source/upload.xml"/><tool file="data_source/ucsc_tablebrowser.xml" /><tool file="data_source/ucsc_tablebrowser_test.xml" />
https://bitbucket.org/galaxy/galaxy-central/commits/68b694a22c59/
Changeset: 68b694a22c59
User: fubar
Date: 2013-10-08 01:56:03
Summary: backout -r 10811
Affected #: 2 files
diff -r 99404250db290eafa86f1e97e13100d9f705284a -r 68b694a22c59b6419a568330bbd0a02186068526 run.sh
--- a/run.sh
+++ b/run.sh
@@ -1,6 +1,7 @@
#!/bin/sh
cd `dirname $0`
+
python ./scripts/check_python.py
[ $? -ne 0 ] && exit 1
diff -r 99404250db290eafa86f1e97e13100d9f705284a -r 68b694a22c59b6419a568330bbd0a02186068526 tool-data/shared/ucsc/ucsc_build_sites.txt
--- a/tool-data/shared/ucsc/ucsc_build_sites.txt
+++ b/tool-data/shared/ucsc/ucsc_build_sites.txt
@@ -1,6 +1,8 @@
#Harvested from http://genome.ucsc.edu/cgi-bin/das/dsn
-main http://genome.ucsc.edu/cgi-bin/hgTracks? latCha1,eriEur1,petMar2,papAnu2,ce6,anoCar2,ce2,rn3,loxAfr3,rn5,rn4,droYak1,oreNil2,droYak2,dp3,dp2,vicPac1,vicPac2,caeRem2,caeRem3,geoFor1,sorAra1,caePb2,gadMor1,droAna1,droAna2,triMan1,sacCer2,sacCer3,oryCun2,dasNov3,droGri1,caeJap1,anoCar1,choHof1,taeGut1,sacCer1,tupBel1,ce4,macEug2,ochPri2,gorGor3,mm8,mm7,pteVam1,micMur1,galGal3,galGal2,proCap1,ornAna1,sarHar1,equCab2,equCab1,myoLuc2,rheMac3,rheMac2,droPer1,apiMel1,droVir2,droVir1,cerSim1,dm1,ailMel1,cb3,calJac1,calJac3,melUnd1,droSim1,hg16,hg17,hg18,hg19,monDom5,monDom4,oviAri1,petMar1,oviAri3,droSec1,saiBol1,aplCal1,strPur1,mm9,braFlo1,oryLat2,susScr2,susScr3,canFam1,canFam3,canFam2,echTel2,caePb1,echTel1,ci2,ci1,dm3,papHam1,ponAbe2,ce10,mm10,xenTro1,xenTro3,xenTro2,fr3,fr2,fr1,galGal4,gasAcu1,dm2,nomLeu3,nomLeu2,nomLeu1,apiMel2,priPac1,cavPor3,panTro4,panTro1,panTro3,panTro2,danRer7,danRer6,danRer5,danRer4,danRer3,dipOrd1,tetNig2,tetNig1,droMoj2,bosTau3,bosTau2,bosTau4,musFur1,bosTau6,melGal1,droEre1,hetGla2,turTru2,hetGla1,monDom1,cb1,droMoj1,speTri2,allMis1,strPur2,otoGar3,bosTau7,tarSyr1,felCat3,chrPic1,anoGam1,felCat4,felCat5
+main http://genome.ucsc.edu/cgi-bin/hgTracks? priPac1,danRer4,mm9,mm8,droAna1,mm5,caeRem2,mm7,mm6,panTro1,dm3,panTro2,anoCar1,ce4,galGal3,galGal2,ce1,rn3,rn2,droMoj1,droMoj2,rn4,droYak1,droYak2,dp3,dp2,dm1,canFam1,danRer5,canFam2,danRer3,danRer2,ornAna1,ci2,ci1,tetNig1,bosTau1,bosTau3,bosTau2,equCab1,oryLat1,droAna2,droEre1,ponAbe2,rheMac2,sacCer1,droPer1,droSim1,monDom1,cb1,dm2,droSec1,strPur1,droVir2,droVir1,strPur2,sc1,xenTro1,droGri1,xenTro2,cb3,gasAcu1,caePb1,anoGam1,fr2,fr1,hg15,hg16,hg17,hg18,hg19,felCat3,apiMel2,monDom4,apiMel1,ce2
#Harvested from http://archaea.ucsc.edu/cgi-bin/das/dsn
-archaea http://archaea.ucsc.edu/cgi-bin/hgTracks? therSibi1,symbTher_IAM14863,haloBori1,zymoMobi_ZM4,therFusc_YX,methIgne1,methPalus1,nocaJS61,his2Viru,acidCryp_JF_5,lactLact,paraSp_UWE25,geobKaus_HTA426,rhizEtli_CFN_42,jannCCS1,syntFuma_MPOB,dichNodo_VCS1703A,eschColi_O157H7,burk383,alteMacl_ATCC27126,bradJapo,lebron_Phage,aerPer1,acinSp_ADP1,anapMarg_ST_MARIES,sulfIsla_L_D_8_5,desuPsyc_LSV54,sulfIsla_ROD_SHAPED_VIRUS_2,shewDeni,methBoon1,shewLoihPV4,igniHosp1,therTher_HB8,ferrAcid2,sulfIsla_REY15A,acidCell_11B,stapHell1,acidBoon1,salmTyph_LT2,methBurt2,pyroYaya1,haloHisp1,glucOxyd_621H,geobSulf,acidFila_VIRUS_6,soliUsit_ELLIN6076,methAeol1,procMari_CCMP1375,oenoOeni_PSU_1,alcaBork_SK2,wiggBrev,therUzon1,peloProp_DSM2379,orieTsut_BORYONG,salmEnte_PARATYPI_ATC,therOnnu1,onioYell_PHYTOPLASMA,mariAqua_VT8,heliPylo_J99,pf5,psycIngr_37,methMahi1,candPela_UBIQUE_HTCC1,myxoXant_DK_1622,salmTyph_TY2,chloChlo_CAD3,mesoFlor_L1,shewW318,pyro1860_1,closDiff_630,pyroFuri_COM1,methMaze1,shewWood,haloPhag_HF2,sulfViru_KAMCHATKA_1,ureaUrea,streCoel,polaJS66,rhodRubr_ATCC11170,rhodRHA1,pireSp,geobBemi_BEM,enteCloa_ATCC13047_uid48363,franTula_TULARENSIS,syneSp_WH8102,archProf1,methPetr_PM1,heliAcin_SHEEBA,thioCrun_XCL_2,haloHalo1,idioLoih_L2TR,xantCamp,sodaGlos_MORSITANS,coryEffi_YS_314,mycoTube_H37RV,polyQLWP,peloLute_DSM273,burkCeno_AU_1054,vibrVuln_CMCP6_1,ente638,neorSenn_MIYAYAMA,burkCepa_AMMD,methMari_C5_1,methZhil1,pyroFuma1,methBark1,geobMeta_GS15,eschColi_CFT073,metMar1,enteFaec_V583,hodgCica_DSEM,erwiCaro_ATROSEPTICA,methFerv1,igniAggr1,campJeju_81_176,pseuHalo_TAC125,chlaTrac,heliPylo_SHI470,chroViol,mycoGeni,methSp_FS406_1,therTher_HB27,sulfIsla_Y_G_57_14,uncuMeth_RCI,haloUtah1,sulfSolf_98_2,propAcne_KPA171202,acidJS42,chroSale_DSM3043,tropWhip_TW08_27,acidFila_VIRUS_2,natrPhar1,salmEnte_CHOLERAESUIS,rubrXyla_DSM9941,methVulc1,franCcI3,persMariEXH1,caldSacc_DSM8903,haloSali1,lactPlan,strePneu_R6_uid57859,siliPome_DSS_3,nitrWino_NB_255,mesoLoti,coxiBurn,acidFila_VIRUS_7,acidBact_ELLIN345,caldMaqu1,acidFila_VIRUS_3,amycMedi_U32,acidFila_VIRUS_1,therAggr1,acidFila_VIRUS_8,acidFila_VIRUS_9,methKand1,methCaps_BATH,burkCeno_HI2424,psycArct_273_4,buchSp,pyroCali1,chloTepi_TLS,bifiLong,picrTorr1,shewANA3,bdelBact,gramFors_KT0803,peloCarb,sulfIsla_ROD_SHAPED_V,haloHF1,sulfViru_STSV1,clavMich_NCPPB_382,baciSubt2,methPhag_PSIM100,therNeut1,saliTrop_CNB_440,campJeju,blocFlor,pastMult,saliRube_DSM13855,carbHydrZ2901,mycoPhag_BPS,halaJeot1,dehaEthe_195,mycoPhag_CORNDOG,pyroAbys_VIRUS_1,shewMR7,shewMR4,desuMuco1,geobUran_RF4,desuRedu_MI_1,methStad1,legiPneu_PHILADELPHIA,methSWAN1,gloeViol,moorTher_ATCC39073,acidRods_VIRUS_1,roseDeni_OCH_114,natrMaga1,xantOryz_MAFF_311018,shewHali,burkXeno_LB400,heliPylo_26695,geobFRC_32,eschColi_K_12_SUBSTR_W3110,haloArch_DL31,leptInte,methEves1,hydrY04A,tricEryt_IMS101,photLumi,peloTher_SI,therKoda1,desuAuda,lawsIntr_PHE_MN1_00,mycoSmeg_MC2_155,therBaro1,methOkin1,metaCupr1,therElon,shewPutrCN32,eschColi_O157H7_EDL93,mariMari_MCS10,methInfe1,shigFlex_2A,magnMagn_AMB_1,sulfIsla_FILAMENTOUS,erytLito_HTCC2594,coryGlut_Bielefeld,pyrAer1,thioDeni_ATCC25259,deinRadi,yersPest_CO92,therVolc1,wolbEndo_OF_DROSOPHIL,acidHosp1,shewSedi_HAW_EB3,vulcMout1,ralsSola,crimd_Phage,natrPhag_PHICH1,neisMeni_Z2491_1,shewPeal,anabVari_ATCC29413,colwPsyc_34H,xantAuto_PY2,hyphNept_ATCC15444,vibrChol1,deinGeot_DSM11300,salmEnte_ENTERITIDIS,metaSedu,mycoPhag_GILES,lactSali_UCC118,pyroOgun2,flavJohn_UW101,mannSucc_MBEL55E,haemSomn_129PT,therTena2,haloXana1,methFerv_DSM2088,koraCryp1,oceaIhey,haloTurk1,haloWals1,baciHalo,hypeButy1,agroTume_C58_UWASH,pyroIsla1,cytoHutc_ATCC33406,nitrEuro,therMari,pyroArse1,nitrMari1,vibrFisc_ES114_1,methJann1,heliPylo_G27,vibrPara1,metAce1,neisGono_FA1090_1,methConc1,chlaPneu_CWL029,strePyog_M1_GAS,haloLacu1,novoArom_DSM12444,eschColi_536,azoaSp_EBN1,sulfToko1,listInno,sulfAcid1,geobTher_NG80_2,sulfSpin_VIRUS_4,arthFB24,porpGing_W83,leifXyli_XYLI_CTCB0,campFetu_82_40,mycoPhag_Firecracker,mycoPhag_Dori,methTher1,nostSp,sphiAlas_RB2256,saccDegr_2_40,bordBron,rickBell_RML369_C,thioDeni_ATCC33889,methSmit1,alkaMeta_QYMF,sulfIsla_M_16_27,halMar1,vermEise_EF01_2,granBeth_CGDNIH1,therPend1,sulfIsla_L_S_2_15,vibrChol_O395_1,nitrOcea_ATCC19707,campJeju_RM1221,pyroSphe_VIRUS,yersPseu_IP32953,woliSucc,caldSubt1,sulfTurr_ICOSAHEDRAL,methMarb1,aquiAeol,acidSacc1,burkPseu_1106A,tremPrin_PCIT,azorCaul2,synePCC6,hydrSp_128_5R1,baumCica_HOMALODISCA,shewBalt,therTeng,alkaEhrl_MLHE_1,sulSol1,eschColi_UTI89,methHung1,desuKamc1,pediPent_ATCC25745,therGamm1,nanEqu1,shewAmaz,hydr3684,desuVulg_HILDENBOROUG,haheChej_KCTC_2396,sulfIsla_Y_N_15_51,candBloc_FLORIDANUS,haloPhag_SH1,candCars_RUDDII,burkMall_ATCC23344,burkViet_G4,acidTwot_VIRUS,methVann1,archFulg1,fusoNucl,heliHepa,sulfViru_1,sulfViru_2,syntAcid_SB,salmTyph,trepPall,neisMeni_MC58_1,syntWolf_GOETTINGEN,syneElon_PCC_7942,haemInfl_KW20,haloHalo_SL1,plasFalc1,rhodSpha_2_4_1,archVene1,rhodPalu_CGA009,stapMari1,sinoMeli,sulfYell_SS_5,archBJ1_VIRUS,bartHens_HOUSTON_1,sulfAzorFu1,methMari_C6,methMari_C7,caulCres,crocWats_WH8501_0,rhodSpha_ATCC17025,magnMC1,rhodFerr_DSM15236,hydrTher1,heliPylo_HPAG1,pyrHor1,acidBott_VIRUS,sulfIsla_M_16_4,actiPleu_L20,paraDeni_PD1222,borrBurg,pseuAeru,methRumi1,methVolt1,cenaSymb1,geobLovl_SZ,pyrFur2,saccEryt_NRRL_2338,vibrChol_MO10_1,methPetr1,pyroNA2,yersPseu_IP31758,ehrlRumi_WELGEVONDEN,methLabrZ_1,vibrVuln_YJ016_1,vulcDist1,sulfIsla_M_14_25,his1Viru,methTherPT1,methFlag_KT,therAcid1,therPetr_RKU_1,shewFrig,photProf_SS9,burkThai_E264,nitrMult_ATCC25196,methMari_XI,neisMeni_FAM18_1,hermArse,haloVolc1,desuHafn_Y51,nocaFarc_IFM10152,anaeDeha_2CP_C,methPalud1,eschColi_APEC_O1,xyleFast,leucMese_ATCC8293,bactThet_VPI_5482,brucMeli,ferrPlac1,ralsEutr_JMP134,sulfViru_RAGGED_HILLS,pyrAby1,mculMari1,dechArom_RCB,stapAure_MU50,methPhag_PSIM2,eschColi_K12,sulfYO3A,haloMuko1,aeroHydr_ATCC7966,ther4557,baciAnth_AMES,shewOnei
+archaea http://archaea.ucsc.edu/cgi-bin/hgTracks? alkaEhrl_MLHE_1,shewW318,idioLoih_L2TR,sulSol1,erwiCaro_ATROSEPTICA,symbTher_IAM14863,moorTher_ATCC39073,therFusc_YX,methHung1,bradJapo,therElon,shewPutrCN32,pediPent_ATCC25745,mariMari_MCS10,nanEqu1,baciSubt,chlaTrac,magnMagn_AMB_1,chroViol,ralsSola,acidCryp_JF_5,erytLito_HTCC2594,desuVulg_HILDENBOROUG,pyrAer1,sulfToko1,shewANA3,paraSp_UWE25,geobKaus_HTA426,rhizEtli_CFN_42,uncuMeth_RCI,candBloc_FLORIDANUS,deinRadi,yersPest_CO92,saccEryt_NRRL_2338,rhodRHA1,candCars_RUDDII,burkMall_ATCC23344,eschColi_O157H7,burk383,psycIngr_37,rhodSpha_2_4_1,wolbEndo_OF_DROSOPHIL,burkViet_G4,propAcne_KPA171202,enteFaec_V583,campJeju_81_176,acidJS42,heliPylo_26695,pseuHalo_TAC125,chroSale_DSM3043,methVann1,archFulg1,neisMeni_Z2491_1,fusoNucl,vermEise_EF01_2,anabVari_ATCC29413,tropWhip_TW08_27,heliHepa,acinSp_ADP1,anapMarg_ST_MARIES,natrPhar1,haheChej_KCTC_2396,therPetr_RKU_1,neisGono_FA1090_1,colwPsyc_34H,desuPsyc_LSV54,hyphNept_ATCC15444,vibrChol1,deinGeot_DSM11300,strePyog_M1_GAS,franCcI3,salmTyph,metaSedu,lactSali_UCC118,trepPall,neisMeni_MC58_1,syntWolf_GOETTINGEN,flavJohn_UW101,methBoon1,haemSomn_129PT,shewLoihPV4,igniHosp1,haemInfl_KW20,haloHalo_SL1,ferrAcid1,sphiAlas_RB2256,candPela_UBIQUE_HTCC1,caldSacc_DSM8903,aerPer1,lactPlan,carbHydr_Z_2901,therTher_HB8,vibrVuln_YJ016_1,rhodPalu_CGA009,acidCell_11B,siliPome_DSS_3,therVolc1,haloWals1,rubrXyla_DSM9941,shewAmaz,nocaJS61,vibrVuln_CMCP6_1,sinoMeli,ureaUrea,baciHalo,bartHens_HOUSTON_1,nitrWino_NB_255,hypeButy1,methBurt2,polaJS66,mesoLoti,methMari_C7,caulCres,neisMeni_FAM18_1,acidBact_ELLIN345,caldMaqu1,salmEnte_PARATYPI_ATC,glucOxyd_621H,cytoHutc_ATCC33406,nitrEuro,therMari,coxiBurn,woliSucc,heliPylo_HPAG1,mesoFlor_L1,pyrHor1,methAeol1,procMari_CCMP1375,pyroArse1,oenoOeni_PSU_1,alcaBork_SK2,wiggBrev,actiPleu_L20,lactLact,methJann1,paraDeni_PD1222,borrBurg,pyroIsla1,orieTsut_BORYONG,shewMR4,methKand1,methCaps_BATH,onioYell_PHYTOPLASMA,bordBron,cenaSymb1,burkCeno_HI2424,franTula_TULARENSIS,pyrFur2,mariAqua_VT8,heliPylo_J99,psycArct_273_4,vibrChol_MO10_1,vibrPara1,rickBell_RML369_C,metAce1,buchSp,ehrlRumi_WELGEVONDEN,methLabrZ_1,chlaPneu_CWL029,thioCrun_XCL_2,pyroCali1,chloTepi_TLS,stapAure_MU50,novoArom_DSM12444,magnMC1,zymoMobi_ZM4,salmTyph_TY2,chloChlo_CAD3,azoaSp_EBN1,therTher_HB27,bifiLong,picrTorr1,listInno,bdelBact,gramFors_KT0803,sulfAcid1,geobTher_NG80_2,peloCarb,ralsEutr_JMP134,mannSucc_MBEL55E,syneSp_WH8102,methTherPT1,clavMich_NCPPB_382,therAcid1,syntAcid_SB,porpGing_W83,therNeut0,leifXyli_XYLI_CTCB0,shewFrig,photProf_SS9,thioDeni_ATCC25259,methMaze1,desuRedu_MI_1,burkThai_E264,campFetu_82_40,blocFlor,jannCCS1,nitrMult_ATCC25196,streCoel,soliUsit_ELLIN6076,pastMult,saliRube_DSM13855,methTher1,nostSp,shigFlex_2A,saccDegr_2_40,oceaIhey,dehaEthe_195,rhodRubr_ATCC11170,arthFB24,shewMR7,pireSp,anaeDeha_2CP_C,haloVolc1,dichNodo_VCS1703A,tricEryt_IMS101,mycoGeni,thioDeni_ATCC33889,methSmit1,geobUran_RF4,shewDeni,halMar1,desuHafn_Y51,methStad1,granBeth_CGDNIH1,therPend1,legiPneu_PHILADELPHIA,vibrChol_O395_1,nitrOcea_ATCC19707,campJeju_RM1221,methPetr_PM1,heliAcin_SHEEBA,eschColi_APEC_O1,peloTher_SI,haloHalo1,syntFuma_MPOB,xyleFast,gloeViol,leucMese_ATCC8293,bactThet_VPI_5482,xantCamp,sodaGlos_MORSITANS,geobSulf,roseDeni_OCH_114,coryEffi_YS_314,brucMeli,mycoTube_H37RV,vibrFisc_ES114_1,pyrAby1,burkXeno_LB400,polyQLWP,stapMari1,peloLute_DSM273,burkCeno_AU_1054,shewBalt,nocaFarc_IFM10152,ente638,mculMari1,saliTrop_CNB_440,neorSenn_MIYAYAMA,aquiAeol,dechArom_RCB,myxoXant_DK_1622,burkPseu_1106A,burkCepa_AMMD,methMari_C5_1,azorCaul2,methFlag_KT,leptInte,eschColi_K12,synePCC6,baumCica_HOMALODISCA,methBark1,pseuAeru,geobMeta_GS15,eschColi_CFT073,photLumi,metMar1,hermArse,campJeju,therKoda1,aeroHydr_ATCC7966,baciAnth_AMES,shewOnei,therTeng,lawsIntr_PHE_MN1_00
#Harvested from http://genome-test.cse.ucsc.edu/cgi-bin/das/dsn
-test http://genome-test.cse.ucsc.edu/cgi-bin/hgTracks? punNye1,homIni20,danRer4,amaVit1,latCha1,eriEur1,criGri1,eriEur2,odoRosDiv1,lepWed1,papAnu2,ce6,ce5,anoCar2,ce3,ce2,chiLan1,rn3,loxAfr3,loxAfr2,simHuman,droMoj2,rn5,rn4,haeCon1,tetNig2,oreNil2,droYak2,dp3,dp2,mhcRef,vicPac1,eptFus1,caePb3,vicPac2,strPur2,caeRem3,geoFor1,caeRem1,sorAra1,oviAri1,galGal4,astMex1,musFur1,borEut13,homIni14,gadMor1,cacTus1,cacTus2,cacTus3,oryCun1,sacCer3,oryCun2,micOch1,colLiv1,caeJap3,melInc1,chlSab1,kirRefV3,kirRefV2,choHof1,droSim1,saiBol1,kirRefV5,kirRefV4,octDeg1,centromers1,droGri1,sc1,CHM1,mhcRefnoNs8,mhcRefnoNs9,dasNov2,dasNov3,taeGut1,caeJap4,taeGut2,caeSp71,equCab2,mhcRefnoNs2,mhcRefnoNs3,mhcRefnoNs4,mhcRefnoNs5,tupBel1,hlaRef2,galGal2,caeSp91,ce4,croPor0,bosTau7,bosTau6,ficAlb2,caePb2,panHod1,gorGor3,gorGor2,gorGor1,mayZeb1,mm7,pteVam1,macEug1,loxAfr1,venter1,micMur1,galGal3,mhcRefnoNs6,priPac2,proCap1,priPac1,mhcRefnoNs7,droEre1,sorAra2,mm10Strains1,ornAna1,xipMac1,anaPla1,mmtv,eutHer13,braNey1,droPer1,bioGla0,ce9,sarHar1,mhcRefnoNs,hlaRef3,simHumanMammal,hlaRef1,equCab1,simHumanPrim,cioSav2,myoLuc1,rheMac3,rheMac2,oreNil1,ailMel1,droSec1,canHg12,simRefMam,apiMel1,homIni13,nemVec1,turTru1,sacCer2,droVir2,droVir1,ce7,myoLuc2,repBase0,repBase1,dm1,susScr2,cb3,calJac1,macFas5,calJac3,dm3,triMan1,neoBri1,canFam1,conCri1,caeAng1,hg15,hg16,hg17,hg18,hg19,lotGig0,monDom5,monDom4,euaGli13,pteAle1,panPan1,petMar1,oviAri3,cb4,caeRem2,cioSav1,aplCal0,aplCal1,gliRes13,strPur1,anoCar1,dasNov1,mm9,tetNig1,mm8,braFlo1,oryLat2,caeJap2,braFlo2,lauRas13,susScr1,jacJac1,susScr3,hg19LggInv,caeJap1,canFam3,canFam2,echTel2,caePb1,echTel1,simHumanMam,ci2,ci1,canFamPoodle1,camFer1,papHam1,ce8,cerSim1,capHir1,droMoj1,oryLat1,ponAbe2,ce10,pseHum1,mm10Patch1,calMil0,caeSp111,hapBur1,cheMyd1,hg19Haps,mm10,falciparum,mhcRefnoNs24,mhcRefnoNs23,mhcRefnoNs22,mhcRefnoNs21,mhcRefnoNs20,xenTro1,xenTro3,xenTro2,xenTro7,falPer1,nonAfr13,homPan20,fr3,fr2,fr1,macEug2,caeRem4,gasAcu1,dm2,nomLeu3,nomLeu2,nomLeu1,apiMel2,melHap1,zonAlb1,tupChi1,ecoRef1,ecoRef3,ecoRef2,chrAsi1,ecoRef4,cavPor3,triCas2,panTro4,pelSin1,panTro1,droAna2,panTro3,panTro2,mhcRefnoNs18,mhcRefnoNs19,lepOcu1,hg19Patch10,strPur3,mhcRefnoNs12,mhcRefnoNs13,mhcRefnoNs10,mhcRefnoNs11,mhcRefnoNs16,mhcRefnoNs17,mhcRefnoNs14,mhcRefnoNs15,sacCer1,melUnd1,apaSpi1,danRer6,danRer5,mesAur1,danRer3,dipOrd1,eleEdw1,hg19Patch2,hg19Patch5,droAna1,hg19Patch9,bruMal1,bosTau3,bosTau2,bosTau5,bosTau4,ochPri2,ochPri3,melGal1,petMar2,rodEnt13,oryAfe1,evoSim1,priMat13,hetGla2,turTru2,repeats2,hetGla1,cb2,monDom1,cb1,speTri1,speTri2,catArr1,allMis1,allMis0,plasFalc3D7_2,araMac1,strPur4,otoGar1,otoGar3,kirRef,myoDav1,falChe1,tarSyr1,letCam1,afrOth13,danRer7,takFla1,droYak1,felCat3,orcOrc1,chrPic1,anoGam1,felCat4,felCat5
+test http://genome-test.cse.ucsc.edu/cgi-bin/hgTracks? anoCar1,ce4,ce3,ce2,ce1,loxAfr1,rn2,eschColi_O157H7_1,rn4,droYak1,heliPylo_J99_1,droYak2,dp3,dp2,caeRem2,caeRem1,oryLat1,eschColi_K12_1,homIni13,homIni14,droAna1,droAna2,oryCun1,sacCer1,heliHepa1,droGri1,sc1,dasNov1,choHof1,tupBel1,mm9,mm8,vibrChol1,mm5,mm4,mm7,mm6,mm3,mm2,rn3,venter1,galGal3,galGal2,ornAna1,equCab1,cioSav2,rheMac2,eutHer13,droPer1,droVir2,droVir1,heliPylo_26695_1,euaGli13,calJac1,campJeju1,droSim1,hg13,hg15,hg16,hg17,monDom1,monDom4,droMoj1,petMar1,droMoj2,vibrChol_MO10_1,vibrPara1,gliRes13,vibrVuln_YJ016_1,braFlo1,cioSav1,lauRas13,dm1,canFam1,canFam2,ci1,echTel1,ci2,caePb1,dm3,ponAbe2,falciparum,xenTro1,xenTro2,nonAfr13,fr2,fr1,gasAcu1,dm2,apiMel1,apiMel2,eschColi_O157H7EDL933_1,priPac1,panTro1,hg18,panTro2,campJeju_RM1221_1,canHg12,vibrChol_O395_1,vibrFisc_ES114_1,danRer5,danRer4,danRer3,danRer2,danRer1,tetNig1,afrOth13,bosTau1,eschColi_CFT073_1,bosTau3,bosTau2,bosTau4,rodEnt13,droEre1,priMat13,vibrVuln_CMCP6_1,cb2,cb3,cb1,borEut13,droSec1,felCat3,strPur1,strPur2,otoGar1,catArr1,anoGam1,triCas2
+ucla http://epigenomics.mcdb.ucla.edu/cgi-bin/hgTracks? araTha1
+psu bx main http://main.genome-browser.bx.psu.edu/cgi-bin/hgTracks? hg18,hg19,mm8,mm9
https://bitbucket.org/galaxy/galaxy-central/commits/c2561f65d6f9/
Changeset: c2561f65d6f9
User: fubar
Date: 2013-10-08 04:34:54
Summary: revert bowtie2 and tophat2 wrappers
Affected #: 2 files
diff -r 68b694a22c59b6419a568330bbd0a02186068526 -r c2561f65d6f9d181e76d60d0f7ed60e1ce94c513 tools/ngs_rna/tophat2_wrapper.xml
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -1,5 +1,5 @@
-<tool id="tophat2" name="Tophat2" version="0.5">
- <!-- Wrapper compatible with Tophat version 2.0.0 -->
+<tool id="tophat2" name="Tophat2" version="0.6">
+ <!-- Wrapper compatible with Tophat version 2.0.0+ --><description>Gapped-read mapper for RNA-seq data</description><version_command>tophat2 --version</version_command><requirements>
@@ -268,24 +268,25 @@
</inputs><stdio>
- <regex match="Exception" source="both" level="fatal" description="Tool exception"/>
+ <regex match="Exception|Error" source="both" level="fatal" description="Tool execution failed"/><regex match=".*" source="both" level="log" description="tool progress"/></stdio><outputs>
- <data format="tabular" name="fusions" label="${on_string}_tophat2fus.xls" from_work_dir="tophat_out/fusions.out">
+ <data format="txt" name="align_summary" label="${tool.name} on ${on_string}: align_summary" from_work_dir="tophat_out/align_summary.txt"/>
+ <data format="tabular" name="fusions" label="${tool.name} on ${on_string}: fusions" from_work_dir="tophat_out/fusions.out"><filter>(params['settingsType'] == 'full' and params['fusion_search']['do_search'] == 'Yes')</filter></data>
- <data format="bed" name="insertions" label="${on_string}_tophat2ins.bed" from_work_dir="tophat_out/insertions.bed">
+ <data format="bed" name="insertions" label="${tool.name} on ${on_string}: insertions" from_work_dir="tophat_out/insertions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="deletions" label="${on_string}_tophhat2del.bed" from_work_dir="tophat_out/deletions.bed">
+ <data format="bed" name="deletions" label="${tool.name} on ${on_string}: deletions" from_work_dir="tophat_out/deletions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="junctions" label="${on_string}tophat2sj.bed" from_work_dir="tophat_out/junctions.bed">
+ <data format="bed" name="junctions" label="${tool.name} on ${on_string}: splice junctions" from_work_dir="tophat_out/junctions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bam" name="accepted_hits" label="${on_string}tophat2hits.bam" from_work_dir="tophat_out/accepted_hits.bam">
+ <data format="bam" name="accepted_hits" label="${tool.name} on ${on_string}: accepted_hits" from_work_dir="tophat_out/accepted_hits.bam"><expand macro="dbKeyActions" /></data></outputs>
@@ -312,6 +313,7 @@
</actions></macro></macros>
+
<tests><!-- Test base-space single-end reads with pre-built index and preset parameters --><test>
@@ -450,7 +452,8 @@
<help>
**Tophat Overview**
-TopHat_ is a fast splice junction mapper for RNA-Seq reads. It aligns RNA-Seq reads to mammalian-sized genomes using the ultra high-throughput short read aligner Bowtie, and then analyzes the mapping results to identify splice junctions between exons. Please cite: Trapnell, C., Pachter, L. and Salzberg, S.L. TopHat: discovering splice junctions with RNA-Seq. Bioinformatics 25, 1105-1111 (2009).
+TopHat_ is a fast splice junction mapper for RNA-Seq reads. It aligns RNA-Seq reads to mammalian-sized genomes using the ultra high-throughput short read aligner Bowtie(2), and then analyzes the mapping results to identify splice junctions between exons. Please cite: Kim D, Pertea G, Trapnell C, Pimentel H, Kelley R, and Salzberg SL. TopHat2: accurate alignment
+of transcriptomes in the presence of insertions, deletions and gene fusions. Genome Biol 14:R36, 2013.
.. _Tophat: http://tophat.cbcb.umd.edu/
diff -r 68b694a22c59b6419a568330bbd0a02186068526 -r c2561f65d6f9d181e76d60d0f7ed60e1ce94c513 tools/sr_mapping/bowtie2_wrapper.xml
--- a/tools/sr_mapping/bowtie2_wrapper.xml
+++ b/tools/sr_mapping/bowtie2_wrapper.xml
@@ -95,14 +95,15 @@
#end if
## view/sort and output file
- | samtools view -Su - | samtools sort -o - - > $output
+ | samtools view -Su - | samtools sort -o - - > $output;
## rename unaligned sequence files
#if $library.type == "paired" and $output_unaligned_reads_l and $output_unaligned_reads_r:
#set left = str($output_unaligned_reads_l).replace( '.dat', '.1.dat' )
#set right = str($output_unaligned_reads_l).replace( '.dat', '.2.dat' )
- ;mv $left $output_unaligned_reads_l;
- mv $right $output_unaligned_reads_r
+
+ mv $left $output_unaligned_reads_l;
+ mv $right $output_unaligned_reads_r;
#end if
</command>
@@ -112,8 +113,6 @@
</stdio><inputs>
- <param name="jobtitle" type="text" value="bowtie2" label="Memorable short reminder of this job's importance for outputs" />
-
<!-- single/paired --><conditional name="library"><param name="type" type="select" label="Is this library mate-paired?">
@@ -217,7 +216,7 @@
<!-- define outputs --><outputs>
- <data format="fastqsanger" name="output_unaligned_reads_l" label="${on_string}_${jobtitle}_unaligL.fastq" >
+ <data format="fastqsanger" name="output_unaligned_reads_l" label="${tool.name} on ${on_string}: unaligned reads (L)" ><filter>unaligned_file is True</filter><actions><action type="format">
@@ -225,7 +224,7 @@
</action></actions></data>
- <data format="fastqsanger" name="output_unaligned_reads_r" label="${on_string}_${jobtitle}_unaligR.fastq)">
+ <data format="fastqsanger" name="output_unaligned_reads_r" label="${tool.name} on ${on_string}: unaligned reads (R)"><filter>library['type'] == "paired" and unaligned_file is True</filter><actions><action type="format">
@@ -233,7 +232,7 @@
</action></actions></data>
- <data format="bam" name="output" label="${tool.name} on ${on_string}_${jobtitle}_aligned.bam">
+ <data format="bam" name="output" label="${tool.name} on ${on_string}: aligned reads"><actions><conditional name="reference_genome.source"><when value="indexed">
https://bitbucket.org/galaxy/galaxy-central/commits/a2ffcbc614d4/
Changeset: a2ffcbc614d4
User: fubar
Date: 2013-10-08 04:39:22
Summary: restore original tools/actions/__init__.py
Affected #: 1 file
diff -r c2561f65d6f9d181e76d60d0f7ed60e1ce94c513 -r a2ffcbc614d42f343f4bab09035e771bb50b0dcc lib/galaxy/tools/actions/__init__.py
--- a/lib/galaxy/tools/actions/__init__.py
+++ b/lib/galaxy/tools/actions/__init__.py
@@ -1,6 +1,5 @@
import os
import galaxy.tools
-import re
from galaxy.exceptions import ObjectInvalid
from galaxy.model import LibraryDatasetDatasetAssociation
@@ -23,13 +22,13 @@
"""
def execute( self, tool, trans, incoming={}, set_output_hid=True ):
raise TypeError("Abstract method")
-
+
class DefaultToolAction( object ):
"""Default tool action is to run an external command"""
-
+
def collect_input_datasets( self, tool, param_values, trans ):
"""
- Collect any dataset inputs from incoming. Returns a mapping from
+ Collect any dataset inputs from incoming. Returns a mapping from
parameter name to Dataset instance for each tool parameter that is
of the DataToolParameter type.
"""
@@ -118,9 +117,9 @@
def make_dict_copy( from_dict ):
"""
Makes a copy of input dictionary from_dict such that all values that are dictionaries
- result in creation of a new dictionary ( a sort of deepcopy ). We may need to handle
- other complex types ( e.g., lists, etc ), but not sure...
- Yes, we need to handle lists (and now are)...
+ result in creation of a new dictionary ( a sort of deepcopy ). We may need to handle
+ other complex types ( e.g., lists, etc ), but not sure...
+ Yes, we need to handle lists (and now are)...
"""
copy_from_dict = {}
for key, value in from_dict.items():
@@ -169,11 +168,11 @@
input_values[ input.name ] = galaxy.tools.SelectToolParameterWrapper( input, input_values[ input.name ], tool.app, other_values = incoming )
else:
input_values[ input.name ] = galaxy.tools.InputValueWrapper( input, input_values[ input.name ], incoming )
-
+
# Set history.
if not history:
history = tool.get_default_history_by_trans( trans, create=True )
-
+
out_data = odict()
# Collect any input datasets from the incoming parameters
inp_data = self.collect_input_datasets( tool, incoming, trans )
@@ -186,26 +185,20 @@
if not data:
data = NoneDataset( datatypes_registry = trans.app.datatypes_registry )
continue
-
+
# Convert LDDA to an HDA.
if isinstance(data, LibraryDatasetDatasetAssociation):
data = data.to_history_dataset_association( None )
inp_data[name] = data
-
-# else: # HDA
-# if data.hid:
-# input_names.append( 'data %s' % data.hid )
+
+ else: # HDA
+ if data.hid:
+ input_names.append( 'data %s' % data.hid )
input_ext = data.ext
-
+
if data.dbkey not in [None, '?']:
input_dbkey = data.dbkey
- data_name_sane = re.sub('[^a-zA-Z0-9_]+', '', data.name)
- if trans.app.config.use_data_id_on_string:
- # we want names in our on_strings not numbers
- input_names.append(data_name_sane)
- else:
- if data.hid:
- input_names.append('data %s' % data.hid)
+
# Collect chromInfo dataset and add as parameters to incoming
db_datasets = {}
db_dataset = trans.db_dataset_for( input_dbkey )
@@ -221,13 +214,13 @@
if 'fasta' in custom_build_dict:
build_fasta_dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( custom_build_dict[ 'fasta' ] )
chrom_info = build_fasta_dataset.get_converted_dataset( trans, 'len' ).file_name
-
+
if not chrom_info:
# Default to built-in build.
chrom_info = os.path.join( trans.app.config.len_file_path, "%s.len" % input_dbkey )
incoming[ "chromInfo" ] = chrom_info
inp_data.update( db_datasets )
-
+
# Determine output dataset permission/roles list
existing_datasets = [ inp for inp in inp_data.values() if inp ]
if existing_datasets:
@@ -239,20 +232,17 @@
if len( input_names ) == 1:
on_text = input_names[0]
elif len( input_names ) == 2:
- #on_text = '%s and %s' % tuple(input_names[0:2])
- on_text = '%s_%s' % tuple(input_names[0:2])
+ on_text = '%s and %s' % tuple(input_names[0:2])
elif len( input_names ) == 3:
- #on_text = '%s, %s, and %s' % tuple(input_names[0:3])
- on_text = '%s_%s_%s' % tuple(input_names[0:3])
+ on_text = '%s, %s, and %s' % tuple(input_names[0:3])
elif len( input_names ) > 3:
- #on_text = '%s, %s, and others' % tuple(input_names[0:2])
- on_text = '%s_%s_and_others' % tuple(input_names[0:2])
+ on_text = '%s, %s, and others' % tuple(input_names[0:2])
else:
on_text = ""
# Add the dbkey to the incoming parameters
incoming[ "dbkey" ] = input_dbkey
params = None #wrapped params are used by change_format action and by output.label; only perform this wrapping once, as needed
- # Keep track of parent / child relationships, we'll create all the
+ # Keep track of parent / child relationships, we'll create all the
# datasets first, then create the associations
parent_to_child_pairs = []
child_dataset_names = set()
@@ -268,7 +258,7 @@
if output.parent:
parent_to_child_pairs.append( ( output.parent, name ) )
child_dataset_names.add( name )
- ## What is the following hack for? Need to document under what
+ ## What is the following hack for? Need to document under what
## conditions can the following occur? (james(a)bx.psu.edu)
# HACK: the output data has already been created
# this happens i.e. as a result of the async controller
@@ -289,7 +279,7 @@
ext = input_extension
except Exception, e:
pass
-
+
#process change_format tags
if output.change_format:
if params is None:
@@ -332,14 +322,14 @@
object_store_id = data.dataset.object_store_id # these will be the same thing after the first output
# This may not be neccesary with the new parent/child associations
data.designation = name
- # Copy metadata from one of the inputs if requested.
+ # Copy metadata from one of the inputs if requested.
if output.metadata_source:
data.init_meta( copy_from=inp_data[output.metadata_source] )
else:
data.init_meta()
# Take dbkey from LAST input
data.dbkey = str(input_dbkey)
- # Set state
+ # Set state
# FIXME: shouldn't this be NEW until the job runner changes it?
data.state = data.states.QUEUED
data.blurb = "queued"
@@ -361,7 +351,7 @@
params = make_dict_copy( incoming )
wrap_values( tool.inputs, params, skip_missing_values = not tool.check_values )
data.name = self._get_default_data_name( data, tool, on_text=on_text, trans=trans, incoming=incoming, history=history, params=params, job_params=job_params )
- # Store output
+ # Store output
out_data[ name ] = data
if output.actions:
#Apply pre-job tool-output-dataset actions; e.g. setting metadata, changing format
@@ -383,7 +373,7 @@
parent_dataset = out_data[ parent_name ]
child_dataset = out_data[ child_name ]
parent_dataset.children.append( child_dataset )
- # Store data after custom code runs
+ # Store data after custom code runs
trans.sa_session.flush()
# Create the job object
job = trans.app.model.Job()
@@ -464,7 +454,7 @@
for name in inp_data.keys():
dataset = inp_data[ name ]
redirect_url = tool.parse_redirect_url( dataset, incoming )
- # GALAXY_URL should be include in the tool params to enable the external application
+ # GALAXY_URL should be include in the tool params to enable the external application
# to send back to the current Galaxy instance
GALAXY_URL = incoming.get( 'GALAXY_URL', None )
assert GALAXY_URL is not None, "GALAXY_URL parameter missing in tool config."
https://bitbucket.org/galaxy/galaxy-central/commits/3df68022beef/
Changeset: 3df68022beef
User: fubar
Date: 2013-10-08 07:15:56
Summary: Big cleanup - remove material in tools/rgenetics accidentally added in August when a push was made from a BakerIDI repository - apologies to all. This push will revert all those changes and also removes the old rgenetics SNP tools which are now deprecated.
Affected #: 1 file
diff -r a2ffcbc614d42f343f4bab09035e771bb50b0dcc -r 3df68022beef2077798ed50a33afb03be75a841a tools/rgenetics/rgFastQC.py
--- a/tools/rgenetics/rgFastQC.py
+++ b/tools/rgenetics/rgFastQC.py
@@ -24,10 +24,29 @@
import optparse
import shutil
import tempfile
-from rgutils import getFileString
import zipfile
import gzip
+
+def getFileString(fpath, outpath):
+ """
+ format a nice file size string
+ """
+ size = ''
+ fp = os.path.join(outpath, fpath)
+ s = '? ?'
+ if os.path.isfile(fp):
+ n = float(os.path.getsize(fp))
+ if n > 2**20:
+ size = ' (%1.1f MB)' % (n/2**20)
+ elif n > 2**10:
+ size = ' (%1.1f KB)' % (n/2**10)
+ elif n > 0:
+ size = ' (%d B)' % (int(n))
+ s = '%s %s' % (fpath, size)
+ return s
+
+
class FastQC():
"""wrapper
"""
https://bitbucket.org/galaxy/galaxy-central/commits/c8b55344e779/
Changeset: c8b55344e779
User: fubar
Date: 2013-10-08 07:30:54
Summary: Proper removal of rgenetics deprecated tool wrappers
Affected #: 41 files
diff -r 3df68022beef2077798ed50a33afb03be75a841a -r c8b55344e779e8be93bae5d66f52128addd83003 tools/rgenetics/listFiles.py
--- a/tools/rgenetics/listFiles.py
+++ /dev/null
@@ -1,227 +0,0 @@
-#Provides Upload tool with access to list of available files
-import glob,sys
-import galaxy.app as thisapp
-import galaxy.util
-
-from elementtree.ElementTree import XML
-
-librepos = '/usr/local/galaxy/data/rg'
-myrepos = '/home/rerla/galaxy'
-marchinirepos = '/usr/local/galaxy/data/rg/snptest'
-
-from galaxy.tools.parameters import DataToolParameter
-
-#Provides Upload tool with access to list of available builds
-
-builds = []
-#Read build names and keys from galaxy.util
-for dbkey, build_name in galaxy.util.dbnames:
- builds.append((build_name,dbkey,False))
-
-#Return available builds
-def get_available_builds(defval='hg18'):
- for i,x in enumerate(builds):
- if x[1] == defval:
- x = list(x)
- x[2] = True
- builds[i] = tuple(x)
- return builds
-
-
-
-def get_tabular_cols( input, outformat='gg' ):
- """numeric only other than rs for strict genome graphs
- otherwise tabular. Derived from galaxy tool source around August 2007 by Ross"""
- columns = []
- seenCnames = {}
- elems = []
- colnames = ['Col%d' % x for x in range(input.metadata.columns+1)]
- strict = (outformat=='gg')
- for i, line in enumerate( file ( input.file_name ) ):
- if line and not line.startswith( '#' ):
- line = line.rstrip('\r\n')
- elems = line.split( '\t' )
-
- """
- Strict gg note:
- Since this tool requires users to select only those columns
- that contain numerical values, we'll restrict the column select
- list appropriately other than the first column which must be a marker
- """
- if len(elems) > 0:
- for col in range(1, input.metadata.columns+1):
- isFloat = False # short circuit common result
- try:
- val = float(elems[col-1])
- isFloat = True
- except:
- val = elems[col-1]
- if val:
- if i == 0: # header row
- colnames[col] = val
- if isFloat or (not strict) or (col == 1): # all in if not GG
- option = colnames[col]
- if not seenCnames.get(option,None): # new
- columns.append((option,str(col),False))
- seenCnames[option] = option
- #print 'get_tab: %d=%s. Columns=%s' % (i,line,str(columns))
- if len(columns) > 0 and i > 10:
- """
- We have our select list built, so we can break out of the outer most for loop
- """
- break
- if i == 30:
- break # Hopefully we never get here...
- for option in range(min(5,len(columns))):
- (x,y,z) = columns[option]
- columns[option] = (x,y,True)
- return columns # sorted select options
-
-def get_marchini_dir():
- """return the filesystem directory for snptest style files"""
- return marchinirepos
-
-
-def get_lib_SNPTESTCaCofiles():
- """return a list of file names - without extensions - available for caco studies
- These have a common file name with both _1 and _2 suffixes"""
- d = get_marchini_dir()
- testsuffix = '.gen_1' # glob these
- flist = glob.glob('%s/*%s' % (d,testsuffix))
- flist = [x.split(testsuffix)[0] for x in flist] # leaves with a list of file set names
- if len(flist) > 0:
- dat = [(flist[0],flist[0],True),]
- dat += [(x,x,False) for x in flist[1:]]
- else:
- dat = [('No Marchini CaCo files found in %s - convert some using the Marchini converter tool' % d,'None',True),]
- return dat
-
-def getChropt():
- """return dynamic chromosome select options
- """
- c = ['X','Y']
- c += ['%d' % x for x in range(1,23)]
- dat = [(x,x,False) for x in c]
- x,y,z = dat[3]
- dat[3] = (x,y,True)
- return dat
-
-
-def get_phecols(fname=''):
- """ return a list of phenotype columns for a multi-select list
- prototype:
- foo = ('fake - not yet implemented','not implemented','False')
- dat = [foo for x in range(5)]
- return dat
- """
- try:
- header = file(fname,'r').next().split()
- except:
- return [('get_phecols unable to open file %s' % fname,'None',False),]
- dat = [(x,x,False) for x in header]
- return dat
-
-#Return various kinds of files
-
-def get_lib_pedfiles():
- dat = glob.glob('%s/ped/*.ped' % librepos)
- dat += glob.glob('%s/ped/*.ped' % myrepos)
- dat.sort()
- if len(dat) > 0:
- dat = [x.split('.ped')[0] for x in dat]
- dat = [(x,x,'True') for x in dat]
- else:
- dat = [('No ped files - add some to %s/ped or %s/ped' % (librepos,myrepos),'None',True),]
- return dat
-
-def get_lib_phefiles():
- ext = 'phe'
- dat = glob.glob('%s/pheno/*.%s' % (librepos,ext))
- dat += glob.glob('%s/pheno/*.%s' % (myrepos,ext))
- dat.sort()
- if len(dat) > 0:
- dat = [(x,x,'False') for x in dat]
- else:
- dat = [('No %s files - add some to %s/pheno or %s/pheno' % (ext,librepos,myrepos),'None',True),]
- return dat
-
-def get_lib_bedfiles():
- dat = glob.glob('%s/plinkbed/*.bed' % librepos)
- dat += glob.glob('%s/plinkbed/*.bed' % myrepos)
- dat.sort()
- if len(dat) > 0:
- dat = [x.split('.bed')[0] for x in dat]
- dat = [(x,x,False) for x in dat]
- else:
- dat = [('No bed files - Please import some to %s/plinkbed or %s/plinkbed' % (librepos,myrepos),'None',True),]
- return dat
-
-def get_lib_fbatfiles():
- dat = glob.glob('%s/plinkfbat/*.ped' % librepos)
- dat += glob.glob('%s/plinkfbat/*.ped' % myrepos)
- dat.sort()
- if len(dat) > 0:
- dat = [(x,x,False) for x in dat]
- else:
- dat = [('No fbat bed files - Please import some to %s/plinkfbat or %s/plinkfbat' % (librepos,myrepos),'None',True),]
- return dat
-
-def get_lib_mapfiles():
- dat = glob.glob('%s/ped/*.map' % librepos)
- dat += glob.glob('%s/ped/*.map' % myrepos)
- dat.sort()
- if len(dat) > 0:
- dat = [(x,x,False) for x in dat]
- else:
- dat = [('No map files - add some to %s/ped' % librepos,'None',True),]
- return dat
-
-def get_my_pedfiles():
- dat = glob.glob('%s/*.ped' % myrepos)
- if len(dat) > 0:
- dat = [(x,x,False) for x in dat]
- else:
- dat = [('No ped files - add some to %s' % librepos,'None',True),]
- return dat
-
-def get_my_mapfiles():
- dat = glob.glob('%s/*.map' % myrepos)
- if len(dat) > 0:
- dat = [(x,x,'True') for x in dat]
- else:
- dat = [('No ped files - add some to %s' % librepos,'None',True),]
- return dat
-
-def get_lib_xlsfiles():
- dat = glob.glob('%s/*.xls' % librepos)
- if len(dat) > 0:
- dat = [(x,x,False) for x in dat]
- else:
- dat = [('No ped files - add some to %s' % librepos,'None',True),]
- return dat
-
-def get_lib_htmlfiles():
- dat = glob.glob('%s/*.html' % librepos)
- if len(dat) > 0:
- dat = [(x,x,False) for x in dat]
- else:
- dat = [('No ped files - add some to %s' % librepos,'None',True),]
- return dat
-
-def get_my_xlsfiles():
- dat = glob.glob('%s/*.xls' % myrepos)
- if len(dat) > 0:
- dat = [(x,x,False) for x in dat]
- else:
- dat = [('No ped files - add some to %s' % librepos,'None',True),]
- return dat
-
-def get_my_htmlfiles():
- dat = glob.glob('%s/*.html' % myrepos)
- if len(dat) > 0:
- dat = [(x,x,False) for x in dat]
- else:
- dat = [('No ped files - add some to %s' % librepos,'None',True),]
- return dat
-
-
diff -r 3df68022beef2077798ed50a33afb03be75a841a -r c8b55344e779e8be93bae5d66f52128addd83003 tools/rgenetics/plinkbinJZ.py
--- a/tools/rgenetics/plinkbinJZ.py
+++ /dev/null
@@ -1,868 +0,0 @@
-#!/usr/bin/env python2.4
-"""
-"""
-
-import optparse,os,subprocess,gzip,struct,time,commands
-from array import array
-
-#from AIMS import util
-#from pga import util as pgautil
-
-__FILE_ID__ = '$Id: plinkbinJZ.py,v 1.14 2009/07/13 20:16:50 rejpz Exp $'
-
-VERBOSE = True
-
-MISSING_ALLELES = set(['N', '0', '.', '-',''])
-
-AUTOSOMES = set(range(1, 23) + [str(c) for c in range(1, 23)])
-
-MAGIC_BYTE1 = '00110110'
-MAGIC_BYTE2 = '11011000'
-FORMAT_SNP_MAJOR_BYTE = '10000000'
-FORMAT_IND_MAJOR_BYTE = '00000000'
-MAGIC1 = (0, 3, 1, 2)
-MAGIC2 = (3, 1, 2, 0)
-FORMAT_SNP_MAJOR = (2, 0, 0, 0)
-FORMAT_IND_MAJOR = (0, 0, 0, 0)
-HEADER_LENGTH = 3
-
-HOM0 = 3
-HOM1 = 0
-MISS = 2
-HET = 1
-HOM0_GENO = (0, 0)
-HOM1_GENO = (1, 1)
-HET_GENO = (0, 1)
-MISS_GENO = (-9, -9)
-
-GENO_TO_GCODE = {
- HOM0_GENO: HOM0,
- HET_GENO: HET,
- HOM1_GENO: HOM1,
- MISS_GENO: MISS,
- }
-
-CHROM_REPLACE = {
- 'X': '23',
- 'Y': '24',
- 'XY': '25',
- 'MT': '26',
- 'M': '26',
-}
-
-MAP_LINE_EXCEPTION_TEXT = """
-One or more lines in the *.map file has only three fields.
-The line was:
-
-%s
-
-If you are running rgGRR through EPMP, this is usually a
-sign that you are using an old version of the map file.
-You can correct the problem by re-running Subject QC. If
-you have already tried this, please contact the developers,
-or file a bug.
-"""
-
-INT_TO_GCODE = {
- 0: array('i', (0, 0, 0, 0)), 1: array('i', (2, 0, 0, 0)), 2: array('i', (1, 0, 0, 0)), 3: array('i', (3, 0, 0, 0)),
- 4: array('i', (0, 2, 0, 0)), 5: array('i', (2, 2, 0, 0)), 6: array('i', (1, 2, 0, 0)), 7: array('i', (3, 2, 0, 0)),
- 8: array('i', (0, 1, 0, 0)), 9: array('i', (2, 1, 0, 0)), 10: array('i', (1, 1, 0, 0)), 11: array('i', (3, 1, 0, 0)),
- 12: array('i', (0, 3, 0, 0)), 13: array('i', (2, 3, 0, 0)), 14: array('i', (1, 3, 0, 0)), 15: array('i', (3, 3, 0, 0)),
- 16: array('i', (0, 0, 2, 0)), 17: array('i', (2, 0, 2, 0)), 18: array('i', (1, 0, 2, 0)), 19: array('i', (3, 0, 2, 0)),
- 20: array('i', (0, 2, 2, 0)), 21: array('i', (2, 2, 2, 0)), 22: array('i', (1, 2, 2, 0)), 23: array('i', (3, 2, 2, 0)),
- 24: array('i', (0, 1, 2, 0)), 25: array('i', (2, 1, 2, 0)), 26: array('i', (1, 1, 2, 0)), 27: array('i', (3, 1, 2, 0)),
- 28: array('i', (0, 3, 2, 0)), 29: array('i', (2, 3, 2, 0)), 30: array('i', (1, 3, 2, 0)), 31: array('i', (3, 3, 2, 0)),
- 32: array('i', (0, 0, 1, 0)), 33: array('i', (2, 0, 1, 0)), 34: array('i', (1, 0, 1, 0)), 35: array('i', (3, 0, 1, 0)),
- 36: array('i', (0, 2, 1, 0)), 37: array('i', (2, 2, 1, 0)), 38: array('i', (1, 2, 1, 0)), 39: array('i', (3, 2, 1, 0)),
- 40: array('i', (0, 1, 1, 0)), 41: array('i', (2, 1, 1, 0)), 42: array('i', (1, 1, 1, 0)), 43: array('i', (3, 1, 1, 0)),
- 44: array('i', (0, 3, 1, 0)), 45: array('i', (2, 3, 1, 0)), 46: array('i', (1, 3, 1, 0)), 47: array('i', (3, 3, 1, 0)),
- 48: array('i', (0, 0, 3, 0)), 49: array('i', (2, 0, 3, 0)), 50: array('i', (1, 0, 3, 0)), 51: array('i', (3, 0, 3, 0)),
- 52: array('i', (0, 2, 3, 0)), 53: array('i', (2, 2, 3, 0)), 54: array('i', (1, 2, 3, 0)), 55: array('i', (3, 2, 3, 0)),
- 56: array('i', (0, 1, 3, 0)), 57: array('i', (2, 1, 3, 0)), 58: array('i', (1, 1, 3, 0)), 59: array('i', (3, 1, 3, 0)),
- 60: array('i', (0, 3, 3, 0)), 61: array('i', (2, 3, 3, 0)), 62: array('i', (1, 3, 3, 0)), 63: array('i', (3, 3, 3, 0)),
- 64: array('i', (0, 0, 0, 2)), 65: array('i', (2, 0, 0, 2)), 66: array('i', (1, 0, 0, 2)), 67: array('i', (3, 0, 0, 2)),
- 68: array('i', (0, 2, 0, 2)), 69: array('i', (2, 2, 0, 2)), 70: array('i', (1, 2, 0, 2)), 71: array('i', (3, 2, 0, 2)),
- 72: array('i', (0, 1, 0, 2)), 73: array('i', (2, 1, 0, 2)), 74: array('i', (1, 1, 0, 2)), 75: array('i', (3, 1, 0, 2)),
- 76: array('i', (0, 3, 0, 2)), 77: array('i', (2, 3, 0, 2)), 78: array('i', (1, 3, 0, 2)), 79: array('i', (3, 3, 0, 2)),
- 80: array('i', (0, 0, 2, 2)), 81: array('i', (2, 0, 2, 2)), 82: array('i', (1, 0, 2, 2)), 83: array('i', (3, 0, 2, 2)),
- 84: array('i', (0, 2, 2, 2)), 85: array('i', (2, 2, 2, 2)), 86: array('i', (1, 2, 2, 2)), 87: array('i', (3, 2, 2, 2)),
- 88: array('i', (0, 1, 2, 2)), 89: array('i', (2, 1, 2, 2)), 90: array('i', (1, 1, 2, 2)), 91: array('i', (3, 1, 2, 2)),
- 92: array('i', (0, 3, 2, 2)), 93: array('i', (2, 3, 2, 2)), 94: array('i', (1, 3, 2, 2)), 95: array('i', (3, 3, 2, 2)),
- 96: array('i', (0, 0, 1, 2)), 97: array('i', (2, 0, 1, 2)), 98: array('i', (1, 0, 1, 2)), 99: array('i', (3, 0, 1, 2)),
- 100: array('i', (0, 2, 1, 2)), 101: array('i', (2, 2, 1, 2)), 102: array('i', (1, 2, 1, 2)), 103: array('i', (3, 2, 1, 2)),
- 104: array('i', (0, 1, 1, 2)), 105: array('i', (2, 1, 1, 2)), 106: array('i', (1, 1, 1, 2)), 107: array('i', (3, 1, 1, 2)),
- 108: array('i', (0, 3, 1, 2)), 109: array('i', (2, 3, 1, 2)), 110: array('i', (1, 3, 1, 2)), 111: array('i', (3, 3, 1, 2)),
- 112: array('i', (0, 0, 3, 2)), 113: array('i', (2, 0, 3, 2)), 114: array('i', (1, 0, 3, 2)), 115: array('i', (3, 0, 3, 2)),
- 116: array('i', (0, 2, 3, 2)), 117: array('i', (2, 2, 3, 2)), 118: array('i', (1, 2, 3, 2)), 119: array('i', (3, 2, 3, 2)),
- 120: array('i', (0, 1, 3, 2)), 121: array('i', (2, 1, 3, 2)), 122: array('i', (1, 1, 3, 2)), 123: array('i', (3, 1, 3, 2)),
- 124: array('i', (0, 3, 3, 2)), 125: array('i', (2, 3, 3, 2)), 126: array('i', (1, 3, 3, 2)), 127: array('i', (3, 3, 3, 2)),
- 128: array('i', (0, 0, 0, 1)), 129: array('i', (2, 0, 0, 1)), 130: array('i', (1, 0, 0, 1)), 131: array('i', (3, 0, 0, 1)),
- 132: array('i', (0, 2, 0, 1)), 133: array('i', (2, 2, 0, 1)), 134: array('i', (1, 2, 0, 1)), 135: array('i', (3, 2, 0, 1)),
- 136: array('i', (0, 1, 0, 1)), 137: array('i', (2, 1, 0, 1)), 138: array('i', (1, 1, 0, 1)), 139: array('i', (3, 1, 0, 1)),
- 140: array('i', (0, 3, 0, 1)), 141: array('i', (2, 3, 0, 1)), 142: array('i', (1, 3, 0, 1)), 143: array('i', (3, 3, 0, 1)),
- 144: array('i', (0, 0, 2, 1)), 145: array('i', (2, 0, 2, 1)), 146: array('i', (1, 0, 2, 1)), 147: array('i', (3, 0, 2, 1)),
- 148: array('i', (0, 2, 2, 1)), 149: array('i', (2, 2, 2, 1)), 150: array('i', (1, 2, 2, 1)), 151: array('i', (3, 2, 2, 1)),
- 152: array('i', (0, 1, 2, 1)), 153: array('i', (2, 1, 2, 1)), 154: array('i', (1, 1, 2, 1)), 155: array('i', (3, 1, 2, 1)),
- 156: array('i', (0, 3, 2, 1)), 157: array('i', (2, 3, 2, 1)), 158: array('i', (1, 3, 2, 1)), 159: array('i', (3, 3, 2, 1)),
- 160: array('i', (0, 0, 1, 1)), 161: array('i', (2, 0, 1, 1)), 162: array('i', (1, 0, 1, 1)), 163: array('i', (3, 0, 1, 1)),
- 164: array('i', (0, 2, 1, 1)), 165: array('i', (2, 2, 1, 1)), 166: array('i', (1, 2, 1, 1)), 167: array('i', (3, 2, 1, 1)),
- 168: array('i', (0, 1, 1, 1)), 169: array('i', (2, 1, 1, 1)), 170: array('i', (1, 1, 1, 1)), 171: array('i', (3, 1, 1, 1)),
- 172: array('i', (0, 3, 1, 1)), 173: array('i', (2, 3, 1, 1)), 174: array('i', (1, 3, 1, 1)), 175: array('i', (3, 3, 1, 1)),
- 176: array('i', (0, 0, 3, 1)), 177: array('i', (2, 0, 3, 1)), 178: array('i', (1, 0, 3, 1)), 179: array('i', (3, 0, 3, 1)),
- 180: array('i', (0, 2, 3, 1)), 181: array('i', (2, 2, 3, 1)), 182: array('i', (1, 2, 3, 1)), 183: array('i', (3, 2, 3, 1)),
- 184: array('i', (0, 1, 3, 1)), 185: array('i', (2, 1, 3, 1)), 186: array('i', (1, 1, 3, 1)), 187: array('i', (3, 1, 3, 1)),
- 188: array('i', (0, 3, 3, 1)), 189: array('i', (2, 3, 3, 1)), 190: array('i', (1, 3, 3, 1)), 191: array('i', (3, 3, 3, 1)),
- 192: array('i', (0, 0, 0, 3)), 193: array('i', (2, 0, 0, 3)), 194: array('i', (1, 0, 0, 3)), 195: array('i', (3, 0, 0, 3)),
- 196: array('i', (0, 2, 0, 3)), 197: array('i', (2, 2, 0, 3)), 198: array('i', (1, 2, 0, 3)), 199: array('i', (3, 2, 0, 3)),
- 200: array('i', (0, 1, 0, 3)), 201: array('i', (2, 1, 0, 3)), 202: array('i', (1, 1, 0, 3)), 203: array('i', (3, 1, 0, 3)),
- 204: array('i', (0, 3, 0, 3)), 205: array('i', (2, 3, 0, 3)), 206: array('i', (1, 3, 0, 3)), 207: array('i', (3, 3, 0, 3)),
- 208: array('i', (0, 0, 2, 3)), 209: array('i', (2, 0, 2, 3)), 210: array('i', (1, 0, 2, 3)), 211: array('i', (3, 0, 2, 3)),
- 212: array('i', (0, 2, 2, 3)), 213: array('i', (2, 2, 2, 3)), 214: array('i', (1, 2, 2, 3)), 215: array('i', (3, 2, 2, 3)),
- 216: array('i', (0, 1, 2, 3)), 217: array('i', (2, 1, 2, 3)), 218: array('i', (1, 1, 2, 3)), 219: array('i', (3, 1, 2, 3)),
- 220: array('i', (0, 3, 2, 3)), 221: array('i', (2, 3, 2, 3)), 222: array('i', (1, 3, 2, 3)), 223: array('i', (3, 3, 2, 3)),
- 224: array('i', (0, 0, 1, 3)), 225: array('i', (2, 0, 1, 3)), 226: array('i', (1, 0, 1, 3)), 227: array('i', (3, 0, 1, 3)),
- 228: array('i', (0, 2, 1, 3)), 229: array('i', (2, 2, 1, 3)), 230: array('i', (1, 2, 1, 3)), 231: array('i', (3, 2, 1, 3)),
- 232: array('i', (0, 1, 1, 3)), 233: array('i', (2, 1, 1, 3)), 234: array('i', (1, 1, 1, 3)), 235: array('i', (3, 1, 1, 3)),
- 236: array('i', (0, 3, 1, 3)), 237: array('i', (2, 3, 1, 3)), 238: array('i', (1, 3, 1, 3)), 239: array('i', (3, 3, 1, 3)),
- 240: array('i', (0, 0, 3, 3)), 241: array('i', (2, 0, 3, 3)), 242: array('i', (1, 0, 3, 3)), 243: array('i', (3, 0, 3, 3)),
- 244: array('i', (0, 2, 3, 3)), 245: array('i', (2, 2, 3, 3)), 246: array('i', (1, 2, 3, 3)), 247: array('i', (3, 2, 3, 3)),
- 248: array('i', (0, 1, 3, 3)), 249: array('i', (2, 1, 3, 3)), 250: array('i', (1, 1, 3, 3)), 251: array('i', (3, 1, 3, 3)),
- 252: array('i', (0, 3, 3, 3)), 253: array('i', (2, 3, 3, 3)), 254: array('i', (1, 3, 3, 3)), 255: array('i', (3, 3, 3, 3)),
- }
-
-GCODE_TO_INT = dict([(tuple(v),k) for (k,v) in INT_TO_GCODE.items()])
-
-### Exceptions
-class DuplicateMarkerInMapFile(Exception): pass
-class MapLineTooShort(Exception): pass
-class ThirdAllele(Exception): pass
-class PedError(Exception): pass
-class BadMagic(Exception):
- """ Raised when one of the MAGIC bytes in a bed file does not match
- """
- pass
-class BedError(Exception):
- """ Raised when parsing a bed file runs into problems
- """
- pass
-class UnknownGenocode(Exception):
- """ Raised when we get a 2-bit genotype that is undecipherable (is it possible?)
- """
- pass
-class UnknownGeno(Exception): pass
-
-### Utility functions
-
-def timenow():
- """return current time as a string
- """
- return time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(time.time()))
-
-def ceiling(n, k):
- ''' Return the least multiple of k which is greater than n
- '''
- m = n % k
- if m == 0:
- return n
- else:
- return n + k - m
-
-def nbytes(n):
- ''' Return the number of bytes required for n subjects
- '''
- return 2*ceiling(n, 4)/8
-
-### Primary module functionality
-class LPed:
- """ The uber-class for processing the Linkage-format *.ped/*.map files
- """
- def __init__(self, base):
- self.base = base
- self._ped = Ped('%s.ped' % (self.base))
- self._map = Map('%s.map' % (self.base))
-
- self._markers = {}
- self._ordered_markers = []
- self._marker_allele_lookup = {}
- self._autosomal_indices = set()
-
- self._subjects = {}
- self._ordered_subjects = []
-
- self._genotypes = []
-
- def parse(self):
- """
- """
- if VERBOSE: print 'plinkbinJZ: Analysis started: %s' % (timenow())
- self._map.parse()
- self._markers = self._map._markers
- self._ordered_markers = self._map._ordered_markers
- self._autosomal_indices = self._map._autosomal_indices
-
- self._ped.parse(self._ordered_markers)
- self._subjects = self._ped._subjects
- self._ordered_subjects = self._ped._ordered_subjects
- self._genotypes = self._ped._genotypes
- self._marker_allele_lookup = self._ped._marker_allele_lookup
-
- ### Adjust self._markers based on the allele information
- ### we got from parsing the ped file
- for m, name in enumerate(self._ordered_markers):
- a1, a2 = self._marker_allele_lookup[m][HET]
- self._markers[name][-2] = a1
- self._markers[name][-1] = a2
- if VERBOSE: print 'plinkbinJZ: Analysis finished: %s' % (timenow())
-
- def getSubjectInfo(self, fid, oiid):
- """
- """
- return self._subject_info[(fid, oiid)]
-
- def getSubjectInfoByLine(self, line):
- """
- """
- return self._subject_info[self._ordered_subjects[line]]
-
- def getGenotypesByIndices(self, s, mlist, format):
- """ needed for grr if lped - deprecated but..
- """
- mlist = dict(zip(mlist,[True,]*len(mlist))) # hash quicker than 'in' ?
- raw_array = array('i', [row[s] for m,row in enumerate(self._genotypes) if mlist.get(m,None)])
- if format == 'raw':
- return raw_array
- elif format == 'ref':
- result = array('i', [0]*len(mlist))
- for m, gcode in enumerate(raw_array):
- if gcode == HOM0:
- nref = 3
- elif gcode == HET:
- nref = 2
- elif gcode == HOM1:
- nref = 1
- else:
- nref = 0
- result[m] = nref
- return result
- else:
- result = []
- for m, gcode in enumerate(raw_array):
- result.append(self._marker_allele_lookup[m][gcode])
- return result
-
- def writebed(self, base):
- """
- """
- dst_name = '%s.fam' % (base)
- print 'Writing pedigree information to [ %s ]' % (dst_name)
- dst = open(dst_name, 'w')
- for skey in self._ordered_subjects:
- (fid, iid, did, mid, sex, phe, sid, d_sid, m_sid) = self._subjects[skey]
- dst.write('%s %s %s %s %s %s\n' % (fid, iid, did, mid, sex, phe))
- dst.close()
-
- dst_name = '%s.bim' % (base)
- print 'Writing map (extended format) information to [ %s ]' % (dst_name)
- dst = open(dst_name, 'w')
- for m, marker in enumerate(self._ordered_markers):
- chrom, name, genpos, abspos, a1, a2 = self._markers[marker]
- dst.write('%s\t%s\t%s\t%s\t%s\t%s\n' % (chrom, name, genpos, abspos, a1, a2))
- dst.close()
-
- bed_name = '%s.bed' % (base)
- print 'Writing genotype bitfile to [ %s ]' % (bed_name)
- print 'Using (default) SNP-major mode'
- bed = open(bed_name, 'w')
-
- ### Write the 3 header bytes
- bed.write(struct.pack('B', int(''.join(reversed(MAGIC_BYTE1)), 2)))
- bed.write(struct.pack('B', int(''.join(reversed(MAGIC_BYTE2)), 2)))
- bed.write(struct.pack('B', int(''.join(reversed(FORMAT_SNP_MAJOR_BYTE)), 2)))
-
- ### Calculate how many "pad bits" we should add after the last subject
- nsubjects = len(self._ordered_subjects)
- nmarkers = len(self._ordered_markers)
- total_bytes = nbytes(nsubjects)
- nbits = nsubjects * 2
- pad_nibbles = ((total_bytes * 8) - nbits)/2
- pad = array('i', [0]*pad_nibbles)
-
- ### And now write genotypes to the file
- for m in xrange(nmarkers):
- geno = self._genotypes[m]
- geno.extend(pad)
- bytes = len(geno)/4
- for b in range(bytes):
- idx = b*4
- gcode = tuple(geno[idx:idx+4])
- try:
- byte = struct.pack('B', GCODE_TO_INT[gcode])
- except KeyError:
- print m, b, gcode
- raise
- bed.write(byte)
- bed.close()
-
- def autosomal_indices(self):
- """ Return the indices of markers in this ped/map that are autosomal.
- This is used by rgGRR so that it can select a random set of markers
- from the autosomes (sex chroms screw up the plot)
- """
- return self._autosomal_indices
-
-class Ped:
- def __init__(self, path):
- self.path = path
- self._subjects = {}
- self._ordered_subjects = []
- self._genotypes = []
- self._marker_allele_lookup = {}
-
- def lineCount(self,infile):
- """ count the number of lines in a file - efficiently using wget
- """
- return int(commands.getoutput('wc -l %s' % (infile)).split()[0])
-
-
- def parse(self, markers):
- """ Parse a given file -- this needs to be memory-efficient so that large
- files can be parsed (~1 million markers on ~5000 subjects?). It
- should also be fast, if possible.
- """
-
- ### Find out how many lines are in the file so we can ...
- nsubjects = self.lineCount(self.path)
- ### ... Pre-allocate the genotype arrays
- nmarkers = len(markers)
- _marker_alleles = [['0', '0'] for _ in xrange(nmarkers)]
- self._genotypes = [array('i', [-1]*nsubjects) for _ in xrange(nmarkers)]
-
- if self.path.endswith('.gz'):
- pfile = gzip.open(self.path, 'r')
- else:
- pfile = open(self.path, 'r')
-
- for s, line in enumerate(pfile):
- line = line.strip()
- if not line:
- continue
-
- fid, iid, did, mid, sex, phe, genos = line.split(None, 6)
- sid = iid.split('.')[0]
- d_sid = did.split('.')[0]
- m_sid = mid.split('.')[0]
-
- skey = (fid, iid)
- self._subjects[skey] = (fid, iid, did, mid, sex, phe, sid, d_sid, m_sid)
- self._ordered_subjects.append(skey)
-
- genotypes = genos.split()
-
- for m, marker in enumerate(markers):
- idx = m*2
- a1, a2 = genotypes[idx:idx+2] # Alleles for subject s, marker m
- s1, s2 = seen = _marker_alleles[m] # Alleles seen for marker m
-
- ### FIXME: I think this can still be faster, and simpler to read
- # Two pieces of logic intertwined here: first, we need to code
- # this genotype as HOM0, HOM1, HET or MISS. Second, we need to
- # keep an ongoing record of the genotypes seen for this marker
- if a1 == a2:
- if a1 in MISSING_ALLELES:
- geno = MISS_GENO
- else:
- if s1 == '0':
- seen[0] = a1
- elif s1 == a1 or s2 == a2:
- pass
- elif s2 == '0':
- seen[1] = a1
- else:
- raise ThirdAllele('a1=a2=%s, seen=%s?' % (a1, str(seen)))
-
- if a1 == seen[0]:
- geno = HOM0_GENO
- elif a1 == seen[1]:
- geno = HOM1_GENO
- else:
- raise PedError('Cannot assign geno for a1=a2=%s from seen=%s' % (a1, str(seen)))
- elif a1 in MISSING_ALLELES or a2 in MISSING_ALLELES:
- geno = MISS_GENO
- else:
- geno = HET_GENO
- if s1 == '0':
- seen[0] = a1
- seen[1] = a2
- elif s2 == '0':
- if s1 == a1:
- seen[1] = a2
- elif s1 == a2:
- seen[1] = a1
- else:
- raise ThirdAllele('a1=%s, a2=%s, seen=%s?' % (a1, a2, str(seen)))
- else:
- if sorted(seen) != sorted((a1, a2)):
- raise ThirdAllele('a1=%s, a2=%s, seen=%s?' % (a1, a2, str(seen)))
-
- gcode = GENO_TO_GCODE.get(geno, None)
- if gcode is None:
- raise UnknownGeno(str(geno))
- self._genotypes[m][s] = gcode
-
- # Build the _marker_allele_lookup table
- for m, alleles in enumerate(_marker_alleles):
- if len(alleles) == 2:
- a1, a2 = alleles
- elif len(alleles) == 1:
- a1 = alleles[0]
- a2 = '0'
- else:
- print 'All alleles blank for %s: %s' % (m, str(alleles))
- raise
-
- self._marker_allele_lookup[m] = {
- HOM0: (a2, a2),
- HOM1: (a1, a1),
- HET : (a1, a2),
- MISS: ('0','0'),
- }
-
- if VERBOSE: print '%s(%s) individuals read from [ %s ]' % (len(self._subjects), nsubjects, self.path)
-
-class Map:
- def __init__(self, path=None):
- self.path = path
- self._markers = {}
- self._ordered_markers = []
- self._autosomal_indices = set()
-
- def __len__(self):
- return len(self._markers)
-
- def parse(self):
- """ Parse a Linkage-format map file
- """
- if self.path.endswith('.gz'):
- fh = gzip.open(self.path, 'r')
- else:
- fh = open(self.path, 'r')
-
- for i, line in enumerate(fh):
- line = line.strip()
- if not line:
- continue
-
- fields = line.split()
- if len(fields) < 4:
- raise MapLineTooShort(MAP_LINE_EXCEPTION_TEXT % (str(line), len(fields)))
- else:
- chrom, name, genpos, abspos = fields
- if name in self._markers:
- raise DuplicateMarkerInMapFile('Marker %s was found twice in map file %s' % (name, self.path))
- abspos = int(abspos)
- if abspos < 0:
- continue
- if chrom in AUTOSOMES:
- self._autosomal_indices.add(i)
- chrom = CHROM_REPLACE.get(chrom, chrom)
- self._markers[name] = [chrom, name, genpos, abspos, None, None]
- self._ordered_markers.append(name)
- fh.close()
- if VERBOSE: print '%s (of %s) markers to be included from [ %s ]' % (len(self._ordered_markers), i, self.path)
-
-class BPed:
- """ The uber-class for processing Plink's Binary Ped file format *.bed/*.bim/*.fam
- """
- def __init__(self, base):
- self.base = base
- self._bed = Bed('%s.bed' % (self.base))
- self._bim = Bim('%s.bim' % (self.base))
- self._fam = Fam('%s.fam' % (self.base))
-
- self._markers = {}
- self._ordered_markers = []
- self._marker_allele_lookup = {}
- self._autosomal_indices = set()
-
- self._subjects = {}
- self._ordered_subjects = []
-
- self._genotypes = []
-
- def parse(self, quick=False):
- """
- """
- self._quick = quick
-
- self._bim.parse()
- self._markers = self._bim._markers
- self._ordered_markers = self._bim._ordered_markers
- self._marker_allele_lookup = self._bim._marker_allele_lookup
- self._autosomal_indices = self._bim._autosomal_indices
-
- self._fam.parse()
- self._subjects = self._fam._subjects
- self._ordered_subjects = self._fam._ordered_subjects
-
- self._bed.parse(self._ordered_subjects, self._ordered_markers, quick=quick)
- self._bedf = self._bed._fh
- self._genotypes = self._bed._genotypes
- self.nsubjects = len(self._ordered_subjects)
- self.nmarkers = len(self._ordered_markers)
- self._bytes_per_marker = nbytes(self.nsubjects)
-
- def writeped(self, path=None):
- """
- """
- path = self.path = path or self.path
-
- map_name = self.path.replace('.bed', '.map')
- print 'Writing map file [ %s ]' % (map_name)
- dst = open(map_name, 'w')
- for m in self._ordered_markers:
- chrom, snp, genpos, abspos, a1, a2 = self._markers[m]
- dst.write('%s\t%s\t%s\t%s\n' % (chrom, snp, genpos, abspos))
- dst.close()
-
- ped_name = self.path.replace('.bed', '.ped')
- print 'Writing ped file [ %s ]' % (ped_name)
- ped = open(ped_name, 'w')
- firstyikes = False
- for s, skey in enumerate(self._ordered_subjects):
- idx = s*2
- (fid, iid, did, mid, sex, phe, oiid, odid, omid) = self._subjects[skey]
- ped.write('%s %s %s %s %s %s' % (fid, iid, odid, omid, sex, phe))
- genotypes_for_subject = self.getGenotypesForSubject(s)
- for m, snp in enumerate(self._ordered_markers):
- #a1, a2 = self.getGenotypeByIndices(s, m)
- a1,a2 = genotypes_for_subject[m]
- ped.write(' %s %s' % (a1, a2))
- ped.write('\n')
- ped.close()
-
- def getGenotype(self, subject, marker):
- """ Retrieve a genotype for a particular subject/marker pair
- """
- m = self._ordered_markers.index(marker)
- s = self._ordered_subjects.index(subject)
- return self.getGenotypeByIndices(s, m)
-
- def getGenotypesForSubject(self, s, raw=False):
- """ Returns list of genotypes for all m markers
- for subject s. If raw==True, then an array
- of raw integer gcodes is returned instead
- """
- if self._quick:
- nmarkers = len(self._markers)
- raw_array = array('i', [0]*nmarkers)
- seek_nibble = s % 4
- for m in xrange(nmarkers):
- seek_byte = m * self._bytes_per_marker + s/4 + HEADER_LENGTH
- self._bedf.seek(seek_byte)
- geno = struct.unpack('B', self._bedf.read(1))[0]
- quartet = INT_TO_GCODE[geno]
- gcode = quartet[seek_nibble]
- raw_array[m] = gcode
- else:
- raw_array = array('i', [row[s] for row in self._genotypes])
-
- if raw:
- return raw_array
- else:
- result = []
- for m, gcode in enumerate(raw_array):
- result.append(self._marker_allele_lookup[m][gcode])
- return result
-
- def getGenotypeByIndices(self, s, m):
- """
- """
- if self._quick:
- # Determine which byte we need to seek to, and
- # which nibble within the byte we need
- seek_byte = m * self._bytes_per_marker + s/4 + HEADER_LENGTH
- seek_nibble = s % 4
- self._bedf.seek(seek_byte)
- geno = struct.unpack('B', self._bedf.read(1))[0]
- quartet = INT_TO_GCODE[geno]
- gcode = quartet[seek_nibble]
- else:
- # Otherwise, just grab the genotypes from the
- # list of arrays
- genos_for_marker = self._genotypes[m]
- gcode = genos_for_marker[s]
-
- return self._marker_allele_lookup[m][gcode]
-
- def getGenotypesByIndices(self, s, mlist, format):
- """
- """
- if self._quick:
- raw_array = array('i', [0]*len(mlist))
- seek_nibble = s % 4
- for i,m in enumerate(mlist):
- seek_byte = m * self._bytes_per_marker + s/4 + HEADER_LENGTH
- self._bedf.seek(seek_byte)
- geno = struct.unpack('B', self._bedf.read(1))[0]
- quartet = INT_TO_GCODE[geno]
- gcode = quartet[seek_nibble]
- raw_array[i] = gcode
- mlist = set(mlist)
- else:
- mlist = set(mlist)
- raw_array = array('i', [row[s] for m,row in enumerate(self._genotypes) if m in mlist])
-
- if format == 'raw':
- return raw_array
- elif format == 'ref':
- result = array('i', [0]*len(mlist))
- for m, gcode in enumerate(raw_array):
- if gcode == HOM0:
- nref = 3
- elif gcode == HET:
- nref = 2
- elif gcode == HOM1:
- nref = 1
- else:
- nref = 0
- result[m] = nref
- return result
- else:
- result = []
- for m, gcode in enumerate(raw_array):
- result.append(self._marker_allele_lookup[m][gcode])
- return result
-
- def getSubject(self, s):
- """
- """
- skey = self._ordered_subjects[s]
- return self._subjects[skey]
-
- def autosomal_indices(self):
- """ Return the indices of markers in this ped/map that are autosomal.
- This is used by rgGRR so that it can select a random set of markers
- from the autosomes (sex chroms screw up the plot)
- """
- return self._autosomal_indices
-
-class Bed:
-
- def __init__(self, path):
- self.path = path
- self._genotypes = []
- self._fh = None
-
- def parse(self, subjects, markers, quick=False):
- """ Parse the bed file, indicated either by the path parameter,
- or as the self.path indicated in __init__. If quick is
- True, then just parse the bim and fam, then genotypes will
- be looked up dynamically by indices
- """
- self._quick = quick
-
- ordered_markers = markers
- ordered_subjects = subjects
- nsubjects = len(ordered_subjects)
- nmarkers = len(ordered_markers)
-
- bed = open(self.path, 'rb')
- self._fh = bed
-
- byte1 = bed.read(1)
- byte2 = bed.read(1)
- byte3 = bed.read(1)
- format_flag = struct.unpack('B', byte3)[0]
-
- h1 = tuple(INT_TO_GCODE[struct.unpack('B', byte1)[0]])
- h2 = tuple(INT_TO_GCODE[struct.unpack('B', byte2)[0]])
- h3 = tuple(INT_TO_GCODE[format_flag])
-
- if h1 != MAGIC1 or h2 != MAGIC2:
- raise BadMagic('One or both MAGIC bytes is wrong: %s==%s or %s==%s' % (h1, MAGIC1, h2, MAGIC2))
- if format_flag:
- print 'Detected that binary PED file is v1.00 SNP-major mode (%s, "%s")\n' % (format_flag, h3)
- else:
- raise 'BAD_FORMAT_FLAG? (%s, "%s")\n' % (format_flag, h3)
-
- print 'Parsing binary ped file for %s markers and %s subjects' % (nmarkers, nsubjects)
-
- ### If quick mode was specified, we're done ...
- self._quick = quick
- if quick:
- return
-
- ### ... Otherwise, parse genotypes into an array, and append that
- ### array to self._genotypes
- ngcodes = ceiling(nsubjects, 4)
- bytes_per_marker = nbytes(nsubjects)
- for m in xrange(nmarkers):
- genotype_array = array('i', [-1]*(ngcodes))
- for byte in xrange(bytes_per_marker):
- intval = struct.unpack('B', bed.read(1))[0]
- idx = byte*4
- genotype_array[idx:idx+4] = INT_TO_GCODE[intval]
- self._genotypes.append(genotype_array)
-
-class Bim:
- def __init__(self, path):
- """
- """
- self.path = path
- self._markers = {}
- self._ordered_markers = []
- self._marker_allele_lookup = {}
- self._autosomal_indices = set()
-
- def parse(self):
- """
- """
- print 'Reading map (extended format) from [ %s ]' % (self.path)
- bim = open(self.path, 'r')
- for m, line in enumerate(bim):
- chrom, snp, gpos, apos, a1, a2 = line.strip().split()
- self._markers[snp] = (chrom, snp, gpos, apos, a1, a2)
- self._marker_allele_lookup[m] = {
- HOM0: (a2, a2),
- HOM1: (a1, a1),
- HET : (a1, a2),
- MISS: ('0','0'),
- }
- self._ordered_markers.append(snp)
- if chrom in AUTOSOMES:
- self._autosomal_indices.add(m)
- bim.close()
- print '%s markers to be included from [ %s ]' % (m+1, self.path)
-
-class Fam:
- def __init__(self, path):
- """
- """
- self.path = path
- self._subjects = {}
- self._ordered_subjects = []
-
- def parse(self):
- """
- """
- print 'Reading pedigree information from [ %s ]' % (self.path)
- fam = open(self.path, 'r')
- for s, line in enumerate(fam):
- fid, iid, did, mid, sex, phe = line.strip().split()
- sid = iid.split('.')[0]
- d_sid = did.split('.')[0]
- m_sid = mid.split('.')[0]
- skey = (fid, iid)
- self._ordered_subjects.append(skey)
- self._subjects[skey] = (fid, iid, did, mid, sex, phe, sid, d_sid, m_sid)
- fam.close()
- print '%s individuals read from [ %s ]' % (s+1, self.path)
-
-### Command-line functionality and testing
-def test(arg):
- '''
- '''
-
- import time
-
- if arg == 'CAMP_AFFY.ped':
- print 'Testing bed.parse(quick=True)'
- s = time.time()
- bed = Bed(arg.replace('.ped', '.bed'))
- bed.parse(quick=True)
- print bed.getGenotype(('400118', '10300283'), 'rs2000467')
- print bed.getGenotype(('400118', '10101384'), 'rs2294019')
- print bed.getGenotype(('400121', '10101149'), 'rs2294019')
- print bed.getGenotype(('400123', '10200290'), 'rs2294019')
- assert bed.getGenotype(('400118', '10101384'), 'rs2294019') == ('4','4')
- e = time.time()
- print 'e-s = %s\n' % (e-s)
-
- print 'Testing bed.parse'
- s = time.time()
- bed = BPed(arg)
- bed.parse(quick=False)
- e = time.time()
- print 'e-s = %s\n' % (e-s)
-
- print 'Testing bed.writeped'
- s = time.time()
- outname = '%s_BEDTEST' % (arg)
- bed.writeped(outname)
- e = time.time()
- print 'e-s = %s\n' % (e-s)
- del(bed)
-
- print 'Testing ped.parse'
- s = time.time()
- ped = LPed(arg)
- ped.parse()
- e = time.time()
- print 'e-s = %s\n' % (e-s)
-
- print 'Testing ped.writebed'
- s = time.time()
- outname = '%s_PEDTEST' % (arg)
- ped.writebed(outname)
- e = time.time()
- print 'e-s = %s\n' % (e-s)
- del(ped)
-
-def profile_bed(arg):
- """
- """
- bed = BPed(arg)
- bed.parse(quick=False)
- outname = '%s_BEDPROFILE' % (arg)
- bed.writeped(outname)
-
-def profile_ped(arg):
- """
- """
- ped = LPed(arg)
- ped.parse()
- outname = '%s_PEDPROFILE' % (arg)
- ped.writebed(outname)
-
-if __name__ == '__main__':
- """ Run as a command-line, this script should get one or more arguments,
- each one a ped file to be parsed with the PedParser (unit tests?)
- """
- op = optparse.OptionParser()
- op.add_option('--profile-bed', action='store_true', default=False)
- op.add_option('--profile-ped', action='store_true', default=False)
- opts, args = op.parse_args()
-
- if opts.profile_bed:
- import profile
- import pstats
- profile.run('profile_bed(args[0])', 'fooprof')
- p = pstats.Stats('fooprof')
- p.sort_stats('cumulative').print_stats(10)
- elif opts.profile_ped:
- import profile
- import pstats
- profile.run('profile_ped(args[0])', 'fooprof')
- p = pstats.Stats('fooprof')
- p.sort_stats('cumulative').print_stats(10)
- else:
- for arg in args:
- test(arg)
-
- ### Code used to generate the INT_TO_GCODE dictionary
- #print '{\n ',
- #for i in range(256):
- # b = INT2BIN[i]
- # ints = []
- # s = str(i).rjust(3)
- # #print b
- # for j in range(4):
- # idx = j*2
- # #print i, j, idx, b[idx:idx+2], int(b[idx:idx+2], 2)
- # ints.append(int(b[idx:idx+2], 2))
- # print '%s: array(\'i\', %s),' % (s,tuple(ints)),
- # if i > 0 and (i+1) % 4 == 0:
- # print '\n ',
- #print '}'
-
-
diff -r 3df68022beef2077798ed50a33afb03be75a841a -r c8b55344e779e8be93bae5d66f52128addd83003 tools/rgenetics/rgCaCo.py
--- a/tools/rgenetics/rgCaCo.py
+++ /dev/null
@@ -1,271 +0,0 @@
-#!/usr/local/bin/python
-# hack to run and process a plink case control association
-# expects args as
-# bfilepath outname jobname outformat (wig,xls)
-# ross lazarus
-# for wig files, we need annotation so look for map file or complain
-"""
-Parameters for wiggle track definition lines
-All options are placed in a single line separated by spaces:
-
- track type=wiggle_0 name=track_label description=center_label \
- visibility=display_mode color=r,g,b altColor=r,g,b \
- priority=priority autoScale=on|off \
- gridDefault=on|off maxHeightPixels=max:default:min \
- graphType=bar|points viewLimits=lower:upper \
- yLineMark=real-value yLineOnOff=on|off \
- windowingFunction=maximum|mean|minimum smoothingWindow=off|2-16
-"""
-
-import sys,math,shutil,subprocess,os,time,tempfile,string
-from os.path import abspath
-from rgutils import timenow, plinke
-imagedir = '/static/rg' # if needed for images
-myversion = 'V000.1 April 2007'
-verbose = False
-
-def makeGFF(resf='',outfname='',logf=None,twd='.',name='track name',description='track description',topn=1000):
- """
- score must be scaled to 0-1000
-
- Want to make some wig tracks from each analysis
- Best n -log10(p). Make top hit the window.
- we use our tab output which has
- rs chrom offset ADD_stat ADD_p ADD_log10p
- rs3094315 1 792429 1.151 0.2528 0.597223
-
- """
-
- def is_number(s):
- try:
- float(s)
- return True
- except ValueError:
- return False
- header = 'track name=%s description="%s" visibility=2 useScore=1 color=0,60,120\n' % (name,description)
- column_names = [ 'Seqname', 'Source', 'Feature', 'Start', 'End', 'Score', 'Strand', 'Frame', 'Group' ]
- halfwidth=100
- resfpath = os.path.join(twd,resf)
- resf = open(resfpath,'r')
- resfl = resf.readlines() # dumb but convenient for millions of rows
- resfl = [x.split() for x in resfl]
- headl = resfl[0]
- resfl = resfl[1:]
- headl = [x.strip().upper() for x in headl]
- headIndex = dict(zip(headl,range(0,len(headl))))
- whatwewant = ['CHR','RS','OFFSET','LOG10ARMITAGEP']
- wewant = [headIndex.get(x,None) for x in whatwewant]
- if None in wewant: # missing something
- logf.write('### Error missing a required header from %s in makeGFF - headIndex=%s\n' % (whatwewant,headIndex))
- return
- ppos = wewant[3] # last in list
- resfl = [x for x in resfl if x[ppos] > '' and x[ppos] <> 'NA']
- resfl = [(float(x[ppos]),x) for x in resfl] # decorate
- resfl.sort()
- resfl.reverse() # using -log10 so larger is better
- pvals = [x[0] for x in resfl] # need to scale
- resfl = [x[1] for x in resfl] # drop decoration
- resfl = resfl[:topn] # truncate
- maxp = max(pvals) # need to scale
- minp = min(pvals)
- prange = abs(maxp-minp) + 0.5 # fudge
- scalefact = 1000.0/prange
- logf.write('###maxp=%f,minp=%f,prange=%f,scalefact=%f\n' % (maxp,minp,prange,scalefact))
- for i,row in enumerate(resfl):
- row[ppos] = '%d' % (int(scalefact*pvals[i]))
- resfl[i] = row # replace
- outf = file(outfname,'w')
- outf.write(header)
- outres = [] # need to resort into chrom offset order
- for i,lrow in enumerate(resfl):
- chrom,snp,offset,p, = [lrow[x] for x in wewant]
- gff = ('chr%s' % chrom,'rgCaCo','variation','%d' % (int(offset)-halfwidth),
- '%d' % (int(offset)+halfwidth),p,'.','.','%s logp=%1.2f' % (snp,pvals[i]))
- outres.append(gff)
- outres = [(x[0],int(x[3]),x) for x in outres] # decorate
- outres.sort() # into chrom offset
- outres=[x[2] for x in outres] # undecorate
- outres = ['\t'.join(x) for x in outres]
- outf.write('\n'.join(outres))
- outf.write('\n')
- outf.close()
-
-
-def plink_assocToGG(plinkout="hm",tag='test'):
- """ plink --assoc output looks like this
- # CHR SNP A1 F_A F_U A2 CHISQ P OR
- # 1 rs3094315 G 0.6685 0.1364 A 104.1 1.929e-24 12.77
- # write as a genegraph input file
- """
- inf = file('%s.assoc' % plinkout,'r')
- outf = file('%sassoc.xls' % plinkout,'w')
- res = ['rs\tlog10p%s\tFakeInvOR%s\tRealOR%s' % (tag,tag,tag),] # output header for ucsc genome graphs
- head = inf.next()
- for l in inf:
- ll = l.split()
- if len(ll) >= 8:
- p = float(ll[7])
- if p <> 'NA': # eesh
- logp = '%9.9f' % -math.log10(p)
- else:
- logp = 'NA'
- try:
- orat = ll[8]
- except:
- orat = 'NA'
- orat2 = orat
- # invert large negative odds ratios
- if float(orat) < 1 and float(orat) > 0.0:
- orat2 = '%9.9f' % (1.0/float(orat))
- outl = [ll[1],logp, orat2, orat]
- res.append('\t'.join(outl))
- outf.write('\n'.join(res))
- outf.write('\n')
- outf.close()
- inf.close()
-
-def xformModel(infname='',resf='',outfname='',
- name='foo',mapf='/usr/local/galaxy/data/rg/ped/x.bim',flog=None):
- """munge a plink .model file into either a ucsc track or an xls file
- rerla@meme ~/plink]$ head hmYRI_CEU.model
- CHR SNP TEST AFF UNAFF CHISQ DF P
- 1 rs3094315 GENO 41/37/11 0/24/64 NA NA NA
- 1 rs3094315 TREND 119/59 24/152 81.05 1 2.201e-19
- 1 rs3094315 ALLELIC 119/59 24/152 104.1 1 1.929e-24
- 1 rs3094315 DOM 78/11 24/64 NA NA NA
-
- bim file has
-[rerla@beast pbed]$ head plink_wgas1_example.bim
-1 rs3094315 0.792429 792429 G A
-1 rs6672353 0.817376 817376 A G
- """
- if verbose:
- print 'Rgenetics rgCaCo.xformModel got resf=%s, outfname=%s' % (resf,outfname)
- res = []
- rsdict = {}
- map = file(mapf,'r')
- for l in map: # plink map
- ll = l.strip().split()
- if len(ll) >= 3:
- rs=ll[1].strip()
- chrom = ll[0]
- if chrom.lower() == 'x':
- chrom='23'
- elif chrom.lower() == 'y':
- chrom = 24
- elif chrom.lower() == 'mito':
- chrom = 25
- offset = ll[3]
- rsdict[rs] = (chrom,offset)
- res.append('rs\tChr\tOffset\tGenop\tlog10Genop\tArmitagep\tlog10Armitagep\tAllelep\tlog10Allelep\tDomp\tlog10Domp')
- f = open(resf,'r')
- headl = f.readline()
- if headl.find('\t') <> -1:
- headl = headl.split('\t')
- delim = '\t'
- else:
- headl = headl.split()
- delim = None
- whatwewant = ['CHR','SNP','TEST','AFF','UNAFF','CHISQ','P']
- wewant = [headl.index(x) for x in whatwewant]
- llen = len(headl)
- lnum = anum = 0
- lastsnp = None # so we know when to write out a gg line
- outl = {}
- f.seek(0)
- for lnum,l in enumerate(f):
- if lnum == 0:
- continue
- ll = l.split()
- if delim:
- ll = l.split(delim)
- if len(ll) >= llen: # valid line
- chr,snp,test,naff,nuaff,chi,p = [ll[x] for x in wewant]
- snp = snp.strip()
- chrom,offset = rsdict.get(snp,(None,None))
- anum += 1
- fp = 1.0 # if NA
- lp = 0.0
- try:
- fp = float(p)
- if fp > 0:
- lp = -math.log10(fp)
- else:
- fp = 9e-100
- flog.write('### WARNING - Plink calculated %s for %s p value!!! 9e-100 substituted!\n' % (p,test))
- flog.write('### offending line #%d in %s = %s' % (lnum,l))
- except:
- pass
- if snp <> lastsnp:
- if len(outl.keys()) > 3:
- sl = [outl.get(x,'?') for x in ('snp','chrom','offset','GENO','TREND','ALLELIC','DOM')]
- res.append('\t'.join(sl)) # last snp line
- outl = {'snp':snp,'chrom':chrom,'offset':offset} # first 3 cols for gg line
- lastsnp = snp # reset for next marker
- #if p == 'NA':
- # p = 1.0
- # let's pass downstream for handling R is fine?
- outl[test] = '%s\t%f' % (p,lp)
- if len(outl.keys()) > 3:
- l = [outl.get(x,'?') for x in ('snp','chrom','offset','GENO','TREND','ALLELIC','DOM')]
- res.append('\t'.join(l)) # last snp line
- f = file(outfname,'w')
- res.append('')
- f.write('\n'.join(res))
- f.close()
-
-
-
-
-if __name__ == "__main__":
- """
- # called as
- <command interpreter="python">
- rgCaCo.py '$i.extra_files_path/$i.metadata.base_name' "$name"
- '$out_file1' '$logf' '$logf.files_path' '$gffout'
- </command></command>
- """
- if len(sys.argv) < 7:
- s = 'rgCaCo.py needs 6 params - got %s \n' % (sys.argv)
- print >> sys.stdout, s
- sys.exit(0)
- bfname = sys.argv[1]
- name = sys.argv[2]
- killme = string.punctuation + string.whitespace
- trantab = string.maketrans(killme,'_'*len(killme))
- name = name.translate(trantab)
- outfname = sys.argv[3]
- logf = sys.argv[4]
- logoutdir = sys.argv[5]
- gffout = sys.argv[6]
- topn = 1000
- try:
- os.makedirs(logoutdir)
- except:
- pass
- map_file = None
- me = sys.argv[0]
- amapf = '%s.bim' % bfname # to decode map in xformModel
- flog = file(logf,'w')
- logme = []
- cdir = os.getcwd()
- s = 'Rgenetics %s http://rgenetics.org Galaxy Tools, rgCaCo.py started %s\n' % (myversion,timenow())
- print >> sys.stdout, s # so will appear as blurb for file
- logme.append(s)
- if verbose:
- s = 'rgCaCo.py: bfname=%s, logf=%s, argv = %s\n' % (bfname, logf, sys.argv)
- print >> sys.stdout, s # so will appear as blurb for file
- logme.append(s)
- twd = tempfile.mkdtemp(suffix='rgCaCo') # make sure plink doesn't spew log file into the root!
- tname = os.path.join(twd,name)
- vcl = [plinke,'--noweb','--bfile',bfname,'--out',name,'--model']
- p=subprocess.Popen(' '.join(vcl),shell=True,stdout=flog,cwd=twd)
- retval = p.wait()
- resf = '%s.model' % tname # plink output is here we hope
- xformModel(bfname,resf,outfname,name,amapf,flog) # leaves the desired summary file
- makeGFF(resf=outfname,outfname=gffout,logf=flog,twd=twd,name='rgCaCo_TopTable',description=name,topn=topn)
- flog.write('\n'.join(logme))
- flog.close() # close the log used
- #shutil.copytree(twd,logoutdir)
- shutil.rmtree(twd) # clean up
-
diff -r 3df68022beef2077798ed50a33afb03be75a841a -r c8b55344e779e8be93bae5d66f52128addd83003 tools/rgenetics/rgCaCo.xml
--- a/tools/rgenetics/rgCaCo.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-<tool id="rgCaCo1" name="Case Control:">
- <requirements><requirement type="package">plink</requirement></requirements>
- <description>for unrelated subjects</description>
- <command interpreter="python">
- rgCaCo.py '$i.extra_files_path/$i.metadata.base_name' "$title" '$out_file1' '$logf' '$logf.files_path' '$gffout'
- </command>
- <inputs>
- <param name="i" type="data" label="RGenetics genotype data from your current history"
- format="pbed" />
- <param name='title' type='text' size="132" value='CaseControl' label="Title for this job"/>
-
- </inputs>
-
- <outputs>
- <data format="tabular" name="out_file1" label="${title}_rgCaCo.xls" />
- <data format="txt" name="logf" label="${title}_rgCaCo.log"/>
- <data format="gff" name="gffout" label="${title}_rgCaCoTop.gff" />
- </outputs>
-<tests>
- <test>
- <param name='i' value='tinywga' ftype='pbed' >
- <metadata name='base_name' value='tinywga' />
- <composite_data value='tinywga.bim' />
- <composite_data value='tinywga.bed' />
- <composite_data value='tinywga.fam' />
- <edit_attributes type='name' value='tinywga' />
- </param>
- <param name='title' value='rgCaCotest1' />
- <output name='out_file1' file='rgCaCotest1_CaCo.xls' ftype='tabular' compare='diff' />
- <output name='logf' file='rgCaCotest1_CaCo_log.txt' ftype='txt' compare='diff' lines_diff='20' />
- <output name='gffout' file='rgCaCotest1_CaCo_topTable.gff' ftype='gff' compare='diff' />
- </test>
-</tests>
-<help>
-
-.. class:: infomark
-
-**Syntax**
-
-- **Genotype file** is the input case control data chosen from available library Plink binary files
-- **Map file** is the linkage format .map file corresponding to the genotypes in the Genotype file
-- **Type of test** is the kind of test statistic to report such as Armitage trend test or genotype test
-- **Format** determines how your data will be returned to your Galaxy workspace
-
------
-
-**Summary**
-
-This tool will perform some standard statistical tests comparing subjects designated as
-affected (cases) and unaffected subjects (controls). To avoid bias, it is important that
-controls who had been affected would have been eligible for sampling as cases. This may seem
-odd, but it requires that the cases and controls are drawn from the same sampling frame.
-
-The armitage trend test is robust to departure from HWE and so very attractive - after all, a real disease
-mutation may well result in distorted HWE at least in cases. All the others are susceptible to
-bias in the presence of HWE departures.
-
-All of these tests are exquisitely sensitive to non-differential population stratification in cases
-compared to controls and this must be tested before believing any results here. Use the PCA method for
-100k markers or more.
-
-If you don't see the genotype data set you want here, it can be imported using one of the methods available from
-the Galaxy Get Data tool page.
-
-Output format can be UCSC .bed if you want to see your
-results as a fully fledged UCSC track. A map file containing the chromosome and offset for each marker is required for
-writing this kind of output.
-Alternatively you can use .gg for the UCSC Genome Graphs tool which has all of the advantages
-of the the .bed track, plus a neat, visual front end that displays a lot of useful clues.
-Either of these are a very useful way of quickly getting a look
-at your data in full genomic context.
-
-Finally, if you can't live without
-spreadsheet data, choose the .xls tab delimited format. It's not a stupid binary excel file. Just a plain old tab delimited
-one with a header. Fortunately excel is dumb enough to open these without much protest.
-
-
------
-
-.. class:: infomark
-
-**Attribution**
-
-This Galaxy tool relies on Plink (see Plinksrc_) to test Casae Control association models.
-
-So, we rely on the author (Shaun Purcell) for the documentation you need specific to those settings - they are very nicely documented - see
-DOC_
-
-Tool and Galaxy datatypes originally designed and written for the Rgenetics
-series of whole genome scale statistical genetics tools by ross lazarus (ross.lazarus(a)gmail.com)
-
-Copyright Ross Lazarus March 2007
-This Galaxy wrapper is released licensed under the LGPL_ but is about as useful as a chocolate teapot without Plink which is GPL.
-
-I'm no lawyer, but it looks like you got GPL if you use this software. Good luck.
-
-.. _Plinksrc: http://pngu.mgh.harvard.edu/~purcell/plink/
-
-.. _LGPL: http://www.gnu.org/copyleft/lesser.html
-
-.. _DOC: http://pngu.mgh.harvard.edu/~purcell/plink/anal.shtml#cc
-
-</help>
-</tool>
diff -r 3df68022beef2077798ed50a33afb03be75a841a -r c8b55344e779e8be93bae5d66f52128addd83003 tools/rgenetics/rgClean.py
--- a/tools/rgenetics/rgClean.py
+++ /dev/null
@@ -1,160 +0,0 @@
-"""
-# galaxy tool xml files can define a galaxy supplied output filename
-# that must be passed to the tool and used to return output
-# here, the plink log file is copied to that file and removed
-# took a while to figure this out!
-# use exec_before_job to give files sensible names
-#
-# ross april 14 2007
-# plink cleanup script
-# ross lazarus March 2007 for camp illumina whole genome data
-# note problems with multiple commands being ignored - eg --freq --missing --mendel
-# only the first seems to get done...
-#
-##Summary statistics versus inclusion criteria
-##
-##Feature As summary statistic As inclusion criteria
-##Missingness per individual --missing --mind N
-##Missingness per marker --missing --geno N
-##Allele frequency --freq --maf N
-##Hardy-Weinberg equilibrium --hardy --hwe N
-##Mendel error rates --mendel --me N M
-#
-# call as plinkClean.py $i $o $mind $geno $hwe $maf $mef $mei $outfile
-# note plinkClean_code.py does some renaming before the job starts
-
-
- <command interpreter="python2.4">
- rgClean.py '$input_file.extra_files_path' '$input_file.metadata.base_name' '$title' '$mind' '$geno' '$hwe' '$maf'
- '$mef' '$mei' '$out_file1' '$out_file1.files_path' '$userId'
-
-
-"""
-import sys,shutil,os,subprocess, glob, string, tempfile, time
-from rgutils import galhtmlprefix, timenow, plinke
-prog = os.path.split(sys.argv[0])[-1]
-myversion = 'January 4 2010'
-verbose=False
-
-
-def fixoutaff(outpath='',newaff='1'):
- """ quick way to create test data sets - set all aff to 1 or 2 for
- some hapmap data and then merge
- [rerla@beast galaxy]$ head tool-data/rg/library/pbed/affyHM_CEU.fam
- 1341 14 0 0 2 1
- 1341 2 13 14 2 1
- 1341 13 0 0 1 1
- 1340 9 0 0 1 1
- 1340 10 0 0 2 1
- """
- nchanged = 0
- fam = '%s.fam' % outpath
- famf = open(fam,'r')
- fl = famf.readlines()
- famf.close()
- for i,row in enumerate(fl):
- lrow = row.split()
- if lrow[-1] <> newaff:
- lrow[-1] = newaff
- fl[i] = ' '.join(lrow)
- fl[i] += '\n'
- nchanged += 1
- fo = open(fam,'w')
- fo.write(''.join(fl))
- fo.close()
- return nchanged
-
-
-
-def clean():
- """
- """
- if len(sys.argv) < 16:
- print >> sys.stdout, '## %s expected 12 params in sys.argv, got %d - %s' % (prog,len(sys.argv),sys.argv)
- print >> sys.stdout, """this script will filter a linkage format ped
- and map file containing genotypes. It takes 14 parameters - the plink --f parameter and"
- a new filename root for the output clean data followed by the mind,geno,hwe,maf, mef and mei"
- documented in the plink docs plus the file to be returned to Galaxy
- called as:
- <command interpreter="python">
- rgClean.py '$input_file.extra_files_path' '$input_file.metadata.base_name' '$title' '$mind'
- '$geno' '$hwe' '$maf' '$mef' '$mei' '$out_file1' '$out_file1.files_path'
- '$relfilter' '$afffilter' '$sexfilter' '$fixaff'
- </command>
-
- """
- sys.exit(1)
- plog = []
- inpath = sys.argv[1]
- inbase = sys.argv[2]
- killme = string.punctuation + string.whitespace
- trantab = string.maketrans(killme,'_'*len(killme))
- title = sys.argv[3].translate(trantab)
- mind = sys.argv[4]
- geno = sys.argv[5]
- hwe = sys.argv[6]
- maf = sys.argv[7]
- me1 = sys.argv[8]
- me2 = sys.argv[9]
- outfname = sys.argv[10]
- outfpath = sys.argv[11]
- relf = sys.argv[12]
- afff = sys.argv[13]
- sexf = sys.argv[14]
- fixaff = sys.argv[15]
- output = os.path.join(outfpath,outfname)
- outpath = os.path.join(outfpath,title)
- outprunepath = os.path.join(outfpath,'ldprune_%s' % title)
- try:
- os.makedirs(outfpath)
- except:
- pass
- bfile = os.path.join(inpath,inbase)
- outf = file(outfname,'w')
- vcl = [plinke,'--noweb','--bfile',bfile,'--make-bed','--out',
- outpath,'--set-hh-missing','--mind',mind,
- '--geno',geno,'--maf',maf,'--hwe',hwe,'--me',me1,me2]
- # yes - the --me parameter takes 2 values - mendels per snp and per family
- if relf == 'oo': # plink filters are what they leave...
- vcl.append('--filter-nonfounders') # leave only offspring
- elif relf == 'fo':
- vcl.append('--filter-founders')
- if afff == 'affonly':
- vcl.append('--filter-controls')
- elif relf == 'unaffonly':
- vcl.append('--filter-cases')
- if sexf == 'fsex':
- vcl.append('--filter-females')
- elif relf == 'msex':
- vcl.append('--filter-males')
- p=subprocess.Popen(' '.join(vcl),shell=True,cwd=outfpath)
- retval = p.wait()
- plog.append('%s started, called as %s' % (prog,' '.join(sys.argv)))
- outf.write(galhtmlprefix % prog)
- outf.write('<ul>\n')
- plogf = '%s.log' % os.path.join(outfpath,title)
- try:
- plogl = file(plogf,'r').readlines()
- plog += [x.strip() for x in plogl]
- except:
- plog += ['###Cannot open plink log file %s' % plogf,]
- # if fixaff, want to 'fix' the fam file
- if fixaff <> '0':
- nchanged = fixoutaff(outpath=outpath,newaff=fixaff)
- plog += ['## fixaff was requested %d subjects affection status changed to %s' % (nchanged,fixaff)]
- pf = file(plogf,'w')
- pf.write('\n'.join(plog))
- pf.close()
- globme = os.path.join(outfpath,'*')
- flist = glob.glob(globme)
- flist.sort()
- for i, data in enumerate( flist ):
- outf.write('<li><a href="%s">%s</a></li>\n' % (os.path.split(data)[-1],os.path.split(data)[-1]))
- outf.write('</ul>\n')
- outf.write("</ul></br></div></body></html>")
- outf.close()
-
-
-if __name__ == "__main__":
- clean()
-
diff -r 3df68022beef2077798ed50a33afb03be75a841a -r c8b55344e779e8be93bae5d66f52128addd83003 tools/rgenetics/rgClean.xml
--- a/tools/rgenetics/rgClean.xml
+++ /dev/null
@@ -1,155 +0,0 @@
-<tool id="rgClean1" name="Clean genotypes:">
- <requirements><requirement type="package">plink</requirement></requirements>
- <description>filter markers, subjects</description>
-
- <command interpreter="python">
- rgClean.py '$input_file.extra_files_path' '$input_file.metadata.base_name' '$title' '$mind'
- '$geno' '$hwe' '$maf' '$mef' '$mei' '$out_file1' '$out_file1.files_path'
- '$relfilter' '$afffilter' '$sexfilter' '$fixaff'
- </command>
-
- <inputs>
- <param name="input_file" type="data" label="RGenetics genotype library file in compressed Plink format"
- size="120" format="pbed" />
- <param name="title" type="text" size="80" label="Descriptive title for cleaned genotype file" value="Cleaned_data"/>
- <param name="geno" type="text" label="Maximum Missing Fraction: Markers" value="0.05" />
- <param name="mind" type="text" value="0.1" label="Maximum Missing Fraction: Subjects"/>
- <param name="mef" type="text" label="Maximum Mendel Error Rate: Family" value="0.05"/>
- <param name="mei" type="text" label="Maximum Mendel Error Rate: Marker" value="0.05"/>
- <param name="hwe" type="text" value="0" label="Smallest HWE p value (set to 0 for all)" />
- <param name="maf" type="text" value="0.01"
- label="Smallest Minor Allele Frequency (set to 0 for all)"/>
- <param name='relfilter' label = "Filter on pedigree relatedness" type="select"
- optional="false" size="132"
- help="Optionally remove related subjects if pedigree identifies founders and their offspring">
- <option value="all" selected='true'>No filter on relatedness</option>
- <option value="fo" >Keep Founders only (pedigree m/f ID = "0")</option>
- <option value="oo" >Keep Offspring only (one randomly chosen if >1 sibs in family)</option>
- </param>
- <param name='afffilter' label = "Filter on affection status" type="select"
- optional="false" size="132"
- help="Optionally remove affected or non affected subjects">
- <option value="allaff" selected='true'>No filter on affection status</option>
- <option value="affonly" >Keep Controls only (affection='1')</option>
- <option value="unaffonly" >Keep Cases only (affection='2')</option>
- </param>
- <param name='sexfilter' label = "Filter on gender" type="select"
- optional="false" size="132"
- help="Optionally remove all male or all female subjects">
- <option value="allsex" selected='true'>No filter on gender status</option>
- <option value="msex" >Keep Males only (pedigree gender='1')</option>
- <option value="fsex" >Keep Females only (pedigree gender='2')</option>
- </param>
- <param name="fixaff" type="text" value="0"
- label = "Change ALL subjects affection status to (0=no change,1=unaff,2=aff)"
- help="Use this option to switch the affection status to a new value for all output subjects" />
- </inputs>
-
- <outputs>
- <data format="pbed" name="out_file1" metadata_source="input_file" label="${title}_rgClean.pbed" />
- </outputs>
-
-<tests>
- <test>
- <param name='input_file' value='tinywga' ftype='pbed' >
- <metadata name='base_name' value='tinywga' />
- <composite_data value='tinywga.bim' />
- <composite_data value='tinywga.bed' />
- <composite_data value='tinywga.fam' />
- <edit_attributes type='name' value='tinywga' />
- </param>
- <param name='title' value='rgCleantest1' />
- <param name="geno" value="1" />
- <param name="mind" value="1" />
- <param name="mef" value="0" />
- <param name="mei" value="0" />
- <param name="hwe" value="0" />
- <param name="maf" value="0" />
- <param name="relfilter" value="all" />
- <param name="afffilter" value="allaff" />
- <param name="sexfilter" value="allsex" />
- <param name="fixaff" value="0" />
- <output name='out_file1' file='rgtestouts/rgClean/rgCleantest1.pbed' compare="diff" lines_diff="25" >
- <extra_files type="file" name='rgCleantest1.bim' value="rgtestouts/rgClean/rgCleantest1.bim" compare="diff" />
- <extra_files type="file" name='rgCleantest1.fam' value="rgtestouts/rgClean/rgCleantest1.fam" compare="diff" />
- <extra_files type="file" name='rgCleantest1.bed' value="rgtestouts/rgClean/rgCleantest1.bed" compare="diff" />
- </output>
- </test>
-</tests>
-<help>
-
-.. class:: infomark
-
-**Syntax**
-
-- **Genotype data** is the input genotype file chosen from your current history
-- **Descriptive title** is the name to use for the filtered output file
-- **Missfrac threshold: subjects** is the threshold for missingness by subject. Subjects with more than this fraction missing will be excluded from the import
-- **Missfrac threshold: markers** is the threshold for missingness by marker. Markers with more than this fraction missing will be excluded from the import
-- **MaxMendel Individuals** Mendel error fraction above which to exclude subjects with more than the specified fraction of mendelian errors in transmission (for family data only)
-- **MaxMendel Families** Mendel error fraction above which to exclude families with more than the specified fraction of mendelian errors in transmission (for family data only)
-- **HWE** is the threshold for HWE test p values below which the marker will not be imported. Set this to -1 and all markers will be imported regardless of HWE p value
-- **MAF** is the threshold for minor allele frequency - SNPs with lower MAF will be excluded
-- **Filters** for founders/offspring or affected/unaffected or males/females are optionally available if needed
-- **Change Affection** is only needed if you want to change the affection status for creating new analysis datasets
-
------
-
-**Attribution**
-
-This tool relies on the work of many people. It uses Plink http://pngu.mgh.harvard.edu/~purcell/plink/,
-and the R http://cran.r-project.org/ and
-Bioconductor http://www.bioconductor.org/ projects.
-respectively.
-
-In particular, http://pngu.mgh.harvard.edu/~purcell/plink/
-has excellent documentation describing the parameters you can set here.
-
-This implementation is a Galaxy tool wrapper around these third party applications.
-It was originally designed and written for family based data from the CAMP Illumina run of 2007 by
-ross lazarus (ross.lazarus(a)gmail.com) and incorporated into the rgenetics toolkit.
-
-Rgenetics merely exposes them, wrapping Plink so you can use it in Galaxy.
-
------
-
-**Summary**
-
-Reliable statistical inference depends on reliable data. Poor quality samples and markers
-may add more noise than signal, decreasing statistical power. Removing the worst of them
-can be done by setting thresholds for some of the commonly used technical quality measures
-for genotype data. Of course discordant replicate calls are also very informative but are not
-in scope here.
-
-Marker cleaning: Filters are available to remove markers below a specific minor allele
-frequency, beyond a Hardy Wienberg threshold, below a minor allele frequency threshold,
-or above a threshold for missingness. If family data are available, thresholds for Mendelian
-error can be set.
-
-Subject cleaning: Filters are available to remove subjects with many missing calls. Subjects and markers for family data can be filtered by proportions
-of Mendelian errors in observed transmission. Use the QC reporting tool to
-generate a comprehensive series of reports for quality control.
-
-Note that ancestry and cryptic relatedness should also be checked using the relevant tools.
-
------
-
-.. class:: infomark
-
-**Tip**
-
-You can check that you got what you asked for by running the QC tool to ensure that the distributions
-are truncated the way you expect. Note that you do not expect that the thresholds will be exactly
-what you set - some bad assays and subjects are out in multiple QC measures, so you sometimes have
-more samples or markers than you exactly set for each threshold. Finally, the ordering of
-operations matters and Plink is somewhat restrictive about what it will do on each pass
-of the data. At least it's fixed.
-
------
-
-This Galaxy tool was written by Ross Lazarus for the Rgenetics project
-It uses Plink for most calculations - for full Plink attribution, source code and documentation,
-please see http://pngu.mgh.harvard.edu/~purcell/plink/ plus some custom python code
-
-</help>
-</tool>
This diff is so big that we needed to truncate the remainder.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/78eaaca131e3/
Changeset: 78eaaca131e3
User: dan
Date: 2013-10-07 23:00:22
Summary: Fix for history updater when setting metadata
Affected #: 1 file
diff -r 4db4660f5b121eee69a822b9c1294e4316855715 -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 static/scripts/mvc/history/history-model.js
--- a/static/scripts/mvc/history/history-model.js
+++ b/static/scripts/mvc/history/history-model.js
@@ -152,7 +152,8 @@
var timeout;
// set up to keep pulling if this history in run/queue state
if( ( history.get( 'state' ) === HistoryDatasetAssociation.STATES.RUNNING )
- || ( history.get( 'state' ) === HistoryDatasetAssociation.STATES.QUEUED ) ){
+ || ( history.get( 'state' ) === HistoryDatasetAssociation.STATES.QUEUED )
+ || ( history.get( 'state' ) === HistoryDatasetAssociation.STATES.SETTING_METADATA )){
//|| ( history.hdas.running() ) ){
timeout = history.updateAfterDelay();
https://bitbucket.org/galaxy/galaxy-central/commits/f76c280cdee5/
Changeset: f76c280cdee5
User: dan
Date: 2013-10-07 23:00:29
Summary: Pack scripts
Affected #: 11 files
diff -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 -r f76c280cdee539b65315ac583f93bfa52feb790d static/scripts/packed/galaxy.base.js
--- a/static/scripts/packed/galaxy.base.js
+++ b/static/scripts/packed/galaxy.base.js
@@ -1,1 +1,1 @@
-(function(){var b=0;var c=["ms","moz","webkit","o"];for(var a=0;a<c.length&&!window.requestAnimationFrame;++a){window.requestAnimationFrame=window[c[a]+"RequestAnimationFrame"];window.cancelRequestAnimationFrame=window[c[a]+"CancelRequestAnimationFrame"]}if(!window.requestAnimationFrame){window.requestAnimationFrame=function(h,e){var d=new Date().getTime();var f=Math.max(0,16-(d-b));var g=window.setTimeout(function(){h(d+f)},f);b=d+f;return g}}if(!window.cancelAnimationFrame){window.cancelAnimationFrame=function(d){clearTimeout(d)}}}());if(!Array.indexOf){Array.prototype.indexOf=function(c){for(var b=0,a=this.length;b<a;b++){if(this[b]==c){return b}}return -1}}function obj_length(c){if(c.length!==undefined){return c.length}var b=0;for(var a in c){b++}return b}$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function make_popupmenu(b,c){var a=(b.data("menu_options"));b.data("menu_options",c);if(a){return}b.bind("click.show_popup",function(d){$(".popmenu-wrapper").remove();setTimeout(function(){var g=$("<ul class='dropdown-menu' id='"+b.attr("id")+"-menu'></ul>");var f=b.data("menu_options");if(obj_length(f)<=0){$("<li>No Options.</li>").appendTo(g)}$.each(f,function(j,i){if(i){var l=i.action||i;g.append($("<li></li>").append($("<a>").attr("href",i.url).html(j).click(l)))}else{g.append($("<li></li>").addClass("head").append($("<a href='#'></a>").html(j)))}});var h=$("<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'></div>").append(g).appendTo("body");var e=d.pageX-h.width()/2;e=Math.min(e,$(document).scrollLeft()+$(window).width()-$(h).width()-5);e=Math.max(e,$(document).scrollLeft()+5);h.css({top:d.pageY,left:e})},10);setTimeout(function(){var f=function(h){$(h).bind("click.close_popup",function(){$(".popmenu-wrapper").remove();h.unbind("click.close_popup")})};f($(window.document));f($(window.top.document));for(var e=window.top.frames.length;e--;){var g=$(window.top.frames[e].document);f(g)}},50);return false})}function make_popup_menus(a){a=a||document;$(a).find("div[popupmenu]").each(function(){var b={};var d=$(this);d.find("a").each(function(){var g=$(this),i=g.get(0),e=i.getAttribute("confirm"),f=i.getAttribute("href"),h=i.getAttribute("target");if(!f){b[g.text()]=null}else{b[g.text()]={url:f,action:function(){if(!e||confirm(e)){var j;if(h==="demo"){if(j===undefined||j.closed){j=window.open(f,h);j.creator=self}}else{g.trigger("click")}}}}}});var c=$(a).find("#"+d.attr("popupmenu"));c.find("a").bind("click",function(f){f.stopPropagation();return true});make_popupmenu(c,b);c.addClass("popup");d.remove()})}function naturalSort(j,h){var p=/(-?[0-9\.]+)/g,k=j.toString().toLowerCase()||"",g=h.toString().toLowerCase()||"",l=String.fromCharCode(0),n=k.replace(p,l+"$1"+l).split(l),e=g.replace(p,l+"$1"+l).split(l),d=(new Date(k)).getTime(),o=d?(new Date(g)).getTime():null;if(o){if(d<o){return -1}else{if(d>o){return 1}}}var m,f;for(var i=0,c=Math.max(n.length,e.length);i<c;i++){m=parseFloat(n[i])||n[i];f=parseFloat(e[i])||e[i];if(m<f){return -1}else{if(m>f){return 1}}}return 0}$.fn.refresh_select2=function(){var b=$(this);var a={width:"resolve",closeOnSelect:!b.is("[MULTIPLE]")};return b.select2(a)};function replace_big_select_inputs(a,c,b){if(!jQuery.fn.select2){return}if(a===undefined){a=20}if(c===undefined){c=3000}b=b||$("select");b.each(function(){var e=$(this);var d=e.find("option").length;if((d<a)||(d>c)){return}if(e.hasClass("no-autocomplete")){return}e.refresh_select2()})}$.fn.make_text_editable=function(g){var d=("num_cols" in g?g.num_cols:30),c=("num_rows" in g?g.num_rows:4),e=("use_textarea" in g?g.use_textarea:false),b=("on_finish" in g?g.on_finish:null),f=("help_text" in g?g.help_text:null);var a=$(this);a.addClass("editable-text").click(function(l){if($(this).children(":input").length>0){return}a.removeClass("editable-text");var i=function(m){a.find(":input").remove();if(m!==""){a.text(m)}else{a.html("<br>")}a.addClass("editable-text");if(b){b(m)}};var h=a.text(),k,j;if(e){k=$("<textarea/>").attr({rows:c,cols:d}).text($.trim(h)).keyup(function(m){if(m.keyCode===27){i(h)}});j=$("<button/>").text("Done").click(function(){i(k.val());return false})}else{k=$("<input type='text'/>").attr({value:$.trim(h),size:d}).blur(function(){i(h)}).keyup(function(m){if(m.keyCode===27){$(this).trigger("blur")}else{if(m.keyCode===13){i($(this).val())}}})}a.text("");a.append(k);if(j){a.append(j)}k.focus();k.select();l.stopPropagation()});if(f){a.attr("title",f).tooltip()}return a};function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).click(function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text($.trim(k))}else{j=$("<input type='text'></input>").attr({value:$.trim(k),size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){if(o!==""){l.text(o)}else{l.html("<em>None</em>")}if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStorage.get("history_expand_state");if(e){for(var g in e){$("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStorage.deleteKey("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id,h=$(this).children("div.historyItemBody"),i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){var k;if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){k=$.jStorage.get("history_expand_state");if(k){delete k[j];$.jStorage.set("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){k=$.jStorage.get("history_expand_state");if(!k){k={}}k[j]=true;$.jStorage.set("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStorage.get("history_expand_state");if(!h){h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStorage.set("history_expand_state",h)}).show()};b()}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length===0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!=="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.val("search tools")}}var GalaxyAsync=function(a){this.url_dict={};this.log_action=(a===undefined?false:a)};GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(document).ready(function(){$("select[refresh_on_change='true']").change(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");$(document).trigger("convert_to_values");a.get(0).form.submit()});$(":checkbox[refresh_on_change='true']").click(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");a.get(0).form.submit()});$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tooltip){$(".unified-panel-header [title]").tooltip({placement:"bottom"});$("[title]").tooltip({placement:"top"})}make_popup_menus();replace_big_select_inputs(20,1500);$("a").click(function(){var b=$(this);var c=(parent.frames&&parent.frames.galaxy_main);if((b.attr("target")=="galaxy_main")&&(!c)){var a=b.attr("href");if(a.indexOf("?")==-1){a+="?"}else{a+="&"}a+="use_panels=True";b.attr("href",a);b.attr("target","_self")}return b})});
\ No newline at end of file
+(function(){var b=0;var c=["ms","moz","webkit","o"];for(var a=0;a<c.length&&!window.requestAnimationFrame;++a){window.requestAnimationFrame=window[c[a]+"RequestAnimationFrame"];window.cancelRequestAnimationFrame=window[c[a]+"CancelRequestAnimationFrame"]}if(!window.requestAnimationFrame){window.requestAnimationFrame=function(h,e){var d=new Date().getTime();var f=Math.max(0,16-(d-b));var g=window.setTimeout(function(){h(d+f)},f);b=d+f;return g}}if(!window.cancelAnimationFrame){window.cancelAnimationFrame=function(d){clearTimeout(d)}}}());if(!Array.indexOf){Array.prototype.indexOf=function(c){for(var b=0,a=this.length;b<a;b++){if(this[b]==c){return b}}return -1}}function obj_length(c){if(c.length!==undefined){return c.length}var b=0;for(var a in c){b++}return b}$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function make_popupmenu(b,c){var a=(b.data("menu_options"));b.data("menu_options",c);if(a){return}b.bind("click.show_popup",function(d){$(".popmenu-wrapper").remove();setTimeout(function(){var g=$("<ul class='dropdown-menu' id='"+b.attr("id")+"-menu'></ul>");var f=b.data("menu_options");if(obj_length(f)<=0){$("<li>No Options.</li>").appendTo(g)}$.each(f,function(j,i){if(i){var l=i.action||i;g.append($("<li></li>").append($("<a>").attr("href",i.url).html(j).click(l)))}else{g.append($("<li></li>").addClass("head").append($("<a href='#'></a>").html(j)))}});var h=$("<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'></div>").append(g).appendTo("body");var e=d.pageX-h.width()/2;e=Math.min(e,$(document).scrollLeft()+$(window).width()-$(h).width()-5);e=Math.max(e,$(document).scrollLeft()+5);h.css({top:d.pageY,left:e})},10);setTimeout(function(){var f=function(h){$(h).bind("click.close_popup",function(){$(".popmenu-wrapper").remove();h.unbind("click.close_popup")})};f($(window.document));f($(window.top.document));for(var e=window.top.frames.length;e--;){var g=$(window.top.frames[e].document);f(g)}},50);return false})}function make_popup_menus(a){a=a||document;$(a).find("div[popupmenu]").each(function(){var b={};var d=$(this);d.find("a").each(function(){var g=$(this),i=g.get(0),e=i.getAttribute("confirm"),f=i.getAttribute("href"),h=i.getAttribute("target");if(!f){b[g.text()]=null}else{b[g.text()]={url:f,action:function(){if(!e||confirm(e)){if(h){window.open(f,h)}else{g.click()}}}}}});var c=$(a).find("#"+d.attr("popupmenu"));c.find("a").bind("click",function(f){f.stopPropagation();return true});make_popupmenu(c,b);c.addClass("popup");d.remove()})}function naturalSort(j,h){var p=/(-?[0-9\.]+)/g,k=j.toString().toLowerCase()||"",g=h.toString().toLowerCase()||"",l=String.fromCharCode(0),n=k.replace(p,l+"$1"+l).split(l),e=g.replace(p,l+"$1"+l).split(l),d=(new Date(k)).getTime(),o=d?(new Date(g)).getTime():null;if(o){if(d<o){return -1}else{if(d>o){return 1}}}var m,f;for(var i=0,c=Math.max(n.length,e.length);i<c;i++){m=parseFloat(n[i])||n[i];f=parseFloat(e[i])||e[i];if(m<f){return -1}else{if(m>f){return 1}}}return 0}$.fn.refresh_select2=function(){var b=$(this);var a={width:"resolve",closeOnSelect:!b.is("[MULTIPLE]")};return b.select2(a)};function replace_big_select_inputs(a,c,b){if(!jQuery.fn.select2){return}if(a===undefined){a=20}if(c===undefined){c=3000}b=b||$("select");b.each(function(){var e=$(this);var d=e.find("option").length;if((d<a)||(d>c)){return}if(e.hasClass("no-autocomplete")){return}e.refresh_select2()})}$.fn.make_text_editable=function(g){var d=("num_cols" in g?g.num_cols:30),c=("num_rows" in g?g.num_rows:4),e=("use_textarea" in g?g.use_textarea:false),b=("on_finish" in g?g.on_finish:null),f=("help_text" in g?g.help_text:null);var a=$(this);a.addClass("editable-text").click(function(l){if($(this).children(":input").length>0){return}a.removeClass("editable-text");var i=function(m){a.find(":input").remove();if(m!==""){a.text(m)}else{a.html("<br>")}a.addClass("editable-text");if(b){b(m)}};var h=a.text(),k,j;if(e){k=$("<textarea/>").attr({rows:c,cols:d}).text($.trim(h)).keyup(function(m){if(m.keyCode===27){i(h)}});j=$("<button/>").text("Done").click(function(){i(k.val());return false})}else{k=$("<input type='text'/>").attr({value:$.trim(h),size:d}).blur(function(){i(h)}).keyup(function(m){if(m.keyCode===27){$(this).trigger("blur")}else{if(m.keyCode===13){i($(this).val())}}})}a.text("");a.append(k);if(j){a.append(j)}k.focus();k.select();l.stopPropagation()});if(f){a.attr("title",f).tooltip()}return a};function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).click(function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text($.trim(k))}else{j=$("<input type='text'></input>").attr({value:$.trim(k),size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){if(o!==""){l.text(o)}else{l.html("<em>None</em>")}if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStorage.get("history_expand_state");if(e){for(var g in e){$("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStorage.deleteKey("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id,h=$(this).children("div.historyItemBody"),i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){var k;if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){k=$.jStorage.get("history_expand_state");if(k){delete k[j];$.jStorage.set("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){k=$.jStorage.get("history_expand_state");if(!k){k={}}k[j]=true;$.jStorage.set("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStorage.get("history_expand_state");if(!h){h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStorage.set("history_expand_state",h)}).show()};b()}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length===0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!=="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.val("search tools")}}var GalaxyAsync=function(a){this.url_dict={};this.log_action=(a===undefined?false:a)};GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(document).ready(function(){$("select[refresh_on_change='true']").change(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");$(document).trigger("convert_to_values");a.get(0).form.submit()});$(":checkbox[refresh_on_change='true']").click(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");a.get(0).form.submit()});$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tooltip){$(".unified-panel-header [title]").tooltip({placement:"bottom"});$("[title]").tooltip({placement:"top"})}make_popup_menus();replace_big_select_inputs(20,1500);$("a").click(function(){var b=$(this);var c=(parent.frames&&parent.frames.galaxy_main);if((b.attr("target")=="galaxy_main")&&(!c)){var a=b.attr("href");if(a.indexOf("?")==-1){a+="?"}else{a+="&"}a+="use_panels=True";b.attr("href",a);b.attr("target","_self")}return b})});
\ No newline at end of file
diff -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 -r f76c280cdee539b65315ac583f93bfa52feb790d static/scripts/packed/galaxy.grids.js
--- a/static/scripts/packed/galaxy.grids.js
+++ b/static/scripts/packed/galaxy.grids.js
@@ -1,1 +1,1 @@
-jQuery.ajaxSettings.traditional=true;var Grid=Backbone.Model.extend({defaults:{url_base:"",async:false,async_ops:[],categorical_filters:[],filters:{},sort_key:null,show_item_checkboxes:false,cur_page:1,num_pages:1,operation:undefined,item_ids:undefined},can_async_op:function(a){return _.indexOf(this.attributes.async_ops,a)!==-1},add_filter:function(e,f,b){if(b){var c=this.attributes.filters[e],a;if(c===null||c===undefined){a=f}else{if(typeof(c)=="string"){if(c=="All"){a=f}else{var d=[];d[0]=c;d[1]=f;a=d}}else{a=c;a.push(f)}}this.attributes.filters[e]=a}else{this.attributes.filters[e]=f}},remove_filter:function(b,e){var a=this.attributes.filters[b];if(a===null||a===undefined){return false}var d=true;if(typeof(a)==="string"){if(a=="All"){d=false}else{delete this.attributes.filters[b]}}else{var c=_.indexOf(a,e);if(c!==-1){a.splice(c,1)}else{d=false}}return d},get_url_data:function(){var a={async:this.attributes.async,sort:this.attributes.sort_key,page:this.attributes.cur_page,show_item_checkboxes:this.attributes.show_item_checkboxes};if(this.attributes.operation){a.operation=this.attributes.operation}if(this.attributes.item_ids){a.id=this.attributes.item_ids}var b=this;_.each(_.keys(b.attributes.filters),function(c){a["f-"+c]=b.attributes.filters[c]});return a}});function init_operation_buttons(){$("input[name=operation]:submit").each(function(){$(this).click(function(){var b=$(this).val();var a=[];$("input[name=id]:checked").each(function(){a.push($(this).val())});do_operation(b,a)})})}function init_grid_controls(){init_operation_buttons();$(".submit-image").each(function(){$(this).mousedown(function(){$(this).addClass("gray-background")});$(this).mouseup(function(){$(this).removeClass("gray-background")})});$(".sort-link").each(function(){$(this).click(function(){set_sort_condition($(this).attr("sort_key"));return false})});$(".page-link > a").each(function(){$(this).click(function(){set_page($(this).attr("page_num"));return false})});$(".categorical-filter > a").each(function(){$(this).click(function(){set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});$(".text-filter-form").each(function(){$(this).submit(function(){var d=$(this).attr("column_key");var c=$("#input-"+d+"-filter");var e=c.val();c.val("");add_filter_condition(d,e,true);return false})});var a=$("#input-tags-filter");if(a.length){a.autocomplete(history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var b=$("#input-name-filter");if(b.length){b.autocomplete(history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}$(".advanced-search-toggle").each(function(){$(this).click(function(){$("#standard-search").slideToggle("fast");$("#advanced-search").slideToggle("fast");return false})})}function init_grid_elements(){$(".grid").each(function(){var b=$(this).find("input.grid-row-select-checkbox");var a=$(this).find("span.grid-selected-count");var c=function(){a.text($(b).filter(":checked").length)};$(b).each(function(){$(this).change(c)});c()});$(".label").each(function(){var a=$(this).attr("href");if(a!==undefined&&a.indexOf("operation=")!=-1){$(this).click(function(){do_operation_from_href($(this).attr("href"));return false})}});$(".community_rating_star").rating({});make_popup_menus()}function go_page_one(){var a=grid.get("cur_page");if(a!==null&&a!==undefined&&a!=="all"){grid.set("cur_page",1)}}function add_filter_condition(c,e,a){if(e===""){return false}grid.add_filter(c,e,a);var d=$("<span>"+e+"<a href='javascript:void(0);'><span class='delete-search-icon' /></a></span>");d.addClass("text-filter-val");d.click(function(){grid.remove_filter(c,e);$(this).remove();go_page_one();update_grid()});var b=$("#"+c+"-filtering-criteria");b.append(d);go_page_one();update_grid()}function add_tag_to_grid_filter(c,b){var a=c+(b!==undefined&&b!==""?":"+b:"");$("#advanced-search").show("fast");add_filter_condition("tags",a,true)}function set_sort_condition(f){var e=grid.get("sort_key");var d=f;if(e.indexOf(f)!==-1){if(e.substring(0,1)!=="-"){d="-"+f}else{}}$(".sort-arrow").remove();var c=(d.substring(0,1)=="-")?"↑":"↓";var a=$("<span>"+c+"</span>").addClass("sort-arrow");var b=$("#"+f+"-header");b.append(a);grid.set("sort_key",d);go_page_one();update_grid()}function set_categorical_filter(b,d){var a=grid.get("categorical_filters")[b],c=grid.get("filters")[b];$("."+b+"-filter").each(function(){var h=$.trim($(this).text());var f=a[h];var g=f[b];if(g==d){$(this).empty();$(this).addClass("current-filter");$(this).append(h)}else{if(g==c){$(this).empty();var e=$("<a href='#'>"+h+"</a>");e.click(function(){set_categorical_filter(b,g)});$(this).removeClass("current-filter");$(this).append(e)}}});grid.add_filter(b,d);go_page_one();update_grid()}function set_page(a){$(".page-link").each(function(){var g=$(this).attr("id"),e=parseInt(g.split("-")[2],10),c=grid.get("cur_page"),f;if(e===a){f=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(f)}else{if(e===c){f=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var d=$("<a href='#'>"+f+"</a>");d.click(function(){set_page(e)});$(this).append(d)}}});var b=true;if(a==="all"){grid.set("cur_page",a);b=false}else{grid.set("cur_page",parseInt(a,10))}update_grid(b)}function do_operation(b,a){b=b.toLowerCase();grid.set({operation:b,item_ids:a});if(grid.can_async_op(b)){update_grid(true)}else{go_to_URL()}}function do_operation_from_href(c){var f=c.split("?");if(f.length>1){var a=f[1];var e=a.split("&");var b=null;var g=-1;for(var d=0;d<e.length;d++){if(e[d].indexOf("operation")!=-1){b=e[d].split("=")[1]}else{if(e[d].indexOf("id")!=-1){g=e[d].split("=")[1]}}}do_operation(b,g);return false}}function go_to_URL(){grid.set("async",false);window.location=grid.get("url_base")+"?"+$.param(grid.get_url_data())}function update_grid(a){if(!grid.get("async")){go_to_URL();return}var b=(grid.get("operation")?"POST":"GET");$(".loading-elt-overlay").show();$.ajax({type:b,url:grid.get("url_base"),data:grid.get_url_data(),error:function(){alert("Grid refresh failed")},success:function(d){var c=d.split("*****");$("#grid-table-body").html(c[0]);$("#grid-table-footer").html(c[1]);$("#grid-table-body").trigger("update");init_grid_elements();init_operation_buttons();make_popup_menus();$(".loading-elt-overlay").hide();var e=$.trim(c[2]);if(e!==""){$("#grid-message").html(e).show();setTimeout(function(){$("#grid-message").hide()},5000)}},complete:function(){grid.set({operation:undefined,item_ids:undefined})}})}function check_all_items(){var a=document.getElementById("check_all"),b=document.getElementsByTagName("input"),d=0,c;if(a.checked===true){for(c=0;c<b.length;c++){if(b[c].name.indexOf("id")!==-1){b[c].checked=true;d++}}}else{for(c=0;c<b.length;c++){if(b[c].name.indexOf("id")!==-1){b[c].checked=false}}}init_grid_elements()}function submit_operation(b,c){var a=$('input[name="id"]:checked').length;if(!a>0){return false}if(c!="None"&&c!=""){if(!confirm(c)){return false}}$("#operation").val(b.value);b.form.submit();return true};
\ No newline at end of file
+jQuery.ajaxSettings.traditional=true;var Grid=Backbone.Model.extend({defaults:{url_base:"",async:false,async_ops:[],categorical_filters:[],filters:{},sort_key:null,show_item_checkboxes:false,cur_page:1,num_pages:1,operation:undefined,item_ids:undefined},can_async_op:function(a){return _.indexOf(this.attributes.async_ops,a)!==-1},add_filter:function(e,f,b){if(b){var c=this.attributes.filters[e],a;if(c===null||c===undefined){a=f}else{if(typeof(c)=="string"){if(c=="All"){a=f}else{var d=[];d[0]=c;d[1]=f;a=d}}else{a=c;a.push(f)}}this.attributes.filters[e]=a}else{this.attributes.filters[e]=f}},remove_filter:function(b,e){var a=this.attributes.filters[b];if(a===null||a===undefined){return false}var d=true;if(typeof(a)==="string"){if(a=="All"){d=false}else{delete this.attributes.filters[b]}}else{var c=_.indexOf(a,e);if(c!==-1){a.splice(c,1)}else{d=false}}return d},get_url_data:function(){var a={async:this.attributes.async,sort:this.attributes.sort_key,page:this.attributes.cur_page,show_item_checkboxes:this.attributes.show_item_checkboxes};if(this.attributes.operation){a.operation=this.attributes.operation}if(this.attributes.item_ids){a.id=this.attributes.item_ids}var b=this;_.each(_.keys(b.attributes.filters),function(c){a["f-"+c]=b.attributes.filters[c]});return a}});function init_operation_buttons(){$("input[name=operation]:submit").each(function(){$(this).click(function(){var b=$(this).val();var a=[];$("input[name=id]:checked").each(function(){a.push($(this).val())});do_operation(b,a)})})}function init_grid_controls(){init_operation_buttons();$(".submit-image").each(function(){$(this).mousedown(function(){$(this).addClass("gray-background")});$(this).mouseup(function(){$(this).removeClass("gray-background")})});$(".sort-link").each(function(){$(this).click(function(){set_sort_condition($(this).attr("sort_key"));return false})});$(".page-link > a").each(function(){$(this).click(function(){set_page($(this).attr("page_num"));return false})});$(".categorical-filter > a").each(function(){$(this).click(function(){set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});$(".text-filter-form").each(function(){$(this).submit(function(){var d=$(this).attr("column_key");var c=$("#input-"+d+"-filter");var e=c.val();c.val("");add_filter_condition(d,e,true);return false})});var a=$("#input-tags-filter");if(a.length){a.autocomplete(history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var b=$("#input-name-filter");if(b.length){b.autocomplete(history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}$(".advanced-search-toggle").each(function(){$(this).click(function(){$("#standard-search").slideToggle("fast");$("#advanced-search").slideToggle("fast");return false})})}function init_grid_elements(){$(".grid").each(function(){var b=$(this).find("input.grid-row-select-checkbox");var a=$(this).find("span.grid-selected-count");var c=function(){a.text($(b).filter(":checked").length)};$(b).each(function(){$(this).change(c)});c()});$(".label").each(function(){var a=$(this).attr("href");if(a!==undefined&&a.indexOf("operation=")!=-1){$(this).click(function(){do_operation_from_href($(this).attr("href"));return false})}});if($(".community_rating_star").length!==0){$(".community_rating_star").rating({})}make_popup_menus()}function go_page_one(){var a=grid.get("cur_page");if(a!==null&&a!==undefined&&a!=="all"){grid.set("cur_page",1)}}function add_filter_condition(c,e,a){if(e===""){return false}grid.add_filter(c,e,a);var d=$("<span>"+e+"<a href='javascript:void(0);'><span class='delete-search-icon' /></a></span>");d.addClass("text-filter-val");d.click(function(){grid.remove_filter(c,e);$(this).remove();go_page_one();update_grid()});var b=$("#"+c+"-filtering-criteria");b.append(d);go_page_one();update_grid()}function add_tag_to_grid_filter(c,b){var a=c+(b!==undefined&&b!==""?":"+b:"");$("#advanced-search").show("fast");add_filter_condition("tags",a,true)}function set_sort_condition(f){var e=grid.get("sort_key");var d=f;if(e.indexOf(f)!==-1){if(e.substring(0,1)!=="-"){d="-"+f}else{}}$(".sort-arrow").remove();var c=(d.substring(0,1)=="-")?"↑":"↓";var a=$("<span>"+c+"</span>").addClass("sort-arrow");var b=$("#"+f+"-header");b.append(a);grid.set("sort_key",d);go_page_one();update_grid()}function set_categorical_filter(b,d){var a=grid.get("categorical_filters")[b],c=grid.get("filters")[b];$("."+b+"-filter").each(function(){var h=$.trim($(this).text());var f=a[h];var g=f[b];if(g==d){$(this).empty();$(this).addClass("current-filter");$(this).append(h)}else{if(g==c){$(this).empty();var e=$("<a href='#'>"+h+"</a>");e.click(function(){set_categorical_filter(b,g)});$(this).removeClass("current-filter");$(this).append(e)}}});grid.add_filter(b,d);go_page_one();update_grid()}function set_page(a){$(".page-link").each(function(){var g=$(this).attr("id"),e=parseInt(g.split("-")[2],10),c=grid.get("cur_page"),f;if(e===a){f=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(f)}else{if(e===c){f=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var d=$("<a href='#'>"+f+"</a>");d.click(function(){set_page(e)});$(this).append(d)}}});var b=true;if(a==="all"){grid.set("cur_page",a);b=false}else{grid.set("cur_page",parseInt(a,10))}update_grid(b)}function do_operation(b,a){b=b.toLowerCase();grid.set({operation:b,item_ids:a});if(grid.can_async_op(b)){update_grid(true)}else{go_to_URL()}}function do_operation_from_href(c){var f=c.split("?");if(f.length>1){var a=f[1];var e=a.split("&");var b=null;var g=-1;for(var d=0;d<e.length;d++){if(e[d].indexOf("operation")!=-1){b=e[d].split("=")[1]}else{if(e[d].indexOf("id")!=-1){g=e[d].split("=")[1]}}}do_operation(b,g);return false}}function go_to_URL(){grid.set("async",false);window.location=grid.get("url_base")+"?"+$.param(grid.get_url_data())}function update_grid(a){if(!grid.get("async")){go_to_URL();return}var b=(grid.get("operation")?"POST":"GET");$(".loading-elt-overlay").show();$.ajax({type:b,url:grid.get("url_base"),data:grid.get_url_data(),error:function(){alert("Grid refresh failed")},success:function(d){var c=d.split("*****");$("#grid-table-body").html(c[0]);$("#grid-table-footer").html(c[1]);$("#grid-table-body").trigger("update");init_grid_elements();init_operation_buttons();make_popup_menus();$(".loading-elt-overlay").hide();var e=$.trim(c[2]);if(e!==""){$("#grid-message").html(e).show();setTimeout(function(){$("#grid-message").hide()},5000)}},complete:function(){grid.set({operation:undefined,item_ids:undefined})}})}function check_all_items(){var a=document.getElementById("check_all"),b=document.getElementsByTagName("input"),d=0,c;if(a.checked===true){for(c=0;c<b.length;c++){if(b[c].name.indexOf("id")!==-1){b[c].checked=true;d++}}}else{for(c=0;c<b.length;c++){if(b[c].name.indexOf("id")!==-1){b[c].checked=false}}}init_grid_elements()}function submit_operation(b,c){var a=$('input[name="id"]:checked').length;if(!a>0){return false}if(c!="None"&&c!=""){if(!confirm(c)){return false}}$("#operation").val(b.value);b.form.submit();return true};
\ No newline at end of file
diff -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 -r f76c280cdee539b65315ac583f93bfa52feb790d static/scripts/packed/galaxy.modal.js
--- a/static/scripts/packed/galaxy.modal.js
+++ b/static/scripts/packed/galaxy.modal.js
@@ -1,1 +1,1 @@
-define(["libs/backbone/backbone-relational"],function(){var a=Backbone.View.extend({el_main:"#everything",defaults:{title:"galaxy-modal",body:"No content available."},initialize:function(b){if(b){this.create(b)}},show:function(c){if(c){this.create(c)}var b=(this.$el).find(".modal-body");b.css("max-height",$(document).height()/2);if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.$el.fadeOut("fast");this.visible=false},create:function(c){c=_.defaults(c,this.defaults);if(c.body=="progress"){c.body='<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>'}if(this.$el){this.$el.remove()}this.setElement(this.template(c.title));var b=(this.$el).find(".modal-body");var e=(this.$el).find(".modal-footer");var d=(this.$el).find(".buttons");b.append($(c.body));if(c.height){b.css("height",c.height)}if(c.buttons){$.each(c.buttons,function(f,g){d.append($('<button id="'+String(f).toLowerCase()+'"></button>').text(f).click(g)).append(" ")})}else{e.hide()}$(this.el_main).append($(this.el))},enable:function(b){$(this.el).find("#"+String(b).toLowerCase()).prop("disabled",false)},disable:function(b){$(this.el).find("#"+String(b).toLowerCase()).prop("disabled",true)},template:function(b){return'<div class="modal in"><div class="modal-backdrop in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><span><h3 class="title">'+b+'</h3></span></div><div class="modal-body"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{GalaxyModal:a}});
\ No newline at end of file
+define(["libs/backbone/backbone-relational"],function(){var a=Backbone.View.extend({elMain:"#everything",optionsDefaults:{title:"galaxy-modal",body:"No content available."},initialize:function(b){if(b){this.create(b)}},show:function(c){this.initialize(c);var b=(this.$el).find(".modal-body");b.css("max-height",$(document).height()/2);if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.$el.fadeOut("fast");this.visible=false},create:function(c){c=_.defaults(c,this.optionsDefault);if(c.body=="progress"){c.body='<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>'}if(this.$el){this.$el.remove()}this.setElement(this.template(c.title));var b=(this.$el).find(".modal-body");var e=(this.$el).find(".modal-footer");var d=(this.$el).find(".buttons");b.append($(c.body));if(c.height){b.css("height",c.height)}if(c.buttons){$.each(c.buttons,function(f,g){d.append($('<button id="'+String(f).toLowerCase()+'"></button>').text(f).click(g)).append(" ")})}else{e.hide()}$(this.elMain).append($(this.el))},enable:function(b){$(this.el).find("#"+String(b).toLowerCase()).prop("disabled",false)},disable:function(b){$(this.el).find("#"+String(b).toLowerCase()).prop("disabled",true)},scrollTop:function(){return $(this.el).find(".modal-body").scrollTop()},template:function(b){return'<div class="modal in"><div class="modal-backdrop in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><span><h3 class="title">'+b+'</h3></span></div><div class="modal-body"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{GalaxyModal:a}});
\ No newline at end of file
diff -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 -r f76c280cdee539b65315ac583f93bfa52feb790d static/scripts/packed/mvc/data.js
--- a/static/scripts/packed/mvc/data.js
+++ b/static/scripts/packed/mvc/data.js
@@ -1,1 +1,1 @@
-define(["libs/backbone/backbone-relational"],function(){var d=Backbone.RelationalModel.extend({});var e=Backbone.RelationalModel.extend({defaults:{id:"",type:"",name:"",hda_ldda:"hda",metadata:null},initialize:function(){this.on("change",this._set_metadata,this)},_set_metadata:function(){var i=new d();_.each(_.keys(this.attributes),function(j){if(j.indexOf("metadata_")===0){var l=j.split("metadata_")[1];i.set(l,this.attributes[j]);delete this.attributes[j]}},this);this.set("metadata",i,{silent:true})},get_metadata:function(i){return this.attributes.metadata.get(i)},urlRoot:galaxy_config.root+"api/datasets"});var c=e.extend({defaults:_.extend({},e.prototype.defaults,{chunk_url:null,first_data_chunk:null,chunk_index:-1,at_eof:false}),initialize:function(i){e.prototype.initialize.call(this);this.attributes.chunk_index=(this.attributes.first_data_chunk?1:0)},get_next_chunk:function(){if(this.attributes.at_eof){return null}var i=this,j=$.Deferred();$.getJSON(this.attributes.chunk_url,{chunk:i.attributes.chunk_index++}).success(function(k){var l;if(k.ck_data!==""){l=k}else{i.attributes.at_eof=true;l=null}j.resolve(l)});return j}});var g=Backbone.Collection.extend({model:e});var f=Backbone.View.extend({initialize:function(i){new b(i)},render:function(){var m=$("<table/>").attr({id:"content_table",cellpadding:0});this.$el.append(m);var i=this.model.get_metadata("column_names");if(i){m.append("<tr><th>"+i.join("</th><th>")+"</th></tr>")}var k=this.model.get("first_data_chunk");if(k){this._renderChunk(k)}var j=this,n=_.find(this.$el.parents(),function(o){return $(o).css("overflow")==="auto"}),l=false;if(!n){n=window}n=$(n);n.scroll(function(){if(!l&&(j.$el.height()-n.scrollTop()-n.height()<=0)){l=true;$.when(j.model.get_next_chunk()).then(function(o){if(o){j._renderChunk(o);l=false}})}});$("#loading_indicator").ajaxStart(function(){$(this).show()}).ajaxStop(function(){$(this).hide()})},_renderCell:function(k,i,l){var j=this.model.get_metadata("column_types");if(l!==undefined){return $("<td>").attr("colspan",l).addClass("stringalign").text(k)}else{if(j[i]==="str"||j==="list"){return $("<td>").addClass("stringalign").text(k)}else{return $("<td>").text(k)}}},_renderRow:function(i){var j=i.split("\t"),l=$("<tr>"),k=this.model.get_metadata("columns");if(j.length===k){_.each(j,function(n,m){l.append(this._renderCell(n,m))},this)}else{if(j.length>k){_.each(j.slice(0,k-1),function(n,m){l.append(this._renderCell(n,m))},this);l.append(this._renderCell(j.slice(k-1).join("\t"),k-1))}else{if(k>5&&j.length===k-1){_.each(j,function(n,m){l.append(this._renderCell(n,m))},this);l.append($("<td>"))}else{l.append(this._renderCell(i,0,k))}}}return l},_renderChunk:function(i){var j=this.$el.find("table");_.each(i.ck_data.split("\n"),function(k,l){j.append(this._renderRow(k))},this)}});var b=Backbone.View.extend({col:{chrom:null,start:null,end:null},url_viz:null,dataset_id:null,genome_build:null,data_type:null,initialize:function(k){var j=k.model.attributes;var m=k.model.attributes.metadata.attributes;if(typeof j.data_type!=="undefined"){this.data_type=j.data_type}else{console.log("TabularButtonTrackster : Data type missing.")}if(this.data_type=="bed"){if(typeof m.chromCol!=="undefined"||typeof m.startCol!=="undefined"||typeof m.endCol!=="undefined"){this.col.chrom=m.chromCol-1;this.col.start=m.startCol-1;this.col.end=m.endCol-1}else{console.log("TabularButtonTrackster : Bed-file metadata incomplete.")}}if(this.data_type=="vcf"){function l(o,p){for(var n=0;n<p.length;n++){if(p[n].match(o)){return n}}return -1}this.col.chrom=l("Chrom",m.column_names);this.col.start=l("Pos",m.column_names);this.col.end=null;if(this.col.chrom==-1||this.col.start==-1){console.log("TabularButtonTrackster : VCF-file metadata incomplete.")}}if(this.col.chrom===null){console.log("TabularButtonTrackster : Chromosome column undefined.");return}if(typeof k.model.attributes.id==="undefined"){console.log("TabularButtonTrackster : Dataset identification is missing.")}else{this.dataset_id=k.model.attributes.id}if(typeof k.model.attributes.url_viz==="undefined"){console.log("TabularButtonTrackster : Url for visualization controller is missing.")}else{this.url_viz=k.model.attributes.url_viz}if(typeof k.model.attributes.genome_build!=="undefined"){this.genome_build=k.model.attributes.genome_build}var i=new IconButtonView({model:new IconButton({title:"Visualize",icon_class:"chart_curve",id:"btn_viz"})});this.$el.append(i.render().$el);$("#btn_viz").hide()},events:{"mouseover tr":"btn_viz_show",mouseleave:"btn_viz_hide"},btn_viz_show:function(n){function m(s){return !isNaN(parseFloat(s))&&isFinite(s)}if(this.col.chrom===null){return}var r=$(n.target).parent();var o=r.children().eq(this.col.chrom).html();var i=r.children().eq(this.col.start).html();var k=this.col.end?r.children().eq(this.col.end).html():i;if(!o.match("^#")&&o!==""&&m(i)){var q={dataset_id:this.dataset_id,gene_region:o+":"+i+"-"+k};var l=r.offset();var j=l.left-10;var p=l.top-$(window).scrollTop();$("#btn_viz").css({position:"fixed",top:p+"px",left:j+"px"});$("#btn_viz").off("click");$("#btn_viz").click(this.create_trackster_action(this.url_viz,q,this.genome_build));$("#btn_viz").show()}else{$("#btn_viz").hide()}},btn_viz_hide:function(){$("#btn_viz").hide()},create_trackster_action:function(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.show_modal(("View Data in a New or Saved Visualization"),"",{Cancel:function(){n.hide_modal()},"View in saved visualization":function(){n.show_modal(("Add Data to Saved Visualization"),m,{Cancel:function(){n.hide_modal()},"Add to visualization":function(){$(n.document).find("input[name=id]:checked").each(function(){var o=$(this).val();k.id=o;n.Galaxy.frame_manager.frame_new({title:"Trackster",type:"url",content:i+"/trackster?"+$.param(k)});n.hide_modal()})}})},"View in new visualization":function(){var o=i+"/trackster?"+$.param(k);n.Galaxy.frame_manager.frame_new({title:"Trackster",type:"url",content:o});n.hide_modal()}})}});return false}}});var a=function(l,j,m,i){var k=new j({model:new l(m)});k.render();if(i){i.append(k.$el)}return k};var h=function(k,i){var j=$("<div/>").appendTo(i);return new f({el:j,model:new c(k)}).render()};return{Dataset:e,TabularDataset:c,DatasetCollection:g,TabularDatasetChunkedView:f,createTabularDatasetChunkedView:h}});
\ No newline at end of file
+define(["libs/backbone/backbone-relational"],function(){var d=Backbone.RelationalModel.extend({});var e=Backbone.RelationalModel.extend({defaults:{id:"",type:"",name:"",hda_ldda:"hda",metadata:null},initialize:function(){this.on("change",this._set_metadata,this)},_set_metadata:function(){var i=new d();_.each(_.keys(this.attributes),function(j){if(j.indexOf("metadata_")===0){var l=j.split("metadata_")[1];i.set(l,this.attributes[j]);delete this.attributes[j]}},this);this.set("metadata",i,{silent:true})},get_metadata:function(i){return this.attributes.metadata.get(i)},urlRoot:galaxy_config.root+"api/datasets"});var c=e.extend({defaults:_.extend({},e.prototype.defaults,{chunk_url:null,first_data_chunk:null,chunk_index:-1,at_eof:false}),initialize:function(i){e.prototype.initialize.call(this);this.attributes.chunk_index=(this.attributes.first_data_chunk?1:0)},get_next_chunk:function(){if(this.attributes.at_eof){return null}var i=this,j=$.Deferred();$.getJSON(this.attributes.chunk_url,{chunk:i.attributes.chunk_index++}).success(function(k){var l;if(k.ck_data!==""){l=k}else{i.attributes.at_eof=true;l=null}j.resolve(l)});return j}});var g=Backbone.Collection.extend({model:e});var f=Backbone.View.extend({initialize:function(i){new b(i)},render:function(){var m=$("<table/>").attr({id:"content_table",cellpadding:0});this.$el.append(m);var i=this.model.get_metadata("column_names");if(i){m.append("<tr><th>"+i.join("</th><th>")+"</th></tr>")}var k=this.model.get("first_data_chunk");if(k){this._renderChunk(k)}var j=this,n=_.find(this.$el.parents(),function(o){return $(o).css("overflow")==="auto"}),l=false;if(!n){n=window}n=$(n);n.scroll(function(){if(!l&&(j.$el.height()-n.scrollTop()-n.height()<=0)){l=true;$.when(j.model.get_next_chunk()).then(function(o){if(o){j._renderChunk(o);l=false}})}});$("#loading_indicator").ajaxStart(function(){$(this).show()}).ajaxStop(function(){$(this).hide()})},_renderCell:function(l,i,m){var j=$("<td>").text(l);var k=this.model.get_metadata("column_types");if(m!==undefined){j.attr("colspan",m).addClass("stringalign")}else{if(k!==undefined&&i<k.length){if(k[i]==="str"||k==="list"){j.addClass("stringalign")}}}return j},_renderRow:function(i){var j=i.split("\t"),l=$("<tr>"),k=this.model.get_metadata("columns");if(j.length===k){_.each(j,function(n,m){l.append(this._renderCell(n,m))},this)}else{if(j.length>k){_.each(j.slice(0,k-1),function(n,m){l.append(this._renderCell(n,m))},this);l.append(this._renderCell(j.slice(k-1).join("\t"),k-1))}else{if(k>5&&j.length===k-1){_.each(j,function(n,m){l.append(this._renderCell(n,m))},this);l.append($("<td>"))}else{l.append(this._renderCell(i,0,k))}}}return l},_renderChunk:function(i){var j=this.$el.find("table");_.each(i.ck_data.split("\n"),function(k,l){j.append(this._renderRow(k))},this)}});var b=Backbone.View.extend({col:{chrom:null,start:null,end:null},url_viz:null,dataset_id:null,genome_build:null,data_type:null,initialize:function(k){var j=k.model.attributes;var m=k.model.attributes.metadata.attributes;if(typeof j.data_type!=="undefined"){this.data_type=j.data_type}else{console.log("TabularButtonTrackster : Data type missing.")}if(this.data_type=="bed"){if(typeof m.chromCol!=="undefined"||typeof m.startCol!=="undefined"||typeof m.endCol!=="undefined"){this.col.chrom=m.chromCol-1;this.col.start=m.startCol-1;this.col.end=m.endCol-1}else{console.log("TabularButtonTrackster : Bed-file metadata incomplete.")}}if(this.data_type=="vcf"){function l(o,p){for(var n=0;n<p.length;n++){if(p[n].match(o)){return n}}return -1}this.col.chrom=l("Chrom",m.column_names);this.col.start=l("Pos",m.column_names);this.col.end=null;if(this.col.chrom==-1||this.col.start==-1){console.log("TabularButtonTrackster : VCF-file metadata incomplete.")}}if(this.col.chrom===null){console.log("TabularButtonTrackster : Chromosome column undefined.");return}if(typeof k.model.attributes.id==="undefined"){console.log("TabularButtonTrackster : Dataset identification is missing.")}else{this.dataset_id=k.model.attributes.id}if(typeof k.model.attributes.url_viz==="undefined"){console.log("TabularButtonTrackster : Url for visualization controller is missing.")}else{this.url_viz=k.model.attributes.url_viz}if(typeof k.model.attributes.genome_build!=="undefined"){this.genome_build=k.model.attributes.genome_build}var i=new IconButtonView({model:new IconButton({title:"Visualize",icon_class:"chart_curve",id:"btn_viz"})});this.$el.append(i.render().$el);$("#btn_viz").hide()},events:{"mouseover tr":"btn_viz_show",mouseleave:"btn_viz_hide"},btn_viz_show:function(n){function m(s){return !isNaN(parseFloat(s))&&isFinite(s)}if(this.col.chrom===null){return}var r=$(n.target).parent();var o=r.children().eq(this.col.chrom).html();var i=r.children().eq(this.col.start).html();var k=this.col.end?r.children().eq(this.col.end).html():i;if(!o.match("^#")&&o!==""&&m(i)){var q={dataset_id:this.dataset_id,gene_region:o+":"+i+"-"+k};var l=r.offset();var j=l.left-10;var p=l.top-$(window).scrollTop();$("#btn_viz").css({position:"fixed",top:p+"px",left:j+"px"});$("#btn_viz").off("click");$("#btn_viz").click(this.create_trackster_action(this.url_viz,q,this.genome_build));$("#btn_viz").show()}else{$("#btn_viz").hide()}},btn_viz_hide:function(){$("#btn_viz").hide()},create_trackster_action:function(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_manager.frame_new({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_manager.frame_new({title:"Trackster",type:"url",content:o})}}})}});return false}}});var a=function(l,j,m,i){var k=new j({model:new l(m)});k.render();if(i){i.append(k.$el)}return k};var h=function(k,i){var j=$("<div/>").appendTo(i);return new f({el:j,model:new c(k)}).render()};return{Dataset:e,TabularDataset:c,DatasetCollection:g,TabularDatasetChunkedView:f,createTabularDatasetChunkedView:h}});
\ No newline at end of file
diff -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 -r f76c280cdee539b65315ac583f93bfa52feb790d 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 @@
-var HDAEditView=HDABaseView.extend(LoggableMixin).extend({initialize:function(a){HDABaseView.prototype.initialize.call(this,a);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton,this._render_rerunButton]},_setUpBehaviors:function(c){HDABaseView.prototype._setUpBehaviors.call(this,c);var a=this,b=this.urls.purge,d=c.find("#historyItemPurger-"+this.model.get("id"));if(d){d.attr("href",["javascript","void(0)"].join(":"));d.click(function(e){var f=jQuery.ajax(b);f.success(function(i,g,h){a.model.set("purged",true);a.trigger("purged",a)});f.error(function(h,g,i){a.trigger("error",_l("Unable to purge this dataset"),h,g,i)})})}},_render_warnings:function(){return $(jQuery.trim(HDABaseView.templates.messages(_.extend(this.model.toJSON(),{urls:this.urls}))))},_render_titleButtons:function(){var a=$('<div class="historyItemButtons"></div>');a.append(this._render_displayButton());a.append(this._render_editButton());a.append(this._render_deleteButton());return a},_render_editButton:function(){if((this.model.get("state")===HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===HistoryDatasetAssociation.STATES.UPLOAD)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){this.editButton=null;return null}var c=this.model.get("purged"),a=this.model.get("deleted"),b={title:_l("Edit Attributes"),href:this.urls.edit,target:"galaxy_main",icon_class:"edit"};if(a||c){b.enabled=false;if(c){b.title=_l("Cannot edit attributes of datasets removed from disk")}else{if(a){b.title=_l("Undelete dataset to edit attributes")}}}this.editButton=new IconButtonView({model:new IconButton(b)});return this.editButton.render().$el},_render_deleteButton:function(){if((this.model.get("state")===HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){this.deleteButton=null;return null}var a=this,b=a.urls["delete"],c={title:_l("Delete"),href:b,id:"historyItemDeleter-"+this.model.get("id"),icon_class:"delete",on_click:function(){$.ajax({url:b,type:"POST",error:function(){a.$el.show()},success:function(){a.model.set({deleted:true})}})}};if(this.model.get("deleted")||this.model.get("purged")){c={title:_l("Dataset is already deleted"),icon_class:"delete",enabled:false}}this.deleteButton=new IconButtonView({model:new IconButton(c)});return this.deleteButton.render().$el},_render_hdaSummary:function(){var a=_.extend(this.model.toJSON(),{urls:this.urls});if(this.model.get("metadata_dbkey")==="?"&&!this.model.isDeletedOrPurged()){_.extend(a,{dbkey_unknown_and_editable:true})}return HDABaseView.templates.hdaSummary(a)},_render_errButton:function(){if(this.model.get("state")!==HistoryDatasetAssociation.STATES.ERROR){this.errButton=null;return null}this.errButton=new IconButtonView({model:new IconButton({title:_l("View or report this error"),href:this.urls.report_error,target:"galaxy_main",icon_class:"bug"})});return this.errButton.render().$el},_render_rerunButton:function(){this.rerunButton=new IconButtonView({model:new IconButton({title:_l("Run this job again"),href:this.urls.rerun,target:"galaxy_main",icon_class:"arrow-circle"})});return this.rerunButton.render().$el},_render_visualizationsButton:function(){var a=this.model.get("visualizations");if((!this.model.hasData())||(_.isEmpty(a))){this.visualizationsButton=null;return null}if(_.isObject(a[0])){return this._render_visualizationsFrameworkButton(a)}if(!this.urls.visualization){this.visualizationsButton=null;return null}var c=this.model.get("dbkey"),f=this.urls.visualization,d={},g={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(c){g.dbkey=c}this.visualizationsButton=new IconButtonView({model:new IconButton({title:_l("Visualize"),href:this.urls.visualization,icon_class:"chart_curve"})});var b=this.visualizationsButton.render().$el;b.addClass("visualize-icon");function e(h){switch(h){case"trackster":return create_trackster_action_fn(f,g,c);case"scatterplot":return create_scatterplot_action_fn(f,g);default:return function(){Galaxy.frame_manager.frame_new({title:"Visualization",type:"url",content:f+"/"+h+"?"+$.param(g)})}}}if(a.length===1){b.attr("title",a[0]);b.click(e(a[0]))}else{_.each(a,function(i){var h=i.charAt(0).toUpperCase()+i.slice(1);d[_l(h)]=e(i)});make_popupmenu(b,d)}return b},_render_visualizationsFrameworkButton:function(a){if(!(this.model.hasData())||!(a&&!_.isEmpty(a))){this.visualizationsButton=null;return null}this.visualizationsButton=new IconButtonView({model:new IconButton({title:_l("Visualize"),icon_class:"chart_curve"})});var c=this.visualizationsButton.render().$el;c.addClass("visualize-icon");if(_.keys(a).length===1){c.attr("title",_.keys(a)[0]);c.attr("href",_.values(a)[0])}else{var d=[];_.each(a,function(e){d.push(e)});var b=new PopupMenu(c,d)}return c},_render_secondaryActionButtons:function(b){var c=$("<div/>"),a=this;c.attr("style","float: right;").attr("id","secondary-actions-"+this.model.get("id"));_.each(b,function(d){c.append(d.call(a))});return c},_render_tagButton:function(){if(!(this.model.hasData())||(!this.urls.tags.get)){this.tagButton=null;return null}this.tagButton=new IconButtonView({model:new IconButton({title:_l("Edit dataset tags"),target:"galaxy_main",href:this.urls.tags.get,icon_class:"tags"})});return this.tagButton.render().$el},_render_annotateButton:function(){if(!(this.model.hasData())||(!this.urls.annotation.get)){this.annotateButton=null;return null}this.annotateButton=new IconButtonView({model:new IconButton({title:_l("Edit dataset annotation"),target:"galaxy_main",icon_class:"annotate"})});return this.annotateButton.render().$el},_render_tagArea:function(){if(!this.urls.tags.set){return null}return $(HDAEditView.templates.tagArea(_.extend(this.model.toJSON(),{urls:this.urls})).trim())},_render_annotationArea:function(){if(!this.urls.annotation.get){return null}return $(HDAEditView.templates.annotationArea(_.extend(this.model.toJSON(),{urls:this.urls})).trim())},_render_body_error:function(a){HDABaseView.prototype._render_body_error.call(this,a);var b=a.find("#primary-actions-"+this.model.get("id"));b.prepend(this._render_errButton())},_render_body_ok:function(a){a.append(this._render_hdaSummary());if(this.model.isDeletedOrPurged()){a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_showParamsButton,this._render_rerunButton]));return}a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_showParamsButton,this._render_rerunButton,this._render_visualizationsButton]));a.append(this._render_secondaryActionButtons([this._render_tagButton,this._render_annotateButton]));a.append('<div class="clear"/>');a.append(this._render_tagArea());a.append(this._render_annotationArea());a.append(this._render_displayAppArea());this._render_displayApps(a);a.append(this._render_peek())},events:{"click .historyItemTitle":"toggleBodyVisibility","click a.icon-button.tags":"loadAndDisplayTags","click a.icon-button.annotate":"loadAndDisplayAnnotation"},loadAndDisplayTags:function(c){this.log(this+".loadAndDisplayTags",c);var a=this,d=this.$el.find(".tag-area"),b=d.find(".tag-elt");if(d.is(":hidden")){if(!jQuery.trim(b.html())){$.ajax({url:this.urls.tags.get,error:function(g,e,f){a.log("Tagging failed",g,e,f);a.trigger("error",_l("Tagging failed"),g,e,f)},success:function(e){b.html(e);b.find("[title]").tooltip();d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},loadAndDisplayAnnotation:function(b){this.log(this+".loadAndDisplayAnnotation",b);var d=this.$el.find(".annotation-area"),c=d.find(".annotation-elt"),a=this.urls.annotation.set;if(d.is(":hidden")){if(!jQuery.trim(c.html())){$.ajax({url:this.urls.annotation.get,error:function(){view.log("Annotation failed",xhr,status,error);view.trigger("error",_l("Annotation failed"),xhr,status,error)},success:function(e){if(e===""){e="<em>"+_l("Describe or add notes to dataset")+"</em>"}c.html(e);d.find("[title]").tooltip();async_save_text(c.attr("id"),c.attr("id"),a,"new_annotation",18,true,4);d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toString:function(){var a=(this.model)?(this.model+""):("(no model)");return"HDAView("+a+")"}});HDAEditView.templates={tagArea:Handlebars.templates["template-hda-tagArea"],annotationArea:Handlebars.templates["template-hda-annotationArea"]};function create_scatterplot_action_fn(a,b){action=function(){Galaxy.frame_manager.frame_new({title:"Scatterplot",type:"url",content:a+"/scatterplot?"+$.param(b),location:"center"});$("div.popmenu-wrapper").remove();return false};return action}function create_trackster_action_fn(a,c,b){return function(){var d={};if(b){d["f-dbkey"]=b}$.ajax({url:a+"/list_tracks?"+$.param(d),dataType:"html",error:function(){alert(_l("Could not add this dataset to browser")+".")},success:function(e){var f=window.parent;f.show_modal(_l("View Data in a New or Saved Visualization"),"",{Cancel:function(){f.hide_modal()},"View in saved visualization":function(){f.show_modal(_l("Add Data to Saved Visualization"),e,{Cancel:function(){f.hide_modal()},"Add to visualization":function(){$(f.document).find("input[name=id]:checked").each(function(){var g=$(this).val();c.id=g;f.hide_modal();Galaxy.frame_manager.frame_new({title:"Trackster",type:"url",content:a+"/trackster?"+$.param(c)})})}})},"View in new visualization":function(){f.hide_modal();Galaxy.frame_manager.frame_new({title:"Trackster",type:"url",content:a+"/trackster?"+$.param(c)})}})}});return false}};
\ No newline at end of file
+var HDAEditView=HDABaseView.extend(LoggableMixin).extend({initialize:function(a){HDABaseView.prototype.initialize.call(this,a);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton,this._render_rerunButton]},_setUpBehaviors:function(c){HDABaseView.prototype._setUpBehaviors.call(this,c);var a=this,b=this.urls.purge,d=c.find("#historyItemPurger-"+this.model.get("id"));if(d){d.attr("href",["javascript","void(0)"].join(":"));d.click(function(e){var f=jQuery.ajax(b);f.success(function(i,g,h){a.model.set("purged",true);a.trigger("purged",a)});f.error(function(h,g,i){a.trigger("error",_l("Unable to purge this dataset"),h,g,i)})})}},_render_warnings:function(){return $(jQuery.trim(HDABaseView.templates.messages(_.extend(this.model.toJSON(),{urls:this.urls}))))},_render_titleButtons:function(){var a=$('<div class="historyItemButtons"></div>');a.append(this._render_displayButton());a.append(this._render_editButton());a.append(this._render_deleteButton());return a},_render_editButton:function(){if((this.model.get("state")===HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===HistoryDatasetAssociation.STATES.UPLOAD)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){this.editButton=null;return null}var c=this.model.get("purged"),a=this.model.get("deleted"),b={title:_l("Edit Attributes"),href:this.urls.edit,target:"galaxy_main",icon_class:"edit"};if(a||c){b.enabled=false;if(c){b.title=_l("Cannot edit attributes of datasets removed from disk")}else{if(a){b.title=_l("Undelete dataset to edit attributes")}}}this.editButton=new IconButtonView({model:new IconButton(b)});return this.editButton.render().$el},_render_deleteButton:function(){if((this.model.get("state")===HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){this.deleteButton=null;return null}var a=this,b=a.urls["delete"],c={title:_l("Delete"),href:b,id:"historyItemDeleter-"+this.model.get("id"),icon_class:"delete",on_click:function(){$.ajax({url:b,type:"POST",error:function(){a.$el.show()},success:function(){a.model.set({deleted:true})}})}};if(this.model.get("deleted")||this.model.get("purged")){c={title:_l("Dataset is already deleted"),icon_class:"delete",enabled:false}}this.deleteButton=new IconButtonView({model:new IconButton(c)});return this.deleteButton.render().$el},_render_hdaSummary:function(){var a=_.extend(this.model.toJSON(),{urls:this.urls});if(this.model.get("metadata_dbkey")==="?"&&!this.model.isDeletedOrPurged()){_.extend(a,{dbkey_unknown_and_editable:true})}return HDABaseView.templates.hdaSummary(a)},_render_errButton:function(){if(this.model.get("state")!==HistoryDatasetAssociation.STATES.ERROR){this.errButton=null;return null}this.errButton=new IconButtonView({model:new IconButton({title:_l("View or report this error"),href:this.urls.report_error,target:"galaxy_main",icon_class:"bug"})});return this.errButton.render().$el},_render_rerunButton:function(){this.rerunButton=new IconButtonView({model:new IconButton({title:_l("Run this job again"),href:this.urls.rerun,target:"galaxy_main",icon_class:"arrow-circle"})});return this.rerunButton.render().$el},_render_visualizationsButton:function(){var a=this.model.get("visualizations");if((!this.model.hasData())||(_.isEmpty(a))){this.visualizationsButton=null;return null}if(_.isObject(a[0])){return this._render_visualizationsFrameworkButton(a)}if(!this.urls.visualization){this.visualizationsButton=null;return null}var c=this.model.get("dbkey"),f=this.urls.visualization,d={},g={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(c){g.dbkey=c}this.visualizationsButton=new IconButtonView({model:new IconButton({title:_l("Visualize"),href:this.urls.visualization,icon_class:"chart_curve"})});var b=this.visualizationsButton.render().$el;b.addClass("visualize-icon");function e(h){switch(h){case"trackster":return create_trackster_action_fn(f,g,c);case"scatterplot":return create_scatterplot_action_fn(f,g);default:return function(){Galaxy.frame_manager.frame_new({title:"Visualization",type:"url",content:f+"/"+h+"?"+$.param(g)})}}}if(a.length===1){b.attr("title",a[0]);b.click(e(a[0]))}else{_.each(a,function(i){var h=i.charAt(0).toUpperCase()+i.slice(1);d[_l(h)]=e(i)});make_popupmenu(b,d)}return b},_render_visualizationsFrameworkButton:function(a){if(!(this.model.hasData())||!(a&&!_.isEmpty(a))){this.visualizationsButton=null;return null}this.visualizationsButton=new IconButtonView({model:new IconButton({title:_l("Visualize"),icon_class:"chart_curve"})});var c=this.visualizationsButton.render().$el;c.addClass("visualize-icon");if(_.keys(a).length===1){c.attr("title",_.keys(a)[0]);c.attr("href",_.values(a)[0])}else{var d=[];_.each(a,function(e){d.push(e)});var b=new PopupMenu(c,d)}return c},_render_secondaryActionButtons:function(b){var c=$("<div/>"),a=this;c.attr("style","float: right;").attr("id","secondary-actions-"+this.model.get("id"));_.each(b,function(d){c.append(d.call(a))});return c},_render_tagButton:function(){if(!(this.model.hasData())||(!this.urls.tags.get)){this.tagButton=null;return null}this.tagButton=new IconButtonView({model:new IconButton({title:_l("Edit dataset tags"),target:"galaxy_main",href:this.urls.tags.get,icon_class:"tags"})});return this.tagButton.render().$el},_render_annotateButton:function(){if(!(this.model.hasData())||(!this.urls.annotation.get)){this.annotateButton=null;return null}this.annotateButton=new IconButtonView({model:new IconButton({title:_l("Edit dataset annotation"),target:"galaxy_main",icon_class:"annotate"})});return this.annotateButton.render().$el},_render_tagArea:function(){if(!this.urls.tags.set){return null}return $(HDAEditView.templates.tagArea(_.extend(this.model.toJSON(),{urls:this.urls})).trim())},_render_annotationArea:function(){if(!this.urls.annotation.get){return null}return $(HDAEditView.templates.annotationArea(_.extend(this.model.toJSON(),{urls:this.urls})).trim())},_render_body_error:function(a){HDABaseView.prototype._render_body_error.call(this,a);var b=a.find("#primary-actions-"+this.model.get("id"));b.prepend(this._render_errButton())},_render_body_ok:function(a){a.append(this._render_hdaSummary());if(this.model.isDeletedOrPurged()){a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_showParamsButton,this._render_rerunButton]));return}a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_showParamsButton,this._render_rerunButton,this._render_visualizationsButton]));a.append(this._render_secondaryActionButtons([this._render_tagButton,this._render_annotateButton]));a.append('<div class="clear"/>');a.append(this._render_tagArea());a.append(this._render_annotationArea());a.append(this._render_displayAppArea());this._render_displayApps(a);a.append(this._render_peek())},events:{"click .historyItemTitle":"toggleBodyVisibility","click a.icon-button.tags":"loadAndDisplayTags","click a.icon-button.annotate":"loadAndDisplayAnnotation"},loadAndDisplayTags:function(c){this.log(this+".loadAndDisplayTags",c);var a=this,d=this.$el.find(".tag-area"),b=d.find(".tag-elt");if(d.is(":hidden")){if(!jQuery.trim(b.html())){$.ajax({url:this.urls.tags.get,error:function(g,e,f){a.log("Tagging failed",g,e,f);a.trigger("error",_l("Tagging failed"),g,e,f)},success:function(e){b.html(e);b.find("[title]").tooltip();d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},loadAndDisplayAnnotation:function(b){this.log(this+".loadAndDisplayAnnotation",b);var d=this.$el.find(".annotation-area"),c=d.find(".annotation-elt"),a=this.urls.annotation.set;if(d.is(":hidden")){if(!jQuery.trim(c.html())){$.ajax({url:this.urls.annotation.get,error:function(){view.log("Annotation failed",xhr,status,error);view.trigger("error",_l("Annotation failed"),xhr,status,error)},success:function(e){if(e===""){e="<em>"+_l("Describe or add notes to dataset")+"</em>"}c.html(e);d.find("[title]").tooltip();async_save_text(c.attr("id"),c.attr("id"),a,"new_annotation",18,true,4);d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toString:function(){var a=(this.model)?(this.model+""):("(no model)");return"HDAView("+a+")"}});HDAEditView.templates={tagArea:Handlebars.templates["template-hda-tagArea"],annotationArea:Handlebars.templates["template-hda-annotationArea"]};function create_scatterplot_action_fn(a,b){action=function(){Galaxy.frame_manager.frame_new({title:"Scatterplot",type:"url",content:a+"/scatterplot?"+$.param(b),location:"center"});$("div.popmenu-wrapper").remove();return false};return action}function create_trackster_action_fn(a,c,b){return function(){var d={};if(b){d["f-dbkey"]=b}$.ajax({url:a+"/list_tracks?"+$.param(d),dataType:"html",error:function(){alert(("Could not add this dataset to browser")+".")},success:function(e){var f=window.parent;f.Galaxy.modal.show({title:"View Data in a New or Saved Visualization",buttons:{Cancel:function(){f.Galaxy.modal.hide()},"View in saved visualization":function(){f.Galaxy.modal.show({title:"Add Data to Saved Visualization",body:e,buttons:{Cancel:function(){f.Galaxy.modal.hide()},"Add to visualization":function(){$(f.document).find("input[name=id]:checked").each(function(){f.Galaxy.modal.hide();var g=$(this).val();c.id=g;f.Galaxy.frame_manager.frame_new({title:"Trackster",type:"url",content:a+"/trackster?"+$.param(c)})})}}})},"View in new visualization":function(){f.Galaxy.modal.hide();var g=a+"/trackster?"+$.param(c);f.Galaxy.frame_manager.frame_new({title:"Trackster",type:"url",content:g})}}})}});return false}};
\ No newline at end of file
diff -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 -r f76c280cdee539b65315ac583f93bfa52feb790d static/scripts/packed/mvc/history/history-model.js
--- a/static/scripts/packed/mvc/history/history-model.js
+++ b/static/scripts/packed/mvc/history/history-model.js
@@ -1,1 +1,1 @@
-var History=Backbone.Model.extend(LoggableMixin).extend({defaults:{id:null,name:"Unnamed History",state:"new",diskSize:0,deleted:false},urlRoot:"api/histories/",url:function(){return this.urlRoot+this.get("id")},initialize:function(b,c,a){a=a||null;this.log(this+".initialize:",b,c);this.hdas=new HDACollection();this.updateTimeoutId=null;if(c&&_.isArray(c)){this.hdas.add(c);this.checkForUpdates();if(this.hdas.length>0){this.updateDisplayApplications()}}this.hdas.bind("state:ready",function(e,f,d){if(e.get("force_history_refresh")){this.updateAfterDelay()}},this);if(this.logger){this.bind("all",function(d){this.log(this+"",arguments)},this)}},hdaIdsFromStateIds:function(){return _.reduce(_.values(this.get("state_ids")),function(b,a){return b.concat(a)})},updateAfterDelay:function(){var a=this;this.updateTimeoutId=setTimeout(function(){a.stateUpdater()},History.UPDATE_DELAY);return this.updateTimeoutId},checkForUpdates:function(){if(this.hdas.running().length){this.updateAfterDelay()}else{this.trigger("ready")}return this},stateUpdater:function(){var c=this,a=this.get("state"),b=this.get("state_ids");jQuery.ajax("api/histories/"+this.get("id")).success(function(d){c.set(d);c.log("current history state:",c.get("state"),"(was)",a,"new size:",c.get("nice_size"));var f=[];_.each(_.keys(d.state_ids),function(h){var g=_.difference(d.state_ids[h],b[h]);f=f.concat(g)});if(f.length){c.fetchHdaUpdates(f).done(function(){e(c)})}else{e(c)}function e(h){var g;if((h.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(h.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){g=h.updateAfterDelay()}else{this.updateTimeoutId=null;h.trigger("ready")}return g}}).error(function(g,d,e){if(g.status===502){setTimeout(function(){c.log("Bad Gateway error. Retrying...");c.stateUpdater()},History.UPDATE_DELAY)}else{if(!((g.readyState===0)&&(g.status===0))){c.log("stateUpdater error:",e,"responseText:",g.responseText);var f=_l("An error occurred while getting updates from the server.")+" "+_l("Please contact a Galaxy administrator if the problem persists.");c.trigger("error",f,g,d,e)}}})},fetchHdaUpdates:function(c){var a=this,b=jQuery.ajax({url:this.url()+"/contents?"+jQuery.param({ids:c.join(",")}),error:function(i,d,e){if((i.readyState===0)&&(i.status===0)){return}var g=JSON.parse(i.responseText);if(_.isArray(g)){var f=_.groupBy(g,function(j){if(_.has(j,"error")){return"errored"}return"ok"});a.log("fetched, errored datasets:",f.errored);a.updateHdas(g)}else{a.log("Error updating hdas from api history contents",c,i,d,e,g);var h=_l("An error occurred while getting dataset details from the server.")+" "+_l("Please contact a Galaxy administrator if the problem persists.");a.trigger("error",h,i,d,e)}},success:function(e,d,f){a.log(a+".fetchHdaUpdates, success:",d,f);a.updateHdas(e)}});return b},updateHdas:function(a){var c=this,b=[];c.log(c+".updateHdas:",a);_.each(a,function(e,f){var d=c.hdas.get(e.id);if(d){c.log("found existing model in list for id "+e.id+", updating...:");d.set(e)}else{c.log("NO existing model for id "+e.id+", creating...:");b.push(e)}});if(b.length){c.addHdas(b)}},addHdas:function(a){var b=this;_.each(a,function(c,d){var e=b.hdas.hidToCollectionIndex(c.hid);c.history_id=b.get("id");b.hdas.add(new HistoryDatasetAssociation(c),{at:e,silent:true})});b.hdas.trigger("add",a)},updateDisplayApplications:function(a){this.log(this+"updateDisplayApplications:",a);var c=this,b=(a&&_.isArray(a))?({hda_ids:a.join(",")}):({});c.log(this+": fetching display application data");jQuery.ajax("history/get_display_application_links",{data:b,success:function(f,d,e){_.each(f,function(h){var g=c.hdas.get(h.id);if(g){g.set(h)}})},error:function(g,d,e){if(!((g.readyState===0)&&(g.status===0))){c.log("Error fetching display applications:",a,g,d,e);var f=_l("An error occurred while getting display applications from the server.")+" "+_l("Please contact a Galaxy administrator if the problem persists.");c.trigger("error",f,g,d,e)}}})},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});History.UPDATE_DELAY=4000;var HistoryCollection=Backbone.Collection.extend(LoggableMixin).extend({model:History,urlRoot:"api/histories"});
\ No newline at end of file
+var History=Backbone.Model.extend(LoggableMixin).extend({defaults:{id:null,name:"Unnamed History",state:"new",diskSize:0,deleted:false},urlRoot:"api/histories/",url:function(){return this.urlRoot+this.get("id")},initialize:function(b,c,a){a=a||null;this.log(this+".initialize:",b,c);this.hdas=new HDACollection();this.updateTimeoutId=null;if(c&&_.isArray(c)){this.hdas.add(c);this.checkForUpdates();if(this.hdas.length>0){this.updateDisplayApplications()}}this.hdas.bind("state:ready",function(e,f,d){if(e.get("force_history_refresh")){this.updateAfterDelay()}},this);if(this.logger){this.bind("all",function(d){this.log(this+"",arguments)},this)}},hdaIdsFromStateIds:function(){return _.reduce(_.values(this.get("state_ids")),function(b,a){return b.concat(a)})},updateAfterDelay:function(){var a=this;this.updateTimeoutId=setTimeout(function(){a.stateUpdater()},History.UPDATE_DELAY);return this.updateTimeoutId},checkForUpdates:function(){if(this.hdas.running().length){this.updateAfterDelay()}else{this.trigger("ready")}return this},stateUpdater:function(){var c=this,a=this.get("state"),b=this.get("state_ids");jQuery.ajax("api/histories/"+this.get("id")).success(function(d){c.set(d);c.log("current history state:",c.get("state"),"(was)",a,"new size:",c.get("nice_size"));var f=[];_.each(_.keys(d.state_ids),function(h){var g=_.difference(d.state_ids[h],b[h]);f=f.concat(g)});if(f.length){c.fetchHdaUpdates(f).done(function(){e(c)})}else{e(c)}function e(h){var g;if((h.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(h.get("state")===HistoryDatasetAssociation.STATES.QUEUED)||(h.get("state")===HistoryDatasetAssociation.STATES.SETTING_METADATA)){g=h.updateAfterDelay()}else{this.updateTimeoutId=null;h.trigger("ready")}return g}}).error(function(g,d,e){if(g.status===502){setTimeout(function(){c.log("Bad Gateway error. Retrying...");c.stateUpdater()},History.UPDATE_DELAY)}else{if(!((g.readyState===0)&&(g.status===0))){c.log("stateUpdater error:",e,"responseText:",g.responseText);var f=_l("An error occurred while getting updates from the server.")+" "+_l("Please contact a Galaxy administrator if the problem persists.");c.trigger("error",f,g,d,e)}}})},fetchHdaUpdates:function(c){var a=this,b=jQuery.ajax({url:this.url()+"/contents?"+jQuery.param({ids:c.join(",")}),error:function(i,d,e){if((i.readyState===0)&&(i.status===0)){return}var g=JSON.parse(i.responseText);if(_.isArray(g)){var f=_.groupBy(g,function(j){if(_.has(j,"error")){return"errored"}return"ok"});a.log("fetched, errored datasets:",f.errored);a.updateHdas(g)}else{a.log("Error updating hdas from api history contents",c,i,d,e,g);var h=_l("An error occurred while getting dataset details from the server.")+" "+_l("Please contact a Galaxy administrator if the problem persists.");a.trigger("error",h,i,d,e)}},success:function(e,d,f){a.log(a+".fetchHdaUpdates, success:",d,f);a.updateHdas(e)}});return b},updateHdas:function(a){var c=this,b=[];c.log(c+".updateHdas:",a);_.each(a,function(e,f){var d=c.hdas.get(e.id);if(d){c.log("found existing model in list for id "+e.id+", updating...:");d.set(e)}else{c.log("NO existing model for id "+e.id+", creating...:");b.push(e)}});if(b.length){c.addHdas(b)}},addHdas:function(a){var b=this;_.each(a,function(c,d){var e=b.hdas.hidToCollectionIndex(c.hid);c.history_id=b.get("id");b.hdas.add(new HistoryDatasetAssociation(c),{at:e,silent:true})});b.hdas.trigger("add",a)},updateDisplayApplications:function(a){this.log(this+"updateDisplayApplications:",a);var c=this,b=(a&&_.isArray(a))?({hda_ids:a.join(",")}):({});c.log(this+": fetching display application data");jQuery.ajax("history/get_display_application_links",{data:b,success:function(f,d,e){_.each(f,function(h){var g=c.hdas.get(h.id);if(g){g.set(h)}})},error:function(g,d,e){if(!((g.readyState===0)&&(g.status===0))){c.log("Error fetching display applications:",a,g,d,e);var f=_l("An error occurred while getting display applications from the server.")+" "+_l("Please contact a Galaxy administrator if the problem persists.");c.trigger("error",f,g,d,e)}}})},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});History.UPDATE_DELAY=4000;var HistoryCollection=Backbone.Collection.extend(LoggableMixin).extend({model:History,urlRoot:"api/histories"});
\ No newline at end of file
diff -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 -r f76c280cdee539b65315ac583f93bfa52feb790d static/scripts/packed/utils/config.js
--- a/static/scripts/packed/utils/config.js
+++ b/static/scripts/packed/utils/config.js
@@ -1,1 +1,1 @@
-define(["libs/underscore","viz/trackster/util"],function(b,e){var c=Backbone.Model.extend({initialize:function(f){var g=this.get("key");this.set("id",g);var h=b.find(c.known_settings_defaults,function(i){return i.key===g});if(h){this.set(b.extend({},h,f))}if(this.get("type")==="color"&&!this.get("value")){this.set("value",e.get_random_color())}this.on("change:value",this.cast_value,this)},cast_value:function(){var f=this.get("type"),g=this.get("value");if(f==="float"){g=parseFloat(g)}else{if(f==="int"){g=parseInt(g,10)}}this.set("value")}},{known_settings_defaults:[{key:"name",label:"Name",type:"text",default_value:""},{key:"color",label:"Color",type:"color",default_value:null},{key:"min_value",label:"Min Value",type:"float",default_value:null},{key:"max_value",label:"Max Value",type:"float",default_value:null},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true},{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"block_color",label:"Block color",type:"color",default_value:null},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:null},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true}]});var d=Backbone.Collection.extend({model:c,to_key_value_dict:function(){var f={};this.each(function(g){f[g.get("key")]=g.get("value")});return f},get_value:function(f){var g=this.get(f);if(g){return g.get("value")}return undefined}},{from_config_dict:function(g){var f=b.map(b.keys(g),function(h){return{key:h,value:g[h]}});return new d(f)}});var a=Backbone.View.extend({className:"config-settings-view",render:function(){var i=this.model;var f=this.$el;var h;function g(n,j){for(var r=0;r<n.length;r++){h=n[r];if(h.hidden){continue}var l="param_"+r;var v=i.values[h.key];var x=$("<div class='form-row' />").appendTo(j);x.append($("<label />").attr("for",l).text(h.label+":"));if(type==="bool"){x.append($('<input type="checkbox" />').attr("id",l).attr("name",l).attr("checked",v))}else{if(type==="text"){x.append($('<input type="text"/>').attr("id",l).val(v).click(function(){$(this).select()}))}else{if(type==="select"){var t=$("<select />").attr("id",l);for(var p=0;p<h.options.length;p++){$("<option/>").text(h.options[p].label).attr("value",h.options[p].value).appendTo(t)}t.val(v);x.append(t)}else{if(type==="color"){var w=$("<div/>").appendTo(x),s=$("<input />").attr("id",l).attr("name",l).val(v).css("float","left").appendTo(w).click(function(z){$(".tooltip").removeClass("in");var y=$(this).siblings(".tooltip").addClass("in");y.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(y).height()/2)+($(this).height()/2)}).show();y.click(function(A){A.stopPropagation()});$(document).bind("click.color-picker",function(){y.hide();$(document).unbind("click.color-picker")});z.stopPropagation()}),q=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(w).attr("title","Set new random color").tooltip(),u=$("<div class='tooltip right' style='position: absolute;' />").appendTo(w).hide(),m=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(u),k=$("<div class='tooltip-arrow'></div>").appendTo(u),o=$.farbtastic(m,{width:100,height:100,callback:s,color:v});w.append($("<div/>").css("clear","both"));(function(y){q.click(function(){y.setColor(e.get_random_color())})})(o)}else{x.append($("<input />").attr("id",l).attr("name",l).val(v))}}}}if(h.help){x.append($("<div class='help'/>").text(h.help))}}}g(this.params,f);return this},render_in_modal:function(){var h=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},f=function(){this.update_from_form();hide_modal();$(window).unbind("keypress.check_enter_esc")},g=function(i){if((i.keyCode||i.which)===27){h()}else{if((i.keyCode||i.which)===13){f()}}};$(window).bind("keypress.check_enter_esc",g);if(this.$el.children().length===0){this.render()}show_modal("Configure",drawable.config.build_form(),{Cancel:h,OK:f})},update_from_form:function(){var f=this;this.collection.each(function(h,g){if(!h.get("hidden")){var j="param_"+g;var i=f.$el.find("#"+j).val();if(type==="bool"){i=container.find("#"+j).is(":checked")}h.set("value",i)}})}});return{ConfigSettingCollection:d,ConfigSettingCollectionView:a}});
\ No newline at end of file
+define(["libs/underscore","viz/trackster/util"],function(b,e){var c=Backbone.Model.extend({initialize:function(f){var g=this.get("key");this.set("id",g);var h=b.find(c.known_settings_defaults,function(i){return i.key===g});if(h){this.set(b.extend({},h,f))}if(this.get("type")==="color"&&!this.get("value")){this.set("value",e.get_random_color())}this.on("change:value",this.cast_value,this)},cast_value:function(){var f=this.get("type"),g=this.get("value");if(f==="float"){g=parseFloat(g)}else{if(f==="int"){g=parseInt(g,10)}}this.set("value")}},{known_settings_defaults:[{key:"name",label:"Name",type:"text",default_value:""},{key:"color",label:"Color",type:"color",default_value:null},{key:"min_value",label:"Min Value",type:"float",default_value:null},{key:"max_value",label:"Max Value",type:"float",default_value:null},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true},{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"block_color",label:"Block color",type:"color",default_value:null},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:null},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true}]});var d=Backbone.Collection.extend({model:c,to_key_value_dict:function(){var f={};this.each(function(g){f[g.get("key")]=g.get("value")});return f},get_value:function(f){var g=this.get(f);if(g){return g.get("value")}return undefined}},{from_config_dict:function(g){var f=b.map(b.keys(g),function(h){return{key:h,value:g[h]}});return new d(f)}});var a=Backbone.View.extend({className:"config-settings-view",render:function(){var i=this.model;var f=this.$el;var h;function g(n,j){for(var r=0;r<n.length;r++){h=n[r];if(h.hidden){continue}var l="param_"+r;var v=i.values[h.key];var x=$("<div class='form-row' />").appendTo(j);x.append($("<label />").attr("for",l).text(h.label+":"));if(type==="bool"){x.append($('<input type="checkbox" />').attr("id",l).attr("name",l).attr("checked",v))}else{if(type==="text"){x.append($('<input type="text"/>').attr("id",l).val(v).click(function(){$(this).select()}))}else{if(type==="select"){var t=$("<select />").attr("id",l);for(var p=0;p<h.options.length;p++){$("<option/>").text(h.options[p].label).attr("value",h.options[p].value).appendTo(t)}t.val(v);x.append(t)}else{if(type==="color"){var w=$("<div/>").appendTo(x),s=$("<input />").attr("id",l).attr("name",l).val(v).css("float","left").appendTo(w).click(function(z){$(".tooltip").removeClass("in");var y=$(this).siblings(".tooltip").addClass("in");y.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(y).height()/2)+($(this).height()/2)}).show();y.click(function(A){A.stopPropagation()});$(document).bind("click.color-picker",function(){y.hide();$(document).unbind("click.color-picker")});z.stopPropagation()}),q=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(w).attr("title","Set new random color").tooltip(),u=$("<div class='tooltip right' style='position: absolute;' />").appendTo(w).hide(),m=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(u),k=$("<div class='tooltip-arrow'></div>").appendTo(u),o=$.farbtastic(m,{width:100,height:100,callback:s,color:v});w.append($("<div/>").css("clear","both"));(function(y){q.click(function(){y.setColor(e.get_random_color())})})(o)}else{x.append($("<input />").attr("id",l).attr("name",l).val(v))}}}}if(h.help){x.append($("<div class='help'/>").text(h.help))}}}g(this.params,f);return this},render_in_modal:function(){var h=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},f=function(){this.update_from_form();hide_modal();$(window).unbind("keypress.check_enter_esc")},g=function(i){if((i.keyCode||i.which)===27){h()}else{if((i.keyCode||i.which)===13){f()}}};$(window).bind("keypress.check_enter_esc",g);if(this.$el.children().length===0){this.render()}Galaxy.modal.show({title:"Configure",body:drawable.config.build_form(),buttons:{Cancel:h,OK:f}})},update_from_form:function(){var f=this;this.collection.each(function(h,g){if(!h.get("hidden")){var j="param_"+g;var i=f.$el.find("#"+j).val();if(type==="bool"){i=container.find("#"+j).is(":checked")}h.set("value",i)}})}});return{ConfigSettingCollection:d,ConfigSettingCollectionView:a}});
\ No newline at end of file
diff -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 -r f76c280cdee539b65315ac583f93bfa52feb790d static/scripts/packed/viz/trackster/filters.js
--- a/static/scripts/packed/viz/trackster/filters.js
+++ b/static/scripts/packed/viz/trackster/filters.js
@@ -1,1 +1,1 @@
-define(["libs/underscore"],function(c){var f=c.extend;var a=function(g){this.manager=null;this.name=g.name;this.index=g.index;this.tool_id=g.tool_id;this.tool_exp_name=g.tool_exp_name};f(a.prototype,{to_dict:function(){return{name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name}}});var d=function(i,h,g){return $("<a/>").attr("href","javascript:void(0);").attr("title",i).addClass("icon-button").addClass(h).tooltip().click(g)};var e=function(o){a.call(this,o);this.low=("low" in o?o.low:-Number.MAX_VALUE);this.high=("high" in o?o.high:Number.MAX_VALUE);this.min=("min" in o?o.min:Number.MAX_VALUE);this.max=("max" in o?o.max:-Number.MAX_VALUE);this.container=null;this.slider=null;this.slider_label=null;var k=function(p,q,r){p.click(function(){var w=q.text(),u=parseFloat(r.slider("option","max")),t=(u<=1?4:u<=1000000?u.toString().length:6),v=false,s=$(this).parents(".slider-row");s.addClass("input");if(r.slider("option","values")){t=2*t+1;v=true}q.text("");$("<input type='text'/>").attr("size",t).attr("maxlength",t).attr("value",w).appendTo(q).focus().select().click(function(x){x.stopPropagation()}).blur(function(){$(this).remove();q.text(w);s.removeClass("input")}).keyup(function(B){if(B.keyCode===27){$(this).trigger("blur")}else{if(B.keyCode===13){var z=r.slider("option","min"),x=r.slider("option","max"),A=function(C){return(isNaN(C)||C>x||C<z)},y=$(this).val();if(!v){y=parseFloat(y);if(A(y)){alert("Parameter value must be in the range ["+z+"-"+x+"]");return $(this)}}else{y=y.split("-");y=[parseFloat(y[0]),parseFloat(y[1])];if(A(y[0])||A(y[1])){alert("Parameter value must be in the range ["+z+"-"+x+"]");return $(this)}}r.slider((v?"values":"value"),y);s.removeClass("input")}}})})};var h=this;h.parent_div=$("<div/>").addClass("filter-row slider-row");var g=$("<div/>").addClass("elt-label").appendTo(h.parent_div),m=$("<span/>").addClass("slider-name").text(h.name+" ").appendTo(g),i=$("<span/>").text(this.low+"-"+this.high),j=$("<span/>").addClass("slider-value").appendTo(g).append("[").append(i).append("]");h.values_span=i;var l=$("<div/>").addClass("slider").appendTo(h.parent_div);h.control_element=$("<div/>").attr("id",h.name+"-filter-control").appendTo(l);h.control_element.slider({range:true,min:this.min,max:this.max,step:this.get_slider_step(this.min,this.max),values:[this.low,this.high],slide:function(p,q){h.slide(p,q)},change:function(p,q){h.control_element.slider("option","slide").call(h.control_element,p,q)}});h.slider=h.control_element;h.slider_label=i;k(j,i,h.control_element);var n=$("<div/>").addClass("display-controls").appendTo(h.parent_div);this.transparency_icon=d("Use filter for data transparency","layer-transparent",function(){if(h.manager.alpha_filter!==h){h.manager.alpha_filter=h;h.manager.parent_div.find(".layer-transparent").removeClass("active").hide();h.transparency_icon.addClass("active").show()}else{h.manager.alpha_filter=null;h.transparency_icon.removeClass("active")}h.manager.track.request_draw({force:true,clear_after:true})}).appendTo(n).hide();this.height_icon=d("Use filter for data height","arrow-resize-090",function(){if(h.manager.height_filter!==h){h.manager.height_filter=h;h.manager.parent_div.find(".arrow-resize-090").removeClass("active").hide();h.height_icon.addClass("active").show()}else{h.manager.height_filter=null;h.height_icon.removeClass("active")}h.manager.track.request_draw({force:true,clear_after:true})}).appendTo(n).hide();h.parent_div.hover(function(){h.transparency_icon.show();h.height_icon.show()},function(){if(h.manager.alpha_filter!==h){h.transparency_icon.hide()}if(h.manager.height_filter!==h){h.height_icon.hide()}});$("<div style='clear: both;'/>").appendTo(h.parent_div)};f(e.prototype,{to_dict:function(){var g=a.prototype.to_dict.call(this);return f(g,{type:"number",min:this.min,max:this.max,low:this.low,high:this.high})},copy:function(){return new e({name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name})},get_slider_step:function(i,g){var h=g-i;return(h<=2?0.01:1)},slide:function(i,j){var h=j.values;this.values_span.text(h[0]+"-"+h[1]);this.low=h[0];this.high=h[1];var g=this;setTimeout(function(){if(h[0]===g.low&&h[1]===g.high){g.manager.track.request_draw({force:true,clear_after:true})}},25)},applies_to:function(g){if(g.length>this.index){return true}return false},_keep_val:function(g){return(isNaN(g)||(g>=this.low&&g<=this.high))},keep:function(h){if(!this.applies_to(h)){return true}var k=this;var l=h[this.index];if(l instanceof Array){var j=true;for(var g=0;g<l.length;g++){if(!this._keep_val(l[g])){j=false;break}}return j}else{return this._keep_val(h[this.index])}},update_attrs:function(k){var g=false;if(!this.applies_to(k)){return g}var h=k[this.index];if(!(h instanceof Array)){h=[h]}for(var j=0;j<h.length;j++){var l=h[j];if(l<this.min){this.min=Math.floor(l);g=true}if(l>this.max){this.max=Math.ceil(l);g=true}}return g},update_ui_elt:function(){if(this.min<this.max){this.parent_div.show()}else{this.parent_div.hide()}var h=this.slider.slider("option","min"),g=this.slider.slider("option","max");if(this.min<h||this.max>g){this.slider.slider("option","min",this.min);this.slider.slider("option","max",this.max);this.slider.slider("option","step",this.get_slider_step(this.min,this.max));this.slider.slider("option","values",[this.min,this.max])}}});var b=function(j,p){this.track=j;this.alpha_filter=null;this.height_filter=null;this.filters=[];this.parent_div=$("<div/>").addClass("filters").hide();this.parent_div.bind("drag",function(i){i.stopPropagation()}).click(function(i){i.stopPropagation()}).bind("dblclick",function(i){i.stopPropagation()}).bind("keydown",function(i){i.stopPropagation()});if(p&&"filters" in p){var g=("alpha_filter" in p?p.alpha_filter:null),k=("height_filter" in p?p.height_filter:null),m=p.filters,h;for(var n=0;n<m.length;n++){if(m[n].type==="number"){h=new e(m[n]);this.add_filter(h);if(h.name===g){this.alpha_filter=h;h.transparency_icon.addClass("active").show()}if(h.name===k){this.height_filter=h;h.height_icon.addClass("active").show()}}else{console.log("ERROR: unsupported filter: ",name,type)}}if("visible" in p&&p.visible){this.parent_div.show()}}if(this.filters.length!==0){var q=$("<div/>").addClass("param-row").appendTo(this.parent_div);var o=$("<input type='submit'/>").attr("value","Run on complete dataset").appendTo(q);var l=this;o.click(function(){l.run_on_dataset()})}};f(b.prototype,{show:function(){this.parent_div.show()},hide:function(){this.parent_div.hide()},toggle:function(){this.parent_div.toggle()},visible:function(){return this.parent_div.is(":visible")},to_dict:function(){var k={},j=[],h;for(var g=0;g<this.filters.length;g++){h=this.filters[g];j.push(h.to_dict())}k.filters=j;k.alpha_filter=(this.alpha_filter?this.alpha_filter.name:null);k.height_filter=(this.height_filter?this.height_filter.name:null);k.visible=this.parent_div.is(":visible");return k},copy:function(h){var j=new b(h);for(var g=0;g<this.filters.length;g++){j.add_filter(this.filters[g].copy())}return j},add_filter:function(g){g.manager=this;this.parent_div.append(g.parent_div);this.filters.push(g)},remove_all:function(){this.filters=[];this.parent_div.children().remove()},init_filters:function(){for(var g=0;g<this.filters.length;g++){var h=this.filters[g];h.update_ui_elt()}},clear_filters:function(){for(var g=0;g<this.filters.length;g++){var h=this.filters[g];h.slider.slider("option","values",[h.min,h.max])}this.alpha_filter=null;this.height_filter=null;this.parent_div.find(".icon-button").hide()},run_on_dataset:function(){var n=function(q,i,p){if(!(i in q)){q[i]=p}return q[i]};var m={},o,g;for(var l=0;l<this.filters.length;l++){o=this.filters[l];if(o.tool_id){if(o.min!==o.low){g=n(m,o.tool_id,[]);g[g.length]=o.tool_exp_name+" >= "+o.low}if(o.max!==o.high){g=n(m,o.tool_id,[]);g[g.length]=o.tool_exp_name+" <= "+o.high}}}var h=[];for(var k in m){h[h.length]=[k,m[k]]}(function j(u,r){var p=r[0],q=p[0],t=p[1],s="("+t.join(") and (")+")",i={cond:s,input:u,target_dataset_id:u,tool_id:q};r=r.slice(1);$.getJSON(run_tool_url,i,function(v){if(v.error){show_modal("Filter Dataset","Error running tool "+q,{Close:hide_modal})}else{if(r.length===0){show_modal("Filtering Dataset","Filter(s) are running on the complete dataset. Outputs are in dataset's history.",{Close:hide_modal})}else{j(v.dataset_id,r)}}})})(this.track.dataset_id,h)}});return{FiltersManager:b,NumberFilter:e}});
\ No newline at end of file
+define(["libs/underscore"],function(c){var f=c.extend;var a=function(g){this.manager=null;this.name=g.name;this.index=g.index;this.tool_id=g.tool_id;this.tool_exp_name=g.tool_exp_name};f(a.prototype,{to_dict:function(){return{name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name}}});var d=function(i,h,g){return $("<a/>").attr("href","javascript:void(0);").attr("title",i).addClass("icon-button").addClass(h).tooltip().click(g)};var e=function(o){a.call(this,o);this.low=("low" in o?o.low:-Number.MAX_VALUE);this.high=("high" in o?o.high:Number.MAX_VALUE);this.min=("min" in o?o.min:Number.MAX_VALUE);this.max=("max" in o?o.max:-Number.MAX_VALUE);this.container=null;this.slider=null;this.slider_label=null;var k=function(p,q,r){p.click(function(){var w=q.text(),u=parseFloat(r.slider("option","max")),t=(u<=1?4:u<=1000000?u.toString().length:6),v=false,s=$(this).parents(".slider-row");s.addClass("input");if(r.slider("option","values")){t=2*t+1;v=true}q.text("");$("<input type='text'/>").attr("size",t).attr("maxlength",t).attr("value",w).appendTo(q).focus().select().click(function(x){x.stopPropagation()}).blur(function(){$(this).remove();q.text(w);s.removeClass("input")}).keyup(function(B){if(B.keyCode===27){$(this).trigger("blur")}else{if(B.keyCode===13){var z=r.slider("option","min"),x=r.slider("option","max"),A=function(C){return(isNaN(C)||C>x||C<z)},y=$(this).val();if(!v){y=parseFloat(y);if(A(y)){alert("Parameter value must be in the range ["+z+"-"+x+"]");return $(this)}}else{y=y.split("-");y=[parseFloat(y[0]),parseFloat(y[1])];if(A(y[0])||A(y[1])){alert("Parameter value must be in the range ["+z+"-"+x+"]");return $(this)}}r.slider((v?"values":"value"),y);s.removeClass("input")}}})})};var h=this;h.parent_div=$("<div/>").addClass("filter-row slider-row");var g=$("<div/>").addClass("elt-label").appendTo(h.parent_div),m=$("<span/>").addClass("slider-name").text(h.name+" ").appendTo(g),i=$("<span/>").text(this.low+"-"+this.high),j=$("<span/>").addClass("slider-value").appendTo(g).append("[").append(i).append("]");h.values_span=i;var l=$("<div/>").addClass("slider").appendTo(h.parent_div);h.control_element=$("<div/>").attr("id",h.name+"-filter-control").appendTo(l);h.control_element.slider({range:true,min:this.min,max:this.max,step:this.get_slider_step(this.min,this.max),values:[this.low,this.high],slide:function(p,q){h.slide(p,q)},change:function(p,q){h.control_element.slider("option","slide").call(h.control_element,p,q)}});h.slider=h.control_element;h.slider_label=i;k(j,i,h.control_element);var n=$("<div/>").addClass("display-controls").appendTo(h.parent_div);this.transparency_icon=d("Use filter for data transparency","layer-transparent",function(){if(h.manager.alpha_filter!==h){h.manager.alpha_filter=h;h.manager.parent_div.find(".layer-transparent").removeClass("active").hide();h.transparency_icon.addClass("active").show()}else{h.manager.alpha_filter=null;h.transparency_icon.removeClass("active")}h.manager.track.request_draw({force:true,clear_after:true})}).appendTo(n).hide();this.height_icon=d("Use filter for data height","arrow-resize-090",function(){if(h.manager.height_filter!==h){h.manager.height_filter=h;h.manager.parent_div.find(".arrow-resize-090").removeClass("active").hide();h.height_icon.addClass("active").show()}else{h.manager.height_filter=null;h.height_icon.removeClass("active")}h.manager.track.request_draw({force:true,clear_after:true})}).appendTo(n).hide();h.parent_div.hover(function(){h.transparency_icon.show();h.height_icon.show()},function(){if(h.manager.alpha_filter!==h){h.transparency_icon.hide()}if(h.manager.height_filter!==h){h.height_icon.hide()}});$("<div style='clear: both;'/>").appendTo(h.parent_div)};f(e.prototype,{to_dict:function(){var g=a.prototype.to_dict.call(this);return f(g,{type:"number",min:this.min,max:this.max,low:this.low,high:this.high})},copy:function(){return new e({name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name})},get_slider_step:function(i,g){var h=g-i;return(h<=2?0.01:1)},slide:function(i,j){var h=j.values;this.values_span.text(h[0]+"-"+h[1]);this.low=h[0];this.high=h[1];var g=this;setTimeout(function(){if(h[0]===g.low&&h[1]===g.high){g.manager.track.request_draw({force:true,clear_after:true})}},25)},applies_to:function(g){if(g.length>this.index){return true}return false},_keep_val:function(g){return(isNaN(g)||(g>=this.low&&g<=this.high))},keep:function(h){if(!this.applies_to(h)){return true}var k=this;var l=h[this.index];if(l instanceof Array){var j=true;for(var g=0;g<l.length;g++){if(!this._keep_val(l[g])){j=false;break}}return j}else{return this._keep_val(h[this.index])}},update_attrs:function(k){var g=false;if(!this.applies_to(k)){return g}var h=k[this.index];if(!(h instanceof Array)){h=[h]}for(var j=0;j<h.length;j++){var l=h[j];if(l<this.min){this.min=Math.floor(l);g=true}if(l>this.max){this.max=Math.ceil(l);g=true}}return g},update_ui_elt:function(){if(this.min<this.max){this.parent_div.show()}else{this.parent_div.hide()}var h=this.slider.slider("option","min"),g=this.slider.slider("option","max");if(this.min<h||this.max>g){this.slider.slider("option","min",this.min);this.slider.slider("option","max",this.max);this.slider.slider("option","step",this.get_slider_step(this.min,this.max));this.slider.slider("option","values",[this.min,this.max])}}});var b=function(j,p){this.track=j;this.alpha_filter=null;this.height_filter=null;this.filters=[];this.parent_div=$("<div/>").addClass("filters").hide();this.parent_div.bind("drag",function(i){i.stopPropagation()}).click(function(i){i.stopPropagation()}).bind("dblclick",function(i){i.stopPropagation()}).bind("keydown",function(i){i.stopPropagation()});if(p&&"filters" in p){var g=("alpha_filter" in p?p.alpha_filter:null),k=("height_filter" in p?p.height_filter:null),m=p.filters,h;for(var n=0;n<m.length;n++){if(m[n].type==="number"){h=new e(m[n]);this.add_filter(h);if(h.name===g){this.alpha_filter=h;h.transparency_icon.addClass("active").show()}if(h.name===k){this.height_filter=h;h.height_icon.addClass("active").show()}}else{console.log("ERROR: unsupported filter: ",name,type)}}if("visible" in p&&p.visible){this.parent_div.show()}}if(this.filters.length!==0){var q=$("<div/>").addClass("param-row").appendTo(this.parent_div);var o=$("<input type='submit'/>").attr("value","Run on complete dataset").appendTo(q);var l=this;o.click(function(){l.run_on_dataset()})}};f(b.prototype,{show:function(){this.parent_div.show()},hide:function(){this.parent_div.hide()},toggle:function(){this.parent_div.toggle()},visible:function(){return this.parent_div.is(":visible")},to_dict:function(){var k={},j=[],h;for(var g=0;g<this.filters.length;g++){h=this.filters[g];j.push(h.to_dict())}k.filters=j;k.alpha_filter=(this.alpha_filter?this.alpha_filter.name:null);k.height_filter=(this.height_filter?this.height_filter.name:null);k.visible=this.parent_div.is(":visible");return k},copy:function(h){var j=new b(h);for(var g=0;g<this.filters.length;g++){j.add_filter(this.filters[g].copy())}return j},add_filter:function(g){g.manager=this;this.parent_div.append(g.parent_div);this.filters.push(g)},remove_all:function(){this.filters=[];this.parent_div.children().remove()},init_filters:function(){for(var g=0;g<this.filters.length;g++){var h=this.filters[g];h.update_ui_elt()}},clear_filters:function(){for(var g=0;g<this.filters.length;g++){var h=this.filters[g];h.slider.slider("option","values",[h.min,h.max])}this.alpha_filter=null;this.height_filter=null;this.parent_div.find(".icon-button").hide()},run_on_dataset:function(){var n=function(q,i,p){if(!(i in q)){q[i]=p}return q[i]};var m={},o,g;for(var l=0;l<this.filters.length;l++){o=this.filters[l];if(o.tool_id){if(o.min!==o.low){g=n(m,o.tool_id,[]);g[g.length]=o.tool_exp_name+" >= "+o.low}if(o.max!==o.high){g=n(m,o.tool_id,[]);g[g.length]=o.tool_exp_name+" <= "+o.high}}}var h=[];for(var k in m){h[h.length]=[k,m[k]]}(function j(u,r){var p=r[0],q=p[0],t=p[1],s="("+t.join(") and (")+")",i={cond:s,input:u,target_dataset_id:u,tool_id:q};r=r.slice(1);$.getJSON(run_tool_url,i,function(v){if(v.error){Galaxy.modal.show({title:"Filter Dataset",body:"Error running tool "+q,buttons:{Close:Galaxy.modal.hide()}})}else{if(r.length===0){Galaxy.modal.show({title:"Filtering Dataset",body:"Filter(s) are running on the complete dataset. Outputs are in dataset's history.",buttons:{Close:Galaxy.modal.hide()}})}else{j(v.dataset_id,r)}}})})(this.track.dataset_id,h)}});return{FiltersManager:b,NumberFilter:e}});
\ No newline at end of file
diff -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 -r f76c280cdee539b65315ac583f93bfa52feb790d static/scripts/packed/viz/trackster/painters.js
--- a/static/scripts/packed/viz/trackster/painters.js
+++ b/static/scripts/packed/viz/trackster/painters.js
@@ -1,1 +1,1 @@
-define(["libs/underscore"],function(_){var extend=_.extend;var BEFORE=1001,CONTAINS=1002,OVERLAP_START=1003,OVERLAP_END=1004,CONTAINED_BY=1005,AFTER=1006;var compute_overlap=function(first_region,second_region){var first_start=first_region[0],first_end=first_region[1],second_start=second_region[0],second_end=second_region[1],overlap;if(first_start<second_start){if(first_end<=second_start){overlap=BEFORE}else{if(first_end<=second_end){overlap=OVERLAP_START}else{overlap=CONTAINS}}}else{if(first_start>second_end){overlap=AFTER}else{if(first_end<=second_end){overlap=CONTAINED_BY}else{overlap=OVERLAP_END}}}return overlap};var is_overlap=function(first_region,second_region){var overlap=compute_overlap(first_region,second_region);return(overlap!==BEFORE&&overlap!==AFTER)};var dashedLine=function(ctx,x1,y1,x2,y2,dashLen){if(dashLen===undefined){dashLen=4}var dX=x2-x1;var dY=y2-y1;var dashes=Math.floor(Math.sqrt(dX*dX+dY*dY)/dashLen);var dashX=dX/dashes;var dashY=dY/dashes;var q;for(q=0;q<dashes;q++,x1+=dashX,y1+=dashY){if(q%2!==0){continue}ctx.fillRect(x1,y1,dashLen,1)}};var drawDownwardEquilateralTriangle=function(ctx,down_vertex_x,down_vertex_y,side_len){var x1=down_vertex_x-side_len/2,x2=down_vertex_x+side_len/2,y=down_vertex_y-Math.sqrt(side_len*3/2);ctx.beginPath();ctx.moveTo(x1,y);ctx.lineTo(x2,y);ctx.lineTo(down_vertex_x,down_vertex_y);ctx.lineTo(x1,y);ctx.strokeStyle=this.fillStyle;ctx.fill();ctx.stroke();ctx.closePath()};var Scaler=function(default_val){this.default_val=(default_val?default_val:1)};Scaler.prototype.gen_val=function(input){return this.default_val};var Painter=function(data,view_start,view_end,prefs,mode){this.data=data;this.view_start=view_start;this.view_end=view_end;this.prefs=extend({},this.default_prefs,prefs);this.mode=mode};Painter.prototype.default_prefs={};Painter.prototype.draw=function(ctx,width,height,w_scale){};var LinePainter=function(data,view_start,view_end,prefs,mode){Painter.call(this,data,view_start,view_end,prefs,mode);if(this.prefs.min_value===undefined){this.prefs.min_value=_.min(_.map(this.data,function(d){return d[1]}))||0}if(this.prefs.max_value===undefined){this.prefs.max_value=_.max(_.map(this.data,function(d){return d[1]}))||0}};LinePainter.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Histogram",color:"#000",overflow_color:"#F66"};LinePainter.prototype.draw=function(ctx,width,height,w_scale){var in_path=false,min_value=this.prefs.min_value,max_value=this.prefs.max_value,vertical_range=max_value-min_value,height_px=height,view_start=this.view_start,mode=this.mode,data=this.data;ctx.save();var y_zero=Math.round(height+min_value/vertical_range*height);if(mode!=="Intensity"){ctx.fillStyle="#aaa";ctx.fillRect(0,y_zero,width,1)}ctx.beginPath();var x_scaled,y,delta_x_px;if(data.length>1){delta_x_px=Math.ceil((data[1][0]-data[0][0])*w_scale)}else{delta_x_px=10}var painter_color=this.prefs.block_color||this.prefs.color,pref_color=parseInt(painter_color.slice(1),16),pref_r=(pref_color&16711680)>>16,pref_g=(pref_color&65280)>>8,pref_b=pref_color&255,top_overflow=false,bot_overflow=false;for(var i=0,len=data.length;i<len;i++){ctx.fillStyle=ctx.strokeStyle=painter_color;top_overflow=bot_overflow=false;x_scaled=Math.round((data[i][0]-view_start)*w_scale);y=data[i][1];if(y===null){if(in_path&&mode==="Filled"){ctx.lineTo(x_scaled,height_px)}in_path=false;continue}if(y<min_value){bot_overflow=true;y=min_value}else{if(y>max_value){top_overflow=true;y=max_value}}if(mode==="Histogram"){y=Math.round(y/vertical_range*height_px);ctx.fillRect(x_scaled,y_zero,delta_x_px,-y)}else{if(mode==="Intensity"){var saturation=(y-min_value)/vertical_range,new_r=Math.round(pref_r+(255-pref_r)*(1-saturation)),new_g=Math.round(pref_g+(255-pref_g)*(1-saturation)),new_b=Math.round(pref_b+(255-pref_b)*(1-saturation));ctx.fillStyle="rgb("+new_r+","+new_g+","+new_b+")";ctx.fillRect(x_scaled,0,delta_x_px,height_px)}else{y=Math.round(height_px-(y-min_value)/vertical_range*height_px);if(in_path){ctx.lineTo(x_scaled,y)}else{in_path=true;if(mode==="Filled"){ctx.moveTo(x_scaled,height_px);ctx.lineTo(x_scaled,y)}else{ctx.moveTo(x_scaled,y)}}}}ctx.fillStyle=this.prefs.overflow_color;if(top_overflow||bot_overflow){var overflow_x;if(mode==="Histogram"||mode==="Intensity"){overflow_x=delta_x_px}else{x_scaled-=2;overflow_x=4}if(top_overflow){ctx.fillRect(x_scaled,0,overflow_x,3)}if(bot_overflow){ctx.fillRect(x_scaled,height_px-3,overflow_x,3)}}ctx.fillStyle=painter_color}if(mode==="Filled"){if(in_path){ctx.lineTo(x_scaled,y_zero);ctx.lineTo(0,y_zero)}ctx.fill()}else{ctx.stroke()}ctx.restore()};var FeaturePositionMapper=function(slot_height){this.feature_positions={};this.slot_height=slot_height;this.translation=0;this.y_translation=0};FeaturePositionMapper.prototype.map_feature_data=function(feature_data,slot,x_start,x_end){if(!this.feature_positions[slot]){this.feature_positions[slot]=[]}this.feature_positions[slot].push({data:feature_data,x_start:x_start,x_end:x_end})};FeaturePositionMapper.prototype.get_feature_data=function(x,y){var slot=Math.floor((y-this.y_translation)/this.slot_height),feature_dict;if(!this.feature_positions[slot]){return null}x+=this.translation;for(var i=0;i<this.feature_positions[slot].length;i++){feature_dict=this.feature_positions[slot][i];if(x>=feature_dict.x_start&&x<=feature_dict.x_end){return feature_dict.data}}};var FeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){Painter.call(this,data,view_start,view_end,prefs,mode);this.alpha_scaler=(alpha_scaler?alpha_scaler:new Scaler());this.height_scaler=(height_scaler?height_scaler:new Scaler())};FeaturePainter.prototype.default_prefs={block_color:"#FFF",connector_color:"#FFF"};extend(FeaturePainter.prototype,{get_required_height:function(rows_required,width){var required_height=this.get_row_height(),y_scale=required_height,mode=this.mode;if(mode==="no_detail"||mode==="Squish"||mode==="Pack"){required_height=rows_required*y_scale}return required_height+this.get_top_padding(width)+this.get_bottom_padding(width)},get_top_padding:function(width){return 0},get_bottom_padding:function(width){return Math.max(Math.round(this.get_row_height()/2),5)},draw:function(ctx,width,height,w_scale,slots){var data=this.data,view_start=this.view_start,view_end=this.view_end;ctx.save();ctx.fillStyle=this.prefs.block_color;ctx.textAlign="right";var y_scale=this.get_row_height(),feature_mapper=new FeaturePositionMapper(y_scale),x_draw_coords;for(var i=0,len=data.length;i<len;i++){var feature=data[i],feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],slot=(slots&&slots[feature_uid]!==undefined?slots[feature_uid]:null);if((feature_start<view_end&&feature_end>view_start)&&(this.mode==="Dense"||slot!==null)){x_draw_coords=this.draw_element(ctx,this.mode,feature,slot,view_start,view_end,w_scale,y_scale,width);feature_mapper.map_feature_data(feature,slot,x_draw_coords[0],x_draw_coords[1])}}ctx.restore();feature_mapper.y_translation=this.get_top_padding(width);return feature_mapper},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){console.log("WARNING: Unimplemented function.");return[0,0]}});var DENSE_TRACK_HEIGHT=10,NO_DETAIL_TRACK_HEIGHT=3,SQUISH_TRACK_HEIGHT=5,PACK_TRACK_HEIGHT=10,NO_DETAIL_FEATURE_HEIGHT=1,DENSE_FEATURE_HEIGHT=9,SQUISH_FEATURE_HEIGHT=3,PACK_FEATURE_HEIGHT=9,LABEL_SPACING=2,CONNECTOR_COLOR="#ccc";var LinkedFeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){FeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.draw_background_connector=true;this.draw_individual_connectors=false};extend(LinkedFeaturePainter.prototype,FeaturePainter.prototype,{get_row_height:function(){var mode=this.mode,height;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="no_detail"){height=NO_DETAIL_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT}}}return height},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){var feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],feature_name=feature[3],feature_strand=feature[4],f_start=Math.floor(Math.max(0,(feature_start-tile_low-0.5)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low-0.5)*w_scale))),draw_start=f_start,draw_end=f_end,y_center=(mode==="Dense"?0:(0+slot))*y_scale+this.get_top_padding(width),thickness,y_start,thick_start=null,thick_end=null,block_color=(!feature_strand||feature_strand==="+"||feature_strand==="."?this.prefs.block_color:this.prefs.reverse_strand_color);label_color=this.prefs.label_color;ctx.globalAlpha=this.alpha_scaler.gen_val(feature);if(mode==="Dense"){slot=1}if(mode==="no_detail"){ctx.fillStyle=block_color;ctx.fillRect(f_start,y_center+5,f_end-f_start,NO_DETAIL_FEATURE_HEIGHT)}else{var feature_ts=feature[5],feature_te=feature[6],feature_blocks=feature[7],full_height=true;if(feature_ts&&feature_te){thick_start=Math.floor(Math.max(0,(feature_ts-tile_low)*w_scale));thick_end=Math.ceil(Math.min(width,Math.max(0,(feature_te-tile_low)*w_scale)))}var thin_height,thick_height;if(mode==="Squish"){thin_height=1;thick_height=SQUISH_FEATURE_HEIGHT;full_height=false}else{if(mode==="Dense"){thin_height=5;thick_height=DENSE_FEATURE_HEIGHT}else{thin_height=5;thick_height=PACK_FEATURE_HEIGHT}}if(!feature_blocks){ctx.fillStyle=block_color;ctx.fillRect(f_start,y_center+1,f_end-f_start,thick_height);if(feature_strand&&full_height){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand_inv")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand_inv")}}ctx.fillRect(f_start,y_center+1,f_end-f_start,thick_height)}}else{var cur_y_center,cur_height;if(mode==="Squish"||mode==="Dense"){cur_y_center=y_center+Math.floor(SQUISH_FEATURE_HEIGHT/2)+1;cur_height=1}else{if(feature_strand){cur_y_center=y_center;cur_height=thick_height}else{cur_y_center+=(SQUISH_FEATURE_HEIGHT/2)+1;cur_height=1}}if(this.draw_background_connector){if(mode==="Squish"||mode==="Dense"){ctx.fillStyle=CONNECTOR_COLOR}else{if(feature_strand){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand")}}}else{ctx.fillStyle=CONNECTOR_COLOR}}ctx.fillRect(f_start,cur_y_center,f_end-f_start,cur_height)}var start_and_height;for(var k=0,k_len=feature_blocks.length;k<k_len;k++){var block=feature_blocks[k],block_start=Math.floor(Math.max(0,(block[0]-tile_low-0.5)*w_scale)),block_end=Math.ceil(Math.min(width,Math.max((block[1]-tile_low-0.5)*w_scale))),last_block_start,last_block_end;if(block_start>block_end){continue}ctx.fillStyle=block_color;ctx.fillRect(block_start,y_center+(thick_height-thin_height)/2+1,block_end-block_start,thin_height);if(thick_start!==undefined&&feature_te>feature_ts&&!(block_start>thick_end||block_end<thick_start)){var block_thick_start=Math.max(block_start,thick_start),block_thick_end=Math.min(block_end,thick_end);ctx.fillRect(block_thick_start,y_center+1,block_thick_end-block_thick_start,thick_height);if(feature_blocks.length===1&&mode==="Pack"){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand_inv")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand_inv")}}if(block_thick_start+14<block_thick_end){block_thick_start+=2;block_thick_end-=2}ctx.fillRect(block_thick_start,y_center+1,block_thick_end-block_thick_start,thick_height)}}if(this.draw_individual_connectors&&last_block_start){this.draw_connector(ctx,last_block_start,last_block_end,block_start,block_end,y_center)}last_block_start=block_start;last_block_end=block_end}if(mode==="Pack"){ctx.globalAlpha=1;ctx.fillStyle="white";var hscale_factor=this.height_scaler.gen_val(feature),new_height=Math.ceil(thick_height*hscale_factor),ws_height=Math.round((thick_height-new_height)/2);if(hscale_factor!==1){ctx.fillRect(f_start,cur_y_center+1,f_end-f_start,ws_height);ctx.fillRect(f_start,cur_y_center+thick_height-ws_height+1,f_end-f_start,ws_height)}}}ctx.globalAlpha=1;if(feature_name&&mode==="Pack"&&feature_start>tile_low){ctx.fillStyle=label_color;if(tile_low===0&&f_start-ctx.measureText(feature_name).width<0){ctx.textAlign="left";ctx.fillText(feature_name,f_end+LABEL_SPACING,y_center+8);draw_end+=ctx.measureText(feature_name).width+LABEL_SPACING}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING,y_center+8);draw_start-=ctx.measureText(feature_name).width+LABEL_SPACING}}}ctx.globalAlpha=1;return[draw_start,draw_end]}});var ReadPainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler,ref_seq,base_color_fn){FeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.ref_seq=(ref_seq?ref_seq.data:null);this.base_color_fn=base_color_fn};extend(ReadPainter.prototype,FeaturePainter.prototype,{get_row_height:function(){var height,mode=this.mode;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT;if(this.prefs.show_insertions){height*=2}}}return height},_parse_cigar:function(cigar_str){var cigar_ops="MIDNSHP=X";var blocks=[[0,0]],cur_block=blocks[0],base_pos=0,parsed_cigar=_.map(cigar_str.match(/[0-9]+[MIDNSHP=X]/g),function(op){var op_len=parseInt(op.slice(0,-1),10),op_char=op.slice(-1);if(op_char==="N"){if(cur_block[1]!==0){cur_block=[base_pos+op_len,base_pos+op_len];blocks.push(cur_block)}}else{if("ISHP".indexOf(op_char)===-1){cur_block[1]+=op_len;base_pos+=op_len}}return[cigar_ops.indexOf(op_char),op_len]});return{blocks:blocks,cigar:parsed_cigar}},draw_read:function(ctx,mode,w_scale,y_center,tile_low,tile_high,feature_start,cigar,strand,read_seq){ctx.textAlign="center";var tile_region=[tile_low,tile_high],base_offset=0,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),pack_mode=(mode==="Pack");var draw_last=[];if(!cigar){cigar=[[0,read_seq.length]]}for(var cig_id=0,len=cigar.length;cig_id<len;cig_id++){var cig=cigar[cig_id],cig_op="MIDNSHP=X"[cig[0]],cig_len=cig[1];var seq_start=feature_start+base_offset,s_start=Math.floor(Math.max(-0.5*w_scale,(seq_start-tile_low-0.5)*w_scale)),s_end=Math.floor(Math.max(0,(seq_start+cig_len-tile_low-0.5)*w_scale));if(s_start===s_end){s_end+=1}switch(cig_op){case"H":break;case"S":seq_offset+=cig_len;break;case"M":case"=":case"X":if(is_overlap([seq_start,seq_start+cig_len],tile_region)){ctx.fillStyle=block_color;ctx.fillRect(s_start,y_center+(pack_mode?1:4),s_end-s_start,(pack_mode?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT));var seq=read_seq.slice(seq_offset,seq_offset+cig_len),ref_char,read_char;for(var c=0,str_len=seq.length;c<str_len;c++){if(seq_start+c>=tile_low&&seq_start+c<=tile_high){ref_char=(this.ref_seq?this.ref_seq[seq_start-tile_low+c]:null);read_char=seq[c];if((ref_char&&(!this.prefs.show_differences||(read_char.toLowerCase!=="n"&&(ref_char.toLowerCase()!==read_char.toLowerCase()))))||(!ref_char&&!this.prefs.show_differences)){var c_start=Math.floor(Math.max(0,(seq_start+c-tile_low)*w_scale));ctx.fillStyle=this.base_color_fn(seq[c]);if(pack_mode&&w_scale>char_width_px){ctx.fillText(seq[c],c_start,y_center+9)}else{if(w_scale>0.05){ctx.fillRect(c_start-gap,y_center+(pack_mode?1:4),Math.max(1,Math.round(w_scale)),(pack_mode?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT))}}}}}}seq_offset+=cig_len;base_offset+=cig_len;break;case"N":ctx.fillStyle=CONNECTOR_COLOR;ctx.fillRect(s_start,y_center+5,s_end-s_start,1);base_offset+=cig_len;break;case"D":ctx.fillStyle="black";ctx.fillRect(s_start,y_center+4,s_end-s_start,3);base_offset+=cig_len;break;case"P":break;case"I":var insert_x_coord=s_start-gap;if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var seq=read_seq.slice(seq_offset,seq_offset+cig_len);if(this.prefs.show_insertions){var x_center=s_start-(s_end-s_start)/2;if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){ctx.fillStyle="yellow";ctx.fillRect(x_center-gap,y_center-9,s_end-s_start,9);draw_last[draw_last.length]={type:"triangle",data:[insert_x_coord,y_center+4,5]};ctx.fillStyle=CONNECTOR_COLOR;switch(compute_overlap([seq_start,seq_start+cig_len],tile_region)){case (OVERLAP_START):seq=seq.slice(tile_low-seq_start);break;case (OVERLAP_END):seq=seq.slice(0,seq_start-tile_high);break;case (CONTAINED_BY):break;case (CONTAINS):seq=seq.slice(tile_low-seq_start,seq_start-tile_high);break}for(var c=0,str_len=seq.length;c<str_len;c++){var c_start=Math.floor(Math.max(0,(seq_start+c-tile_low)*w_scale));ctx.fillText(seq[c],c_start-(s_end-s_start)/2,y_center)}}else{ctx.fillStyle="yellow";ctx.fillRect(x_center,y_center+(this.mode!=="Dense"?2:5),s_end-s_start,(mode!=="Dense"?SQUISH_FEATURE_HEIGHT:DENSE_FEATURE_HEIGHT))}}else{if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){draw_last.push({type:"text",data:[seq.length,insert_x_coord,y_center+9]})}else{}}}seq_offset+=cig_len;break}}ctx.fillStyle="yellow";var item,type,data;for(var i=0;i<draw_last.length;i++){item=draw_last[i];type=item.type;data=item.data;if(type==="text"){ctx.save();ctx.font="bold "+ctx.font;ctx.fillText(data[0],data[1],data[2]);ctx.restore()}else{if(type==="triangle"){drawDownwardEquilateralTriangle(ctx,data[0],data[1],data[2])}}}},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){var feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],feature_name=feature[3],f_start=Math.floor(Math.max(-0.5*w_scale,(feature_start-tile_low-0.5)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low-0.5)*w_scale))),y_center=(mode==="Dense"?0:(0+slot))*y_scale,label_color=this.prefs.label_color;if(feature[5] instanceof Array){var b1_start=Math.floor(Math.max(0,(feature[4][0]-tile_low)*w_scale)),b1_end=Math.ceil(Math.min(width,Math.max(0,(feature[4][1]-tile_low)*w_scale))),b2_start=Math.floor(Math.max(0,(feature[5][0]-tile_low)*w_scale)),b2_end=Math.ceil(Math.min(width,Math.max(0,(feature[5][1]-tile_low)*w_scale))),connector=true;if(feature[4][1]>=tile_low&&feature[4][0]<=tile_high&&feature[4][2]){this.draw_read(ctx,mode,w_scale,y_center,tile_low,tile_high,feature[4][0],feature[4][2],feature[4][3],feature[4][4])}else{connector=false}if(feature[5][1]>=tile_low&&feature[5][0]<=tile_high&&feature[5][2]){this.draw_read(ctx,mode,w_scale,y_center,tile_low,tile_high,feature[5][0],feature[5][2],feature[5][3],feature[5][4])}else{connector=false}if(connector&&b2_start>b1_end){ctx.fillStyle=CONNECTOR_COLOR;dashedLine(ctx,b1_end,y_center+5,b2_start,y_center+5)}}else{this.draw_read(ctx,mode,w_scale,y_center,tile_low,tile_high,feature_start,feature[4],feature[5],feature[6])}if(mode==="Pack"&&feature_start>=tile_low&&feature_name!=="."){ctx.fillStyle=this.prefs.label_color;if(tile_low===0&&f_start-ctx.measureText(feature_name).width<0){ctx.textAlign="left";ctx.fillText(feature_name,f_end+LABEL_SPACING,y_center+8)}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING,y_center+8)}}return[0,0]}});var RefBasedReadPainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler,ref_seq,base_color_fn){ReadPainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler,ref_seq,base_color_fn)};extend(RefBasedReadPainter.prototype,ReadPainter.prototype,FeaturePainter,{draw_read:function(ctx,mode,w_scale,y_center,tile_low,tile_high,feature_start,cigar,strand,read_seq){ctx.textAlign="center";var tile_region=[tile_low,tile_high],base_offset=0,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),pack_mode=(mode==="Pack"),drawing_blocks=[];var draw_last=[];var t=this._parse_cigar(cigar);cigar=t.cigar;drawing_blocks=t.blocks;for(var i=0;i<drawing_blocks.length;i++){var block=drawing_blocks[i];if(is_overlap([feature_start+block[0],feature_start+block[1]],tile_region)){var s_start=Math.floor(Math.max(-0.5*w_scale,(feature_start+block[0]-tile_low-0.5)*w_scale)),s_end=Math.floor(Math.max(0,(feature_start+block[1]-tile_low-0.5)*w_scale));if(s_start===s_end){s_end+=1}ctx.fillStyle=block_color;ctx.fillRect(s_start,y_center+(pack_mode?1:4),s_end-s_start,(pack_mode?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT))}}for(var cig_id=0,len=cigar.length;cig_id<len;cig_id++){var cig=cigar[cig_id],cig_op="MIDNSHP=X"[cig[0]],cig_len=cig[1];var seq_start=feature_start+base_offset,s_start=Math.floor(Math.max(0,-0.5*w_scale,(seq_start-tile_low-0.5)*w_scale)),s_end=Math.floor(Math.max(0,(seq_start+cig_len-tile_low-0.5)*w_scale));if(s_start===s_end){s_end+=1}switch(cig_op){case"H":case"S":case"P":break;case"M":base_offset+=cig_len;break;case"=":case"X":if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var cur_seq="";if(cig_op==="X"){cur_seq=read_seq.slice(seq_offset,seq_offset+cig_len)}else{if(this.ref_seq){cur_seq=this.ref_seq.slice(Math.max(0,seq_start-tile_low),Math.min(seq_start-tile_low+cig_len,tile_high-tile_low))}}var start_pos=Math.max(seq_start,tile_low);for(var c=0;c<cur_seq.length;c++){if(cur_seq&&!this.prefs.show_differences||cig_op==="X"){var c_start=Math.floor(Math.max(0,(start_pos+c-tile_low)*w_scale));ctx.fillStyle=this.base_color_fn(cur_seq[c]);if(pack_mode&&w_scale>char_width_px){ctx.fillText(cur_seq[c],c_start,y_center+9)}else{if(w_scale>0.05){ctx.fillRect(c_start-gap,y_center+(pack_mode?1:4),Math.max(1,Math.round(w_scale)),(pack_mode?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT))}}}}}if(cig_op==="X"){seq_offset+=cig_len}base_offset+=cig_len;break;case"N":ctx.fillStyle=CONNECTOR_COLOR;ctx.fillRect(s_start,y_center+5,s_end-s_start,1);base_offset+=cig_len;break;case"D":ctx.fillStyle="black";ctx.fillRect(s_start,y_center+4,s_end-s_start,3);base_offset+=cig_len;break;case"I":var insert_x_coord=s_start-gap;if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var seq=read_seq.slice(seq_offset,seq_offset+cig_len);if(this.prefs.show_insertions){var x_center=s_start-(s_end-s_start)/2;if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){ctx.fillStyle="yellow";ctx.fillRect(x_center-gap,y_center-9,s_end-s_start,9);draw_last[draw_last.length]={type:"triangle",data:[insert_x_coord,y_center+4,5]};ctx.fillStyle=CONNECTOR_COLOR;switch(compute_overlap([seq_start,seq_start+cig_len],tile_region)){case (OVERLAP_START):seq=seq.slice(tile_low-seq_start);break;case (OVERLAP_END):seq=seq.slice(0,seq_start-tile_high);break;case (CONTAINED_BY):break;case (CONTAINS):seq=seq.slice(tile_low-seq_start,seq_start-tile_high);break}for(var c=0,str_len=seq.length;c<str_len;c++){var c_start=Math.floor(Math.max(0,(seq_start+c-tile_low)*w_scale));ctx.fillText(seq[c],c_start-(s_end-s_start)/2,y_center)}}else{ctx.fillStyle="yellow";ctx.fillRect(x_center,y_center+(this.mode!=="Dense"?2:5),s_end-s_start,(mode!=="Dense"?SQUISH_FEATURE_HEIGHT:DENSE_FEATURE_HEIGHT))}}else{if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){draw_last.push({type:"text",data:[seq.length,insert_x_coord,y_center+9]})}else{}}}seq_offset+=cig_len;break}}ctx.fillStyle="yellow";var item,type,data;for(var i=0;i<draw_last.length;i++){item=draw_last[i];type=item.type;data=item.data;if(type==="text"){ctx.save();ctx.font="bold "+ctx.font;ctx.fillText(data[0],data[1],data[2]);ctx.restore()}else{if(type==="triangle"){drawDownwardEquilateralTriangle(ctx,data[0],data[1],data[2])}}}}});var ArcLinkedFeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){LinkedFeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.longest_feature_length=this.calculate_longest_feature_length();this.draw_background_connector=false;this.draw_individual_connectors=true};extend(ArcLinkedFeaturePainter.prototype,FeaturePainter.prototype,LinkedFeaturePainter.prototype,{calculate_longest_feature_length:function(){var longest_feature_length=0;for(var i=0,len=this.data.length;i<len;i++){var feature=this.data[i],feature_start=feature[1],feature_end=feature[2];longest_feature_length=Math.max(longest_feature_length,feature_end-feature_start)}return longest_feature_length},get_top_padding:function(width){var view_range=this.view_end-this.view_start,w_scale=width/view_range;return Math.min(128,Math.ceil((this.longest_feature_length/2)*w_scale))},draw_connector:function(ctx,block1_start,block1_end,block2_start,block2_end,y_center){var x_center=(block1_end+block2_start)/2,radius=block2_start-x_center;var angle1=Math.PI,angle2=0;if(radius>0){ctx.beginPath();ctx.arc(x_center,y_center,block2_start-x_center,Math.PI,0);ctx.stroke()}}});var Color=function(rgb,a){if(Array.isArray(rgb)){this.rgb=rgb}else{if(rgb.length==6){this.rgb=rgb.match(/.{2}/g).map(function(c){return parseInt(c,16)})}else{if(rgb.length==7){this.rgb=rgb.substring(1,7).match(/.{2}/g).map(function(c){return parseInt(c,16)})}else{this.rgb=rgb.split("").map(function(c){return parseInt(c+c,16)})}}}this.alpha=typeof(a)==="number"?a:1};Color.prototype={eval:function(){return this},toCSS:function(){if(this.alpha<1){return"rgba("+this.rgb.map(function(c){return Math.round(c)}).concat(this.alpha).join(", ")+")"}else{return"#"+this.rgb.map(function(i){i=Math.round(i);i=(i>255?255:(i<0?0:i)).toString(16);return i.length===1?"0"+i:i}).join("")}},toHSL:function(){var r=this.rgb[0]/255,g=this.rgb[1]/255,b=this.rgb[2]/255,a=this.alpha;var max=Math.max(r,g,b),min=Math.min(r,g,b);var h,s,l=(max+min)/2,d=max-min;if(max===min){h=s=0}else{s=l>0.5?d/(2-max-min):d/(max+min);switch(max){case r:h=(g-b)/d+(g<b?6:0);break;case g:h=(b-r)/d+2;break;case b:h=(r-g)/d+4;break}h/=6}return{h:h*360,s:s,l:l,a:a}},toARGB:function(){var argb=[Math.round(this.alpha*255)].concat(this.rgb);return"#"+argb.map(function(i){i=Math.round(i);i=(i>255?255:(i<0?0:i)).toString(16);return i.length===1?"0"+i:i}).join("")},mix:function(color2,weight){color1=this;var p=weight;var w=p*2-1;var a=color1.toHSL().a-color2.toHSL().a;var w1=(((w*a==-1)?w:(w+a)/(1+w*a))+1)/2;var w2=1-w1;var rgb=[color1.rgb[0]*w1+color2.rgb[0]*w2,color1.rgb[1]*w1+color2.rgb[1]*w2,color1.rgb[2]*w1+color2.rgb[2]*w2];var alpha=color1.alpha*p+color2.alpha*(1-p);return new Color(rgb,alpha)}};var LinearRamp=function(start_color,end_color,start_value,end_value){this.start_color=new Color(start_color);this.end_color=new Color(end_color);this.start_value=start_value;this.end_value=end_value;this.value_range=end_value-start_value};LinearRamp.prototype.map_value=function(value){value=Math.max(value,this.start_value);value=Math.min(value,this.end_value);value=(value-this.start_value)/this.value_range;return this.start_color.mix(this.end_color,1-value).toCSS()};var SplitRamp=function(start_color,middle_color,end_color,start_value,end_value){this.positive_ramp=new LinearRamp(middle_color,end_color,0,end_value);this.negative_ramp=new LinearRamp(middle_color,start_color,0,-start_value);this.start_value=start_value;this.end_value=end_value};SplitRamp.prototype.map_value=function(value){value=Math.max(value,this.start_value);value=Math.min(value,this.end_value);if(value>=0){return this.positive_ramp.map_value(value)}else{return this.negative_ramp.map_value(-value)}};var DiagonalHeatmapPainter=function(data,view_start,view_end,prefs,mode){Painter.call(this,data,view_start,view_end,prefs,mode);var i,len;if(this.prefs.min_value===undefined){var min_value=Infinity;for(i=0,len=this.data.length;i<len;i++){min_value=Math.min(min_value,this.data[i][5])}this.prefs.min_value=min_value}if(this.prefs.max_value===undefined){var max_value=-Infinity;for(i=0,len=this.data.length;i<len;i++){max_value=Math.max(max_value,this.data[i][5])}this.prefs.max_value=max_value}};DiagonalHeatmapPainter.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Heatmap",pos_color:"#FF8C00",neg_color:"#4169E1"};DiagonalHeatmapPainter.prototype.draw=function(ctx,width,height,w_scale){var min_value=this.prefs.min_value,max_value=this.prefs.max_value,value_range=max_value-min_value,height_px=height,view_start=this.view_start,mode=this.mode,data=this.data,invsqrt2=1/Math.sqrt(2);var ramp=(new SplitRamp(this.prefs.neg_color,"#FFFFFF",this.prefs.pos_color,min_value,max_value));var d,s1,e1,s2,e2,value;var scale=function(p){return(p-view_start)*w_scale};ctx.save();ctx.rotate(-45*Math.PI/180);ctx.scale(invsqrt2,invsqrt2);for(var i=0,len=data.length;i<len;i++){d=data[i];s1=scale(d[1]);e1=scale(d[2]);s2=scale(d[4]);e2=scale(d[5]);value=d[6];ctx.fillStyle=(ramp.map_value(value));ctx.fillRect(s1,s2,(e1-s1),(e2-s2))}ctx.restore()};var VariantPainter=function(data,view_start,view_end,prefs,mode,base_color_fn){Painter.call(this,data,view_start,view_end,prefs,mode);this.base_color_fn=base_color_fn;this.divider_height=1};extend(VariantPainter.prototype,Painter.prototype,{get_row_height:function(){var mode=this.mode,height;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT}}return height},get_required_height:function(num_samples){var height=this.prefs.summary_height;if(this.prefs.show_sample_data){height+=this.divider_height+num_samples*this.get_row_height()}return height},draw:function(ctx,width,height,w_scale){ctx.save();var locus_data,pos,id,ref,alt,qual,filter,sample_gts,allele_counts,variant,draw_x_start,char_x_start,draw_y_start,genotype,base_px=Math.max(1,Math.floor(w_scale)),row_height=(this.mode==="Squish"?SQUISH_TRACK_HEIGHT:PACK_TRACK_HEIGHT),feature_height=(w_scale<0.1?row_height:(this.mode==="Squish"?SQUISH_FEATURE_HEIGHT:PACK_FEATURE_HEIGHT)),j;if(this.prefs.show_sample_data){ctx.fillStyle="#F3F3F3";ctx.globalAlpha=1;ctx.fillRect(0,this.prefs.summary_height-this.divider_height,width,this.divider_height)}ctx.textAlign="center";for(var i=0;i<this.data.length;i++){locus_data=this.data[i];pos=locus_data[1];alt=locus_data[4].split(",");sample_gts=locus_data[7].split(",");allele_counts=locus_data.slice(8);if(pos<this.view_start||pos>this.view_end){continue}draw_x_start=Math.floor(Math.max(-0.5*w_scale,(pos-this.view_start-0.5)*w_scale));char_x_start=Math.floor(Math.max(0,(pos-this.view_start)*w_scale));ctx.fillStyle="#999999";ctx.globalAlpha=1;ctx.fillRect(draw_x_start,0,base_px,this.prefs.summary_height);draw_y_start=this.prefs.summary_height;for(j=0;j<alt.length;j++){ctx.fillStyle=this.base_color_fn(alt[j]);allele_frac=allele_counts/sample_gts.length;draw_height=Math.ceil(this.prefs.summary_height*allele_frac);ctx.fillRect(draw_x_start,draw_y_start-draw_height,base_px,draw_height);draw_y_start-=draw_height}if(!this.prefs.show_sample_data){continue}draw_y_start=this.prefs.summary_height+this.divider_height;for(j=0;j<sample_gts.length;j++,draw_y_start+=row_height){genotype=(sample_gts[j]?sample_gts[j].split(/\/|\|/):["0","0"]);variant=null;if(genotype[0]===genotype[1]){if(genotype[0]==="."){}else{if(genotype[0]!=="0"){variant=alt[parseInt(genotype[0],10)-1];ctx.globalAlpha=1}}}else{variant=(genotype[0]!=="0"?genotype[0]:genotype[1]);variant=alt[parseInt(variant,10)-1];ctx.globalAlpha=0.5}if(variant){ctx.fillStyle=this.base_color_fn(variant);if(this.mode==="Squish"||w_scale<ctx.canvas.manager.char_width_px){ctx.fillRect(draw_x_start,draw_y_start+1,base_px,feature_height)}else{ctx.fillText(variant,char_x_start,draw_y_start+row_height)}}}}ctx.restore()}});return{Scaler:Scaler,LinePainter:LinePainter,LinkedFeaturePainter:LinkedFeaturePainter,ReadPainter:ReadPainter,RefBasedReadPainter:RefBasedReadPainter,ArcLinkedFeaturePainter:ArcLinkedFeaturePainter,DiagonalHeatmapPainter:DiagonalHeatmapPainter,VariantPainter:VariantPainter}});
\ No newline at end of file
+define(["libs/underscore"],function(_){var extend=_.extend;var BEFORE=1001,CONTAINS=1002,OVERLAP_START=1003,OVERLAP_END=1004,CONTAINED_BY=1005,AFTER=1006;var compute_overlap=function(first_region,second_region){var first_start=first_region[0],first_end=first_region[1],second_start=second_region[0],second_end=second_region[1],overlap;if(first_start<second_start){if(first_end<=second_start){overlap=BEFORE}else{if(first_end<=second_end){overlap=OVERLAP_START}else{overlap=CONTAINS}}}else{if(first_start>second_end){overlap=AFTER}else{if(first_end<=second_end){overlap=CONTAINED_BY}else{overlap=OVERLAP_END}}}return overlap};var is_overlap=function(first_region,second_region){var overlap=compute_overlap(first_region,second_region);return(overlap!==BEFORE&&overlap!==AFTER)};var dashedLine=function(ctx,x1,y1,x2,y2,dashLen){if(dashLen===undefined){dashLen=4}var dX=x2-x1;var dY=y2-y1;var dashes=Math.floor(Math.sqrt(dX*dX+dY*dY)/dashLen);var dashX=dX/dashes;var dashY=dY/dashes;var q;for(q=0;q<dashes;q++,x1+=dashX,y1+=dashY){if(q%2!==0){continue}ctx.fillRect(x1,y1,dashLen,1)}};var drawDownwardEquilateralTriangle=function(ctx,down_vertex_x,down_vertex_y,side_len){var x1=down_vertex_x-side_len/2,x2=down_vertex_x+side_len/2,y=down_vertex_y-Math.sqrt(side_len*3/2);ctx.beginPath();ctx.moveTo(x1,y);ctx.lineTo(x2,y);ctx.lineTo(down_vertex_x,down_vertex_y);ctx.lineTo(x1,y);ctx.strokeStyle=this.fillStyle;ctx.fill();ctx.stroke();ctx.closePath()};var Scaler=function(default_val){this.default_val=(default_val?default_val:1)};Scaler.prototype.gen_val=function(input){return this.default_val};var Painter=function(data,view_start,view_end,prefs,mode){this.data=data;this.view_start=view_start;this.view_end=view_end;this.prefs=extend({},this.default_prefs,prefs);this.mode=mode};Painter.prototype.default_prefs={};Painter.prototype.draw=function(ctx,width,height,w_scale){};var LinePainter=function(data,view_start,view_end,prefs,mode){Painter.call(this,data,view_start,view_end,prefs,mode);if(this.prefs.min_value===undefined){this.prefs.min_value=_.min(_.map(this.data,function(d){return d[1]}))||0}if(this.prefs.max_value===undefined){this.prefs.max_value=_.max(_.map(this.data,function(d){return d[1]}))||0}};LinePainter.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Histogram",color:"#000",overflow_color:"#F66"};LinePainter.prototype.draw=function(ctx,width,height,w_scale){var in_path=false,min_value=this.prefs.min_value,max_value=this.prefs.max_value,vertical_range=max_value-min_value,height_px=height,view_start=this.view_start,mode=this.mode,data=this.data;ctx.save();var y_zero=Math.round(height+min_value/vertical_range*height);if(mode!=="Intensity"){ctx.fillStyle="#aaa";ctx.fillRect(0,y_zero,width,1)}ctx.beginPath();var x_scaled,y,delta_x_px;if(data.length>1){delta_x_px=Math.ceil((data[1][0]-data[0][0])*w_scale)}else{delta_x_px=10}var painter_color=this.prefs.block_color||this.prefs.color,pref_color=parseInt(painter_color.slice(1),16),pref_r=(pref_color&16711680)>>16,pref_g=(pref_color&65280)>>8,pref_b=pref_color&255,top_overflow=false,bot_overflow=false;for(var i=0,len=data.length;i<len;i++){ctx.fillStyle=ctx.strokeStyle=painter_color;top_overflow=bot_overflow=false;x_scaled=Math.round((data[i][0]-view_start)*w_scale);y=data[i][1];if(y===null){if(in_path&&mode==="Filled"){ctx.lineTo(x_scaled,height_px)}in_path=false;continue}if(y<min_value){bot_overflow=true;y=min_value}else{if(y>max_value){top_overflow=true;y=max_value}}if(mode==="Histogram"){y=Math.round(y/vertical_range*height_px);ctx.fillRect(x_scaled,y_zero,delta_x_px,-y)}else{if(mode==="Intensity"){var saturation=(y-min_value)/vertical_range,new_r=Math.round(pref_r+(255-pref_r)*(1-saturation)),new_g=Math.round(pref_g+(255-pref_g)*(1-saturation)),new_b=Math.round(pref_b+(255-pref_b)*(1-saturation));ctx.fillStyle="rgb("+new_r+","+new_g+","+new_b+")";ctx.fillRect(x_scaled,0,delta_x_px,height_px)}else{y=Math.round(height_px-(y-min_value)/vertical_range*height_px);if(in_path){ctx.lineTo(x_scaled,y)}else{in_path=true;if(mode==="Filled"){ctx.moveTo(x_scaled,height_px);ctx.lineTo(x_scaled,y)}else{ctx.moveTo(x_scaled,y)}}}}ctx.fillStyle=this.prefs.overflow_color;if(top_overflow||bot_overflow){var overflow_x;if(mode==="Histogram"||mode==="Intensity"){overflow_x=delta_x_px}else{x_scaled-=2;overflow_x=4}if(top_overflow){ctx.fillRect(x_scaled,0,overflow_x,3)}if(bot_overflow){ctx.fillRect(x_scaled,height_px-3,overflow_x,3)}}ctx.fillStyle=painter_color}if(mode==="Filled"){if(in_path){ctx.lineTo(x_scaled,y_zero);ctx.lineTo(0,y_zero)}ctx.fill()}else{ctx.stroke()}ctx.restore()};var FeaturePositionMapper=function(slot_height){this.feature_positions={};this.slot_height=slot_height;this.translation=0;this.y_translation=0};FeaturePositionMapper.prototype.map_feature_data=function(feature_data,slot,x_start,x_end){if(!this.feature_positions[slot]){this.feature_positions[slot]=[]}this.feature_positions[slot].push({data:feature_data,x_start:x_start,x_end:x_end})};FeaturePositionMapper.prototype.get_feature_data=function(x,y){var slot=Math.floor((y-this.y_translation)/this.slot_height),feature_dict;if(!this.feature_positions[slot]){return null}x+=this.translation;for(var i=0;i<this.feature_positions[slot].length;i++){feature_dict=this.feature_positions[slot][i];if(x>=feature_dict.x_start&&x<=feature_dict.x_end){return feature_dict.data}}};var FeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){Painter.call(this,data,view_start,view_end,prefs,mode);this.alpha_scaler=(alpha_scaler?alpha_scaler:new Scaler());this.height_scaler=(height_scaler?height_scaler:new Scaler())};FeaturePainter.prototype.default_prefs={block_color:"#FFF",connector_color:"#FFF"};extend(FeaturePainter.prototype,{get_required_height:function(rows_required,width){var required_height=this.get_row_height(),y_scale=required_height,mode=this.mode;if(mode==="no_detail"||mode==="Squish"||mode==="Pack"){required_height=rows_required*y_scale}return required_height+this.get_top_padding(width)+this.get_bottom_padding(width)},get_top_padding:function(width){return 0},get_bottom_padding:function(width){return Math.max(Math.round(this.get_row_height()/2),5)},draw:function(ctx,width,height,w_scale,slots){var data=this.data,view_start=this.view_start,view_end=this.view_end;ctx.save();ctx.fillStyle=this.prefs.block_color;ctx.textAlign="right";var y_scale=this.get_row_height(),feature_mapper=new FeaturePositionMapper(y_scale),x_draw_coords;for(var i=0,len=data.length;i<len;i++){var feature=data[i],feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],slot=(slots&&slots[feature_uid]!==undefined?slots[feature_uid]:null);if((feature_start<view_end&&feature_end>view_start)&&(this.mode==="Dense"||slot!==null)){x_draw_coords=this.draw_element(ctx,this.mode,feature,slot,view_start,view_end,w_scale,y_scale,width);feature_mapper.map_feature_data(feature,slot,x_draw_coords[0],x_draw_coords[1])}}ctx.restore();feature_mapper.y_translation=this.get_top_padding(width);return feature_mapper},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){return[0,0]}});var DENSE_TRACK_HEIGHT=10,NO_DETAIL_TRACK_HEIGHT=3,SQUISH_TRACK_HEIGHT=5,PACK_TRACK_HEIGHT=10,NO_DETAIL_FEATURE_HEIGHT=1,DENSE_FEATURE_HEIGHT=9,SQUISH_FEATURE_HEIGHT=3,PACK_FEATURE_HEIGHT=9,LABEL_SPACING=2,CONNECTOR_COLOR="#ccc";var LinkedFeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){FeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.draw_background_connector=true;this.draw_individual_connectors=false};extend(LinkedFeaturePainter.prototype,FeaturePainter.prototype,{get_row_height:function(){var mode=this.mode,height;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="no_detail"){height=NO_DETAIL_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT}}}return height},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){var feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],feature_name=feature[3],feature_strand=feature[4],f_start=Math.floor(Math.max(0,(feature_start-tile_low-0.5)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low-0.5)*w_scale))),draw_start=f_start,draw_end=f_end,y_center=(mode==="Dense"?0:(0+slot))*y_scale+this.get_top_padding(width),thickness,y_start,thick_start=null,thick_end=null,block_color=(!feature_strand||feature_strand==="+"||feature_strand==="."?this.prefs.block_color:this.prefs.reverse_strand_color);label_color=this.prefs.label_color;ctx.globalAlpha=this.alpha_scaler.gen_val(feature);if(mode==="Dense"){slot=1}if(mode==="no_detail"){ctx.fillStyle=block_color;ctx.fillRect(f_start,y_center+5,f_end-f_start,NO_DETAIL_FEATURE_HEIGHT)}else{var feature_ts=feature[5],feature_te=feature[6],feature_blocks=feature[7],full_height=true;if(feature_ts&&feature_te){thick_start=Math.floor(Math.max(0,(feature_ts-tile_low)*w_scale));thick_end=Math.ceil(Math.min(width,Math.max(0,(feature_te-tile_low)*w_scale)))}var thin_height,thick_height;if(mode==="Squish"){thin_height=1;thick_height=SQUISH_FEATURE_HEIGHT;full_height=false}else{if(mode==="Dense"){thin_height=5;thick_height=DENSE_FEATURE_HEIGHT}else{thin_height=5;thick_height=PACK_FEATURE_HEIGHT}}if(!feature_blocks){ctx.fillStyle=block_color;ctx.fillRect(f_start,y_center+1,f_end-f_start,thick_height);if(feature_strand&&full_height){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand_inv")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand_inv")}}ctx.fillRect(f_start,y_center+1,f_end-f_start,thick_height)}}else{var cur_y_center,cur_height;if(mode==="Squish"||mode==="Dense"){cur_y_center=y_center+Math.floor(SQUISH_FEATURE_HEIGHT/2)+1;cur_height=1}else{if(feature_strand){cur_y_center=y_center;cur_height=thick_height}else{cur_y_center+=(SQUISH_FEATURE_HEIGHT/2)+1;cur_height=1}}if(this.draw_background_connector){if(mode==="Squish"||mode==="Dense"){ctx.fillStyle=CONNECTOR_COLOR}else{if(feature_strand){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand")}}}else{ctx.fillStyle=CONNECTOR_COLOR}}ctx.fillRect(f_start,cur_y_center,f_end-f_start,cur_height)}var start_and_height;for(var k=0,k_len=feature_blocks.length;k<k_len;k++){var block=feature_blocks[k],block_start=Math.floor(Math.max(0,(block[0]-tile_low-0.5)*w_scale)),block_end=Math.ceil(Math.min(width,Math.max((block[1]-tile_low-0.5)*w_scale))),last_block_start,last_block_end;if(block_start>block_end){continue}ctx.fillStyle=block_color;ctx.fillRect(block_start,y_center+(thick_height-thin_height)/2+1,block_end-block_start,thin_height);if(thick_start!==undefined&&feature_te>feature_ts&&!(block_start>thick_end||block_end<thick_start)){var block_thick_start=Math.max(block_start,thick_start),block_thick_end=Math.min(block_end,thick_end);ctx.fillRect(block_thick_start,y_center+1,block_thick_end-block_thick_start,thick_height);if(feature_blocks.length===1&&mode==="Pack"){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand_inv")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand_inv")}}if(block_thick_start+14<block_thick_end){block_thick_start+=2;block_thick_end-=2}ctx.fillRect(block_thick_start,y_center+1,block_thick_end-block_thick_start,thick_height)}}if(this.draw_individual_connectors&&last_block_start){this.draw_connector(ctx,last_block_start,last_block_end,block_start,block_end,y_center)}last_block_start=block_start;last_block_end=block_end}if(mode==="Pack"){ctx.globalAlpha=1;ctx.fillStyle="white";var hscale_factor=this.height_scaler.gen_val(feature),new_height=Math.ceil(thick_height*hscale_factor),ws_height=Math.round((thick_height-new_height)/2);if(hscale_factor!==1){ctx.fillRect(f_start,cur_y_center+1,f_end-f_start,ws_height);ctx.fillRect(f_start,cur_y_center+thick_height-ws_height+1,f_end-f_start,ws_height)}}}ctx.globalAlpha=1;if(feature_name&&mode==="Pack"&&feature_start>tile_low){ctx.fillStyle=label_color;if(tile_low===0&&f_start-ctx.measureText(feature_name).width<0){ctx.textAlign="left";ctx.fillText(feature_name,f_end+LABEL_SPACING,y_center+8);draw_end+=ctx.measureText(feature_name).width+LABEL_SPACING}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING,y_center+8);draw_start-=ctx.measureText(feature_name).width+LABEL_SPACING}}}ctx.globalAlpha=1;return[draw_start,draw_end]}});var ReadPainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler,ref_seq,base_color_fn){FeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.ref_seq=(ref_seq?ref_seq.data:null);this.base_color_fn=base_color_fn};extend(ReadPainter.prototype,FeaturePainter.prototype,{get_row_height:function(){var height,mode=this.mode;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT;if(this.prefs.show_insertions){height*=2}}}return height},_parse_cigar:function(cigar_str){var cigar_ops="MIDNSHP=X";var blocks=[[0,0]],cur_block=blocks[0],base_pos=0,parsed_cigar=_.map(cigar_str.match(/[0-9]+[MIDNSHP=X]/g),function(op){var op_len=parseInt(op.slice(0,-1),10),op_char=op.slice(-1);if(op_char==="N"){if(cur_block[1]!==0){cur_block=[base_pos+op_len,base_pos+op_len];blocks.push(cur_block)}}else{if("ISHP".indexOf(op_char)===-1){cur_block[1]+=op_len;base_pos+=op_len}}return[cigar_ops.indexOf(op_char),op_len]});return{blocks:blocks,cigar:parsed_cigar}},draw_read:function(ctx,mode,w_scale,y_center,tile_low,tile_high,feature_start,cigar,strand,read_seq){ctx.textAlign="center";var tile_region=[tile_low,tile_high],base_offset=0,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),pack_mode=(mode==="Pack");var draw_last=[];if(!cigar){cigar=[[0,read_seq.length]]}for(var cig_id=0,len=cigar.length;cig_id<len;cig_id++){var cig=cigar[cig_id],cig_op="MIDNSHP=X"[cig[0]],cig_len=cig[1];var seq_start=feature_start+base_offset,s_start=Math.floor(Math.max(-0.5*w_scale,(seq_start-tile_low-0.5)*w_scale)),s_end=Math.floor(Math.max(0,(seq_start+cig_len-tile_low-0.5)*w_scale));if(s_start===s_end){s_end+=1}switch(cig_op){case"H":break;case"S":seq_offset+=cig_len;break;case"M":case"=":case"X":if(is_overlap([seq_start,seq_start+cig_len],tile_region)){ctx.fillStyle=block_color;ctx.fillRect(s_start,y_center+(pack_mode?1:4),s_end-s_start,(pack_mode?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT));var seq=read_seq.slice(seq_offset,seq_offset+cig_len),ref_char,read_char;for(var c=0,str_len=seq.length;c<str_len;c++){if(seq_start+c>=tile_low&&seq_start+c<=tile_high){ref_char=(this.ref_seq?this.ref_seq[seq_start-tile_low+c]:null);read_char=seq[c];if((ref_char&&(!this.prefs.show_differences||(read_char.toLowerCase!=="n"&&(ref_char.toLowerCase()!==read_char.toLowerCase()))))||(!ref_char&&!this.prefs.show_differences)){var c_start=Math.floor(Math.max(0,(seq_start+c-tile_low)*w_scale));ctx.fillStyle=this.base_color_fn(seq[c]);if(pack_mode&&w_scale>char_width_px){ctx.fillText(seq[c],c_start,y_center+9)}else{if(w_scale>0.05){ctx.fillRect(c_start-gap,y_center+(pack_mode?1:4),Math.max(1,Math.round(w_scale)),(pack_mode?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT))}}}}}}seq_offset+=cig_len;base_offset+=cig_len;break;case"N":ctx.fillStyle=CONNECTOR_COLOR;ctx.fillRect(s_start,y_center+5,s_end-s_start,1);base_offset+=cig_len;break;case"D":ctx.fillStyle="black";ctx.fillRect(s_start,y_center+4,s_end-s_start,3);base_offset+=cig_len;break;case"P":break;case"I":var insert_x_coord=s_start-gap;if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var seq=read_seq.slice(seq_offset,seq_offset+cig_len);if(this.prefs.show_insertions){var x_center=s_start-(s_end-s_start)/2;if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){ctx.fillStyle="yellow";ctx.fillRect(x_center-gap,y_center-9,s_end-s_start,9);draw_last[draw_last.length]={type:"triangle",data:[insert_x_coord,y_center+4,5]};ctx.fillStyle=CONNECTOR_COLOR;switch(compute_overlap([seq_start,seq_start+cig_len],tile_region)){case (OVERLAP_START):seq=seq.slice(tile_low-seq_start);break;case (OVERLAP_END):seq=seq.slice(0,seq_start-tile_high);break;case (CONTAINED_BY):break;case (CONTAINS):seq=seq.slice(tile_low-seq_start,seq_start-tile_high);break}for(var c=0,str_len=seq.length;c<str_len;c++){var c_start=Math.floor(Math.max(0,(seq_start+c-tile_low)*w_scale));ctx.fillText(seq[c],c_start-(s_end-s_start)/2,y_center)}}else{ctx.fillStyle="yellow";ctx.fillRect(x_center,y_center+(this.mode!=="Dense"?2:5),s_end-s_start,(mode!=="Dense"?SQUISH_FEATURE_HEIGHT:DENSE_FEATURE_HEIGHT))}}else{if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){draw_last.push({type:"text",data:[seq.length,insert_x_coord,y_center+9]})}else{}}}seq_offset+=cig_len;break}}ctx.fillStyle="yellow";var item,type,data;for(var i=0;i<draw_last.length;i++){item=draw_last[i];type=item.type;data=item.data;if(type==="text"){ctx.save();ctx.font="bold "+ctx.font;ctx.fillText(data[0],data[1],data[2]);ctx.restore()}else{if(type==="triangle"){drawDownwardEquilateralTriangle(ctx,data[0],data[1],data[2])}}}},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){var feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],feature_name=feature[3],f_start=Math.floor(Math.max(-0.5*w_scale,(feature_start-tile_low-0.5)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low-0.5)*w_scale))),y_center=(mode==="Dense"?0:(0+slot))*y_scale,label_color=this.prefs.label_color;if(feature[5] instanceof Array){var b1_start=Math.floor(Math.max(0,(feature[4][0]-tile_low)*w_scale)),b1_end=Math.ceil(Math.min(width,Math.max(0,(feature[4][1]-tile_low)*w_scale))),b2_start=Math.floor(Math.max(0,(feature[5][0]-tile_low)*w_scale)),b2_end=Math.ceil(Math.min(width,Math.max(0,(feature[5][1]-tile_low)*w_scale))),connector=true;if(feature[4][1]>=tile_low&&feature[4][0]<=tile_high&&feature[4][2]){this.draw_read(ctx,mode,w_scale,y_center,tile_low,tile_high,feature[4][0],feature[4][2],feature[4][3],feature[4][4])}else{connector=false}if(feature[5][1]>=tile_low&&feature[5][0]<=tile_high&&feature[5][2]){this.draw_read(ctx,mode,w_scale,y_center,tile_low,tile_high,feature[5][0],feature[5][2],feature[5][3],feature[5][4])}else{connector=false}if(connector&&b2_start>b1_end){ctx.fillStyle=CONNECTOR_COLOR;dashedLine(ctx,b1_end,y_center+5,b2_start,y_center+5)}}else{this.draw_read(ctx,mode,w_scale,y_center,tile_low,tile_high,feature_start,feature[4],feature[5],feature[6])}if(mode==="Pack"&&feature_start>=tile_low&&feature_name!=="."){ctx.fillStyle=this.prefs.label_color;if(tile_low===0&&f_start-ctx.measureText(feature_name).width<0){ctx.textAlign="left";ctx.fillText(feature_name,f_end+LABEL_SPACING,y_center+8)}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING,y_center+8)}}return[0,0]}});var RefBasedReadPainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler,ref_seq,base_color_fn){ReadPainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler,ref_seq,base_color_fn)};extend(RefBasedReadPainter.prototype,ReadPainter.prototype,FeaturePainter,{draw_read:function(ctx,mode,w_scale,y_center,tile_low,tile_high,feature_start,cigar,strand,read_seq){ctx.textAlign="center";var tile_region=[tile_low,tile_high],base_offset=0,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),pack_mode=(mode==="Pack"),drawing_blocks=[];var draw_last=[];var t=this._parse_cigar(cigar);cigar=t.cigar;drawing_blocks=t.blocks;for(var i=0;i<drawing_blocks.length;i++){var block=drawing_blocks[i];if(is_overlap([feature_start+block[0],feature_start+block[1]],tile_region)){var s_start=Math.floor(Math.max(-0.5*w_scale,(feature_start+block[0]-tile_low-0.5)*w_scale)),s_end=Math.floor(Math.max(0,(feature_start+block[1]-tile_low-0.5)*w_scale));if(s_start===s_end){s_end+=1}ctx.fillStyle=block_color;ctx.fillRect(s_start,y_center+(pack_mode?1:4),s_end-s_start,(pack_mode?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT))}}for(var cig_id=0,len=cigar.length;cig_id<len;cig_id++){var cig=cigar[cig_id],cig_op="MIDNSHP=X"[cig[0]],cig_len=cig[1];var seq_start=feature_start+base_offset,s_start=Math.floor(Math.max(0,-0.5*w_scale,(seq_start-tile_low-0.5)*w_scale)),s_end=Math.floor(Math.max(0,(seq_start+cig_len-tile_low-0.5)*w_scale));if(s_start===s_end){s_end+=1}switch(cig_op){case"H":case"S":case"P":break;case"M":base_offset+=cig_len;break;case"=":case"X":if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var cur_seq="";if(cig_op==="X"){cur_seq=read_seq.slice(seq_offset,seq_offset+cig_len)}else{if(this.ref_seq){cur_seq=this.ref_seq.slice(Math.max(0,seq_start-tile_low),Math.min(seq_start-tile_low+cig_len,tile_high-tile_low))}}var start_pos=Math.max(seq_start,tile_low);for(var c=0;c<cur_seq.length;c++){if(cur_seq&&!this.prefs.show_differences||cig_op==="X"){var c_start=Math.floor(Math.max(0,(start_pos+c-tile_low)*w_scale));ctx.fillStyle=this.base_color_fn(cur_seq[c]);if(pack_mode&&w_scale>char_width_px){ctx.fillText(cur_seq[c],c_start,y_center+9)}else{if(w_scale>0.05){ctx.fillRect(c_start-gap,y_center+(pack_mode?1:4),Math.max(1,Math.round(w_scale)),(pack_mode?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT))}}}}}if(cig_op==="X"){seq_offset+=cig_len}base_offset+=cig_len;break;case"N":ctx.fillStyle=CONNECTOR_COLOR;ctx.fillRect(s_start,y_center+5,s_end-s_start,1);base_offset+=cig_len;break;case"D":ctx.fillStyle="black";ctx.fillRect(s_start,y_center+4,s_end-s_start,3);base_offset+=cig_len;break;case"I":var insert_x_coord=s_start-gap;if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var seq=read_seq.slice(seq_offset,seq_offset+cig_len);if(this.prefs.show_insertions){var x_center=s_start-(s_end-s_start)/2;if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){ctx.fillStyle="yellow";ctx.fillRect(x_center-gap,y_center-9,s_end-s_start,9);draw_last[draw_last.length]={type:"triangle",data:[insert_x_coord,y_center+4,5]};ctx.fillStyle=CONNECTOR_COLOR;switch(compute_overlap([seq_start,seq_start+cig_len],tile_region)){case (OVERLAP_START):seq=seq.slice(tile_low-seq_start);break;case (OVERLAP_END):seq=seq.slice(0,seq_start-tile_high);break;case (CONTAINED_BY):break;case (CONTAINS):seq=seq.slice(tile_low-seq_start,seq_start-tile_high);break}for(var c=0,str_len=seq.length;c<str_len;c++){var c_start=Math.floor(Math.max(0,(seq_start+c-tile_low)*w_scale));ctx.fillText(seq[c],c_start-(s_end-s_start)/2,y_center)}}else{ctx.fillStyle="yellow";ctx.fillRect(x_center,y_center+(this.mode!=="Dense"?2:5),s_end-s_start,(mode!=="Dense"?SQUISH_FEATURE_HEIGHT:DENSE_FEATURE_HEIGHT))}}else{if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){draw_last.push({type:"text",data:[seq.length,insert_x_coord,y_center+9]})}else{}}}seq_offset+=cig_len;break}}ctx.fillStyle="yellow";var item,type,data;for(var i=0;i<draw_last.length;i++){item=draw_last[i];type=item.type;data=item.data;if(type==="text"){ctx.save();ctx.font="bold "+ctx.font;ctx.fillText(data[0],data[1],data[2]);ctx.restore()}else{if(type==="triangle"){drawDownwardEquilateralTriangle(ctx,data[0],data[1],data[2])}}}}});var ArcLinkedFeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){LinkedFeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.longest_feature_length=this.calculate_longest_feature_length();this.draw_background_connector=false;this.draw_individual_connectors=true};extend(ArcLinkedFeaturePainter.prototype,FeaturePainter.prototype,LinkedFeaturePainter.prototype,{calculate_longest_feature_length:function(){var longest_feature_length=0;for(var i=0,len=this.data.length;i<len;i++){var feature=this.data[i],feature_start=feature[1],feature_end=feature[2];longest_feature_length=Math.max(longest_feature_length,feature_end-feature_start)}return longest_feature_length},get_top_padding:function(width){var view_range=this.view_end-this.view_start,w_scale=width/view_range;return Math.min(128,Math.ceil((this.longest_feature_length/2)*w_scale))},draw_connector:function(ctx,block1_start,block1_end,block2_start,block2_end,y_center){var x_center=(block1_end+block2_start)/2,radius=block2_start-x_center;var angle1=Math.PI,angle2=0;if(radius>0){ctx.beginPath();ctx.arc(x_center,y_center,block2_start-x_center,Math.PI,0);ctx.stroke()}}});var Color=function(rgb,a){if(Array.isArray(rgb)){this.rgb=rgb}else{if(rgb.length==6){this.rgb=rgb.match(/.{2}/g).map(function(c){return parseInt(c,16)})}else{if(rgb.length==7){this.rgb=rgb.substring(1,7).match(/.{2}/g).map(function(c){return parseInt(c,16)})}else{this.rgb=rgb.split("").map(function(c){return parseInt(c+c,16)})}}}this.alpha=typeof(a)==="number"?a:1};Color.prototype={eval:function(){return this},toCSS:function(){if(this.alpha<1){return"rgba("+this.rgb.map(function(c){return Math.round(c)}).concat(this.alpha).join(", ")+")"}else{return"#"+this.rgb.map(function(i){i=Math.round(i);i=(i>255?255:(i<0?0:i)).toString(16);return i.length===1?"0"+i:i}).join("")}},toHSL:function(){var r=this.rgb[0]/255,g=this.rgb[1]/255,b=this.rgb[2]/255,a=this.alpha;var max=Math.max(r,g,b),min=Math.min(r,g,b);var h,s,l=(max+min)/2,d=max-min;if(max===min){h=s=0}else{s=l>0.5?d/(2-max-min):d/(max+min);switch(max){case r:h=(g-b)/d+(g<b?6:0);break;case g:h=(b-r)/d+2;break;case b:h=(r-g)/d+4;break}h/=6}return{h:h*360,s:s,l:l,a:a}},toARGB:function(){var argb=[Math.round(this.alpha*255)].concat(this.rgb);return"#"+argb.map(function(i){i=Math.round(i);i=(i>255?255:(i<0?0:i)).toString(16);return i.length===1?"0"+i:i}).join("")},mix:function(color2,weight){color1=this;var p=weight;var w=p*2-1;var a=color1.toHSL().a-color2.toHSL().a;var w1=(((w*a==-1)?w:(w+a)/(1+w*a))+1)/2;var w2=1-w1;var rgb=[color1.rgb[0]*w1+color2.rgb[0]*w2,color1.rgb[1]*w1+color2.rgb[1]*w2,color1.rgb[2]*w1+color2.rgb[2]*w2];var alpha=color1.alpha*p+color2.alpha*(1-p);return new Color(rgb,alpha)}};var LinearRamp=function(start_color,end_color,start_value,end_value){this.start_color=new Color(start_color);this.end_color=new Color(end_color);this.start_value=start_value;this.end_value=end_value;this.value_range=end_value-start_value};LinearRamp.prototype.map_value=function(value){value=Math.max(value,this.start_value);value=Math.min(value,this.end_value);value=(value-this.start_value)/this.value_range;return this.start_color.mix(this.end_color,1-value).toCSS()};var SplitRamp=function(start_color,middle_color,end_color,start_value,end_value){this.positive_ramp=new LinearRamp(middle_color,end_color,0,end_value);this.negative_ramp=new LinearRamp(middle_color,start_color,0,-start_value);this.start_value=start_value;this.end_value=end_value};SplitRamp.prototype.map_value=function(value){value=Math.max(value,this.start_value);value=Math.min(value,this.end_value);if(value>=0){return this.positive_ramp.map_value(value)}else{return this.negative_ramp.map_value(-value)}};var DiagonalHeatmapPainter=function(data,view_start,view_end,prefs,mode){Painter.call(this,data,view_start,view_end,prefs,mode);var i,len;if(this.prefs.min_value===undefined){var min_value=Infinity;for(i=0,len=this.data.length;i<len;i++){min_value=Math.min(min_value,this.data[i][5])}this.prefs.min_value=min_value}if(this.prefs.max_value===undefined){var max_value=-Infinity;for(i=0,len=this.data.length;i<len;i++){max_value=Math.max(max_value,this.data[i][5])}this.prefs.max_value=max_value}};DiagonalHeatmapPainter.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Heatmap",pos_color:"#FF8C00",neg_color:"#4169E1"};DiagonalHeatmapPainter.prototype.draw=function(ctx,width,height,w_scale){var min_value=this.prefs.min_value,max_value=this.prefs.max_value,value_range=max_value-min_value,height_px=height,view_start=this.view_start,mode=this.mode,data=this.data,invsqrt2=1/Math.sqrt(2);var ramp=(new SplitRamp(this.prefs.neg_color,"#FFFFFF",this.prefs.pos_color,min_value,max_value));var d,s1,e1,s2,e2,value;var scale=function(p){return(p-view_start)*w_scale};ctx.save();ctx.rotate(-45*Math.PI/180);ctx.scale(invsqrt2,invsqrt2);for(var i=0,len=data.length;i<len;i++){d=data[i];s1=scale(d[1]);e1=scale(d[2]);s2=scale(d[4]);e2=scale(d[5]);value=d[6];ctx.fillStyle=(ramp.map_value(value));ctx.fillRect(s1,s2,(e1-s1),(e2-s2))}ctx.restore()};var VariantPainter=function(data,view_start,view_end,prefs,mode,base_color_fn){Painter.call(this,data,view_start,view_end,prefs,mode);this.base_color_fn=base_color_fn;this.divider_height=1};extend(VariantPainter.prototype,Painter.prototype,{get_row_height:function(){var mode=this.mode,height;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT}}return height},get_required_height:function(num_samples){var height=this.prefs.summary_height;if(num_samples>1&&this.prefs.show_sample_data){height+=this.divider_height+num_samples*this.get_row_height()}return height},draw:function(ctx,width,height,w_scale){ctx.save();var locus_data,pos,id,ref,alt,qual,filter,sample_gts,allele_counts,variant,draw_x_start,char_x_start,draw_y_start,genotype,base_px=Math.max(1,Math.floor(w_scale)),num_samples=(this.data.length?this.data[0][7].split(",").length:0),row_height=(this.mode==="Squish"?SQUISH_TRACK_HEIGHT:PACK_TRACK_HEIGHT),feature_height=(w_scale<0.1?row_height:(this.mode==="Squish"?SQUISH_FEATURE_HEIGHT:PACK_FEATURE_HEIGHT)),draw_summary=true,j;if(num_samples===1){row_height=feature_height=(w_scale<ctx.canvas.manager.char_width_px?this.prefs.summary_height:row_height);draw_summary=false}if(this.prefs.show_sample_data&&draw_summary){ctx.fillStyle="#F3F3F3";ctx.globalAlpha=1;ctx.fillRect(0,this.prefs.summary_height-this.divider_height,width,this.divider_height)}ctx.textAlign="center";for(var i=0;i<this.data.length;i++){locus_data=this.data[i];pos=locus_data[1];alt=locus_data[4].split(",");sample_gts=locus_data[7].split(",");allele_counts=locus_data.slice(8);if(pos<this.view_start||pos>this.view_end){continue}draw_x_start=Math.floor(Math.max(-0.5*w_scale,(pos-this.view_start-0.5)*w_scale));char_x_start=Math.floor(Math.max(0,(pos-this.view_start)*w_scale));if(draw_summary){ctx.fillStyle="#999999";ctx.globalAlpha=1;ctx.fillRect(draw_x_start,0,base_px,this.prefs.summary_height);draw_y_start=this.prefs.summary_height;for(j=0;j<alt.length;j++){ctx.fillStyle=this.base_color_fn(alt[j]);allele_frac=allele_counts/sample_gts.length;draw_height=Math.ceil(this.prefs.summary_height*allele_frac);ctx.fillRect(draw_x_start,draw_y_start-draw_height,base_px,draw_height);draw_y_start-=draw_height}}if(!this.prefs.show_sample_data){continue}draw_y_start=(draw_summary?this.prefs.summary_height+this.divider_height:0);for(j=0;j<sample_gts.length;j++,draw_y_start+=row_height){genotype=(sample_gts[j]?sample_gts[j].split(/\/|\|/):["0","0"]);variant=null;if(genotype[0]===genotype[1]){if(genotype[0]==="."){}else{if(genotype[0]!=="0"){variant=alt[parseInt(genotype[0],10)-1];ctx.globalAlpha=1}}}else{variant=(genotype[0]!=="0"?genotype[0]:genotype[1]);variant=alt[parseInt(variant,10)-1];ctx.globalAlpha=0.5}if(variant){ctx.fillStyle=this.base_color_fn(variant);if(this.mode==="Squish"||w_scale<ctx.canvas.manager.char_width_px){ctx.fillRect(draw_x_start,draw_y_start+1,base_px,feature_height)}else{ctx.fillText(variant,char_x_start,draw_y_start+row_height)}}}}ctx.restore()}});return{Scaler:Scaler,LinePainter:LinePainter,LinkedFeaturePainter:LinkedFeaturePainter,ReadPainter:ReadPainter,RefBasedReadPainter:RefBasedReadPainter,ArcLinkedFeaturePainter:ArcLinkedFeaturePainter,DiagonalHeatmapPainter:DiagonalHeatmapPainter,VariantPainter:VariantPainter}});
\ No newline at end of file
diff -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 -r f76c280cdee539b65315ac583f93bfa52feb790d static/scripts/packed/viz/trackster/tracks.js
--- a/static/scripts/packed/viz/trackster/tracks.js
+++ b/static/scripts/packed/viz/trackster/tracks.js
@@ -1,1 +1,1 @@
-define(["libs/underscore","viz/visualization","viz/trackster/util","viz/trackster/slotting","viz/trackster/painters","viz/trackster/filters","mvc/data","mvc/tools"],function(ac,w,j,s,L,h,X,R){var o=ac.extend;var l={};var i=function(ad,ae){l[ad.attr("id")]=ae};var k=function(ad,af,ah,ag){ah=".group";var ae={};l[ad.attr("id")]=ag;ad.bind("drag",{handle:"."+af,relative:true},function(ap,aq){var ao=$(this),au=$(this).parent(),al=au.children(),an=l[$(this).attr("id")],ak,aj,ar,ai,am;aj=$(this).parents(ah);if(aj.length!==0){ar=aj.position().top;ai=ar+aj.outerHeight();if(aq.offsetY<ar){$(this).insertBefore(aj);var at=l[aj.attr("id")];at.remove_drawable(an);at.container.add_drawable_before(an,at);return}else{if(aq.offsetY>ai){$(this).insertAfter(aj);var at=l[aj.attr("id")];at.remove_drawable(an);at.container.add_drawable(an);return}}}aj=null;for(am=0;am<al.length;am++){ak=$(al.get(am));ar=ak.position().top;ai=ar+ak.outerHeight();if(ak.is(ah)&&this!==ak.get(0)&&aq.offsetY>=ar&&aq.offsetY<=ai){if(aq.offsetY-ar<ai-aq.offsetY){ak.find(".content-div").prepend(this)}else{ak.find(".content-div").append(this)}if(an.container){an.container.remove_drawable(an)}l[ak.attr("id")].add_drawable(an);return}}for(am=0;am<al.length;am++){ak=$(al.get(am));if(aq.offsetY<ak.position().top&&!(ak.hasClass("reference-track")||ak.hasClass("intro"))){break}}if(am===al.length){if(this!==al.get(am-1)){au.append(this);l[au.attr("id")].move_drawable(an,am)}}else{if(this!==al.get(am)){$(this).insertBefore(al.get(am));l[au.attr("id")].move_drawable(an,(aq.deltaY>0?am-1:am))}}}).bind("dragstart",function(){ae["border-top"]=ad.css("border-top");ae["border-bottom"]=ad.css("border-bottom");$(this).css({"border-top":"1px solid blue","border-bottom":"1px solid blue"})}).bind("dragend",function(){$(this).css(ae)})};var aa=16,G=9,D=20,z=100,I=12000,S=400,K=5000,v=100,m="Cannot display dataset due to an error. ",J="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",E="No data for this chrom/contig.",u="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",x="Tool cannot be rerun: ",a="Loading data...",U="Ready for display",Q=10,H=20,A=["Histogram","Line","Filled","Intensity"];function V(ae,ad){if(!ad){ad=0}var af=Math.pow(10,ad);return Math.round(ae*af)/af}var p=function(ae,ad,ag){if(!p.id_counter){p.id_counter=0}this.id=p.id_counter++;this.name=ag.name;this.view=ae;this.container=ad;this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name}],saved_values:ag.prefs,onchange:function(){this.track.set_name(this.track.config.values.name)}});this.prefs=this.config.values;this.drag_handle_class=ag.drag_handle_class;this.is_overview=false;this.action_icons={};this.content_visible=true;this.container_div=this.build_container_div();this.header_div=this.build_header_div();if(this.header_div){this.container_div.append(this.header_div);this.icons_div=$("<div/>").css("float","left").hide().appendTo(this.header_div);this.build_action_icons(this.action_icons_def);this.header_div.append($("<div style='clear: both'/>"));this.header_div.dblclick(function(ah){ah.stopPropagation()});var af=this;this.container_div.hover(function(){af.icons_div.show()},function(){af.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};p.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(ad){if(ad.content_visible){ad.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");ad.hide_contents();ad.content_visible=false}else{ad.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");ad.content_visible=true;ad.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"settings-icon",on_click_fn:function(ae){var ag=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},ad=function(){ae.config.update_from_form($(Galaxy.modal.el));Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},af=function(ah){if((ah.keyCode||ah.which)===27){ag()}else{if((ah.keyCode||ah.which)===13){ad()}}};$(window).bind("keypress.check_enter_esc",af);Galaxy.modal.show({title:"Configure",body:ae.config.build_form(),buttons:{Cancel:ag,Ok:ad}})}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(ad){$(".tooltip").remove();ad.remove()}}];o(p.prototype,{init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.content_visible){return true}return false},request_draw:function(){},_draw:function(ad){},to_dict:function(){},set_name:function(ad){this.old_name=this.name;this.name=ad;this.name_div.text(this.name)},revert_name:function(){if(this.old_name){this.name=this.old_name;this.name_div.text(this.name)}},remove:function(){this.changed();this.container.remove_drawable(this);var ad=this.view;this.container_div.hide(0,function(){$(this).remove();ad.update_intro_div()})},build_container_div:function(){},build_header_div:function(){},add_action_icon:function(ae,aj,ai,ah,ad,ag){var af=this;this.action_icons[ae]=$("<a/>").attr("href","javascript:void(0);").attr("title",aj).addClass("icon-button").addClass(ai).tooltip().click(function(){ah(af)}).appendTo(this.icons_div);if(ag){this.action_icons[ae].hide()}},build_action_icons:function(ad){var af;for(var ae=0;ae<ad.length;ae++){af=ad[ae];this.add_action_icon(af.name,af.title,af.css_class,af.on_click_fn,af.prepend,af.hide)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){},get_drawables:function(){}});var y=function(ae,ad,af){p.call(this,ae,ad,af);this.obj_type=af.obj_type;this.drawables=[]};o(y.prototype,p.prototype,{unpack_drawables:function(af){this.drawables=[];var ae;for(var ad=0;ad<af.length;ad++){ae=n(af[ad],this.view,this);this.add_drawable(ae)}},init:function(){for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad].init()}},_draw:function(ad){for(var ae=0;ae<this.drawables.length;ae++){this.drawables[ae]._draw(ad)}},to_dict:function(){var ae=[];for(var ad=0;ad<this.drawables.length;ad++){ae.push(this.drawables[ad].to_dict())}return{name:this.name,prefs:this.prefs,obj_type:this.obj_type,drawables:ae}},add_drawable:function(ad){this.drawables.push(ad);ad.container=this;this.changed()},add_drawable_before:function(af,ad){this.changed();var ae=this.drawables.indexOf(ad);if(ae!==-1){this.drawables.splice(ae,0,af);return true}return false},replace_drawable:function(af,ad,ae){var ag=this.drawables.indexOf(af);if(ag!==-1){this.drawables[ag]=ad;if(ae){af.container_div.replaceWith(ad.container_div)}this.changed()}return ag},remove_drawable:function(ae){var ad=this.drawables.indexOf(ae);if(ad!==-1){this.drawables.splice(ad,1);ae.container=null;this.changed();return true}return false},move_drawable:function(ae,af){var ad=this.drawables.indexOf(ae);if(ad!==-1){this.drawables.splice(ad,1);this.drawables.splice(af,0,ae);this.changed();return true}return false},get_drawables:function(){return this.drawables},get_tracks:function(ag){var ad=this.drawables.slice(0),ae=[],af;while(ad.length!==0){af=ad.shift();if(af instanceof ag){ae.push(af)}else{if(af.drawables){ad=ad.concat(af.drawables)}}}return ae}});var P=function(ae,ad,ag){o(ag,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});y.call(this,ae,ad,ag);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);i(this.container_div,this);i(this.content_div,this);k(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new h.FiltersManager(this);this.header_div.after(this.filters_manager.parent_div);this.saved_filters_managers=[];if("drawables" in ag){this.unpack_drawables(ag.drawables)}if("filters" in ag){var af=this.filters_manager;this.filters_manager=new h.FiltersManager(this,ag.filters);af.parent_div.replaceWith(this.filters_manager.parent_div);if(ag.filters.visible){this.setup_multitrack_filtering()}}};o(P.prototype,p.prototype,y.prototype,{action_icons_def:[p.prototype.action_icons_def[0],p.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(ad){$(".tooltip").remove();ad.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(ad){if(ad.filters_manager.visible()){ad.filters_manager.clear_filters();ad._restore_filter_managers()}else{ad.setup_multitrack_filtering();ad.request_draw({clear_tile_cache:true})}ad.filters_manager.toggle()}},p.prototype.action_icons_def[2]],build_container_div:function(){var ad=$("<div/>").addClass("group").attr("id","group_"+this.id);if(this.container){this.container.content_div.append(ad)}return ad},build_header_div:function(){var ad=$("<div/>").addClass("track-header");ad.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.name).appendTo(ad);return ad},hide_contents:function(){this.tiles_div.hide()},show_contents:function(){this.tiles_div.show();this.request_draw()},update_icons:function(){var af=this.drawables.length;if(af===0){this.action_icons.composite_icon.hide();this.action_icons.filters_icon.hide()}else{if(af===1){if(this.drawables[0] instanceof e){this.action_icons.composite_icon.show()}this.action_icons.filters_icon.hide()}else{var am,al,aj,ap=true,ah=this.drawables[0].get_type(),ad=0;for(am=0;am<af;am++){aj=this.drawables[am];if(aj.get_type()!==ah){can_composite=false;break}if(aj instanceof c){ad++}}if(ap||ad===1){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".tooltip").remove()}if(ad>1&&ad===this.drawables.length){var aq={},ae;aj=this.drawables[0];for(al=0;al<aj.filters_manager.filters.length;al++){ae=aj.filters_manager.filters[al];aq[ae.name]=[ae]}for(am=1;am<this.drawables.length;am++){aj=this.drawables[am];for(al=0;al<aj.filters_manager.filters.length;al++){ae=aj.filters_manager.filters[al];if(ae.name in aq){aq[ae.name].push(ae)}}}this.filters_manager.remove_all();var ag,ai,ak,an;for(var ao in aq){ag=aq[ao];if(ag.length===ad){ai=new h.NumberFilter({name:ag[0].name,index:ag[0].index});this.filters_manager.add_filter(ai)}}if(this.filters_manager.filters.length>0){this.action_icons.filters_icon.show()}else{this.action_icons.filters_icon.hide()}}else{this.action_icons.filters_icon.hide()}}}},_restore_filter_managers:function(){for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad].filters_manager=this.saved_filters_managers[ad]}this.saved_filters_managers=[]},setup_multitrack_filtering:function(){if(this.filters_manager.filters.length>0){this.saved_filters_managers=[];for(var ad=0;ad<this.drawables.length;ad++){drawable=this.drawables[ad];this.saved_filters_managers.push(drawable.filters_manager);drawable.filters_manager=this.filters_manager}}this.filters_manager.init_filters()},show_composite_track:function(){var ae=new e(this.view,this.view,{name:this.name,drawables:this.drawables});var ad=this.container.replace_drawable(this,ae,true);ae.request_draw()},add_drawable:function(ad){y.prototype.add_drawable.call(this,ad);this.update_icons()},remove_drawable:function(ad){y.prototype.remove_drawable.call(this,ad);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var ad=o(y.prototype.to_dict.call(this),{filters:this.filters_manager.to_dict()});if(this.filters_manager.visible()){this.setup_multitrack_filtering()}return ad},request_draw:function(ad){ac.each(this.drawables,function(ae){ae.request_draw(ad)})}});var Y=Backbone.View.extend({initialize:function(ad){o(ad,{obj_type:"View"});y.call(this,"View",ad.container,ad);this.chrom=null;this.vis_id=ad.vis_id;this.dbkey=ad.dbkey;this.label_tracks=[];this.tracks_to_be_redrawn=[];this.max_low=0;this.max_high=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.load_chroms_deferred=null;this.render();this.canvas_manager=new w.CanvasManager(this.container.get(0).ownerDocument);this.reset();this.config=new F({track:this,params:[{key:"a_color",label:"A Color",type:"color",default_value:"#FF0000"},{key:"c_color",label:"C Color",type:"color",default_value:"#00FF00"},{key:"g_color",label:"G Color",type:"color",default_value:"#0000FF"},{key:"t_color",label:"T Color",type:"color",default_value:"#FF00FF"},{key:"n_color",label:"N Color",type:"color",default_value:"#AAAAAA"}],saved_values:ad.prefs,onchange:function(){track.request_redraw({clear_tile_cache:true})}})},render:function(){this.requested_redraw=false;var af=this.container,ad=this;this.top_container=$("<div/>").addClass("top-container").appendTo(af);this.browser_content_div=$("<div/>").addClass("content").css("position","relative").appendTo(af);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(af);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("<div/>").addClass("viewport-container").attr("id","viewport-container").appendTo(this.browser_content_div);this.content_div=this.viewport_container;i(this.viewport_container,ad);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var ag=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){w.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":ad.dbkey},function(ah){ac.each(ah,function(ai){ad.add_drawable(n(ai,ad,ad))})})});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("trackster-nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("trackster-nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("<a/>").attr("href","javascript:void(0);").attr("title","Close overview").addClass("icon-button overview-close tooltip").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div/>").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var ae=function(ah){if(ah.type==="focusout"||(ah.keyCode||ah.which)===13||(ah.keyCode||ah.which)===27){if((ah.keyCode||ah.which)!==27){ad.go_to($(this).val())}$(this).hide();$(this).val("");ad.location_span.show();ad.chrom_select.show()}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ae).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("original-title","Click to change location").tooltip({placement:"bottom"}).appendTo(this.nav_controls);this.location_span.click(function(){ad.location_span.hide();ad.chrom_select.hide();ad.nav_input.val(ad.chrom+":"+ad.low+"-"+ad.high);ad.nav_input.css("display","inline-block");ad.nav_input.select();ad.nav_input.focus();ad.nav_input.autocomplete({source:function(aj,ah){var ak=[],ai=$.map(ad.get_tracks(c),function(al){return al.data_manager.search_features(aj.term).success(function(am){ak=ak.concat(am)})});$.when.apply($,ai).done(function(){ah($.map(ak,function(al){return{label:al[0],value:al[1]}}))})}})});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.nav_controls)}this.zo_link=$("<a/>").attr("id","zoom-out").attr("title","Zoom out").tooltip({placement:"bottom"}).click(function(){ad.zoom_out();ad.request_redraw()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tooltip({placement:"bottom"}).click(function(){ad.zoom_in();ad.request_redraw()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){ad.change_chrom(ad.chrom_select.val())});this.browser_content_div.click(function(ah){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(ah){ad.zoom_in(ah.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(ah,ai){this.current_x=ai.offsetX}).bind("drag",function(ah,aj){var ak=aj.offsetX-this.current_x;this.current_x=aj.offsetX;var ai=Math.round(ak/ad.viewport_container.width()*(ad.max_high-ad.max_low));ad.move_delta(-ai)});this.overview_close.click(function(){ad.reset_overview()});this.viewport_container.bind("draginit",function(ah,ai){if(ah.clientX>ad.viewport_container.width()-16){return false}}).bind("dragstart",function(ah,ai){ai.original_low=ad.low;ai.current_height=ah.clientY;ai.current_x=ai.offsetX}).bind("drag",function(aj,al){var ah=$(this);var am=al.offsetX-al.current_x;var ai=ah.scrollTop()-(aj.clientY-al.current_height);ah.scrollTop(ai);al.current_height=aj.clientY;al.current_x=al.offsetX;var ak=Math.round(am/ad.viewport_container.width()*(ad.high-ad.low));ad.move_delta(ak)}).bind("mousewheel",function(aj,al,ai,ah){if(ai){ai*=50;var ak=Math.round(-ai/ad.viewport_container.width()*(ad.high-ad.low));ad.move_delta(ak)}});this.top_labeltrack.bind("dragstart",function(ah,ai){return $("<div />").css({height:ad.browser_content_div.height()+ad.top_labeltrack.height()+ad.nav_labeltrack.height()+1,top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag",function(al,am){$(am.proxy).css({left:Math.min(al.pageX,am.startX)-ad.container.offset().left,width:Math.abs(al.pageX-am.startX)});var ai=Math.min(al.pageX,am.startX)-ad.container.offset().left,ah=Math.max(al.pageX,am.startX)-ad.container.offset().left,ak=(ad.high-ad.low),aj=ad.viewport_container.width();ad.update_location(Math.round(ai/aj*ak)+ad.low,Math.round(ah/aj*ak)+ad.low)}).bind("dragend",function(am,an){var ai=Math.min(am.pageX,an.startX),ah=Math.max(am.pageX,an.startX),ak=(ad.high-ad.low),aj=ad.viewport_container.width(),al=ad.low;ad.low=Math.round(ai/aj*ak)+al;ad.high=Math.round(ah/aj*ak)+al;$(an.proxy).remove();ad.request_redraw()});this.add_label_track(new W(this,{content_div:this.top_labeltrack}));this.add_label_track(new W(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){if(this.resize_timer){clearTimeout(this.resize_timer)}this.resize_timer=setTimeout(function(){ad.resize_window()},500)});$(document).bind("redraw",function(){ad.redraw()});this.reset();$(window).trigger("resize")},get_base_color:function(ad){return this.config.values[ad.toLowerCase()+"_color"]||this.config.values.n_color}});o(Y.prototype,y.prototype,{changed:function(){this.has_changes=true},update_intro_div:function(){if(this.drawables.length===0){this.intro_div.show()}else{this.intro_div.hide()}},trigger_navigate:function(ae,ag,ad,ah){if(this.timer){clearTimeout(this.timer)}if(ah){var af=this;this.timer=setTimeout(function(){af.trigger("navigate",ae+":"+ag+"-"+ad)},500)}else{view.trigger("navigate",ae+":"+ag+"-"+ad)}},update_location:function(ad,af){this.location_span.text(commatize(ad)+" - "+commatize(af));this.nav_input.val(this.chrom+":"+commatize(ad)+"-"+commatize(af));var ae=view.chrom_select.val();if(ae!==""){this.trigger_navigate(ae,view.low,view.high,true)}},load_chroms:function(af){af.num=v;var ad=this,ae=$.Deferred();$.ajax({url:galaxy_config.root+"api/genomes/"+this.dbkey,data:af,dataType:"json",success:function(ah){if(ah.chrom_info.length===0){return}if(ah.reference){ad.add_label_track(new B(ad))}ad.chrom_data=ah.chrom_info;var ak='<option value="">Select Chrom/Contig</option>';for(var aj=0,ag=ad.chrom_data.length;aj<ag;aj++){var ai=ad.chrom_data[aj].chrom;ak+='<option value="'+ai+'">'+ai+"</option>"}if(ah.prev_chroms){ak+='<option value="previous">Previous '+v+"</option>"}if(ah.next_chroms){ak+='<option value="next">Next '+v+"</option>"}ad.chrom_select.html(ak);ad.chrom_start_index=ah.start_index;ae.resolve(ah.chrom_info)},error:function(){alert("Could not load chroms for this dbkey:",ad.dbkey)}});return ae},change_chrom:function(ai,ae,ak){var af=this;if(!af.chrom_data){af.load_chroms_deferred.then(function(){af.change_chrom(ai,ae,ak)});return}if(!ai||ai==="None"){return}if(ai==="previous"){af.load_chroms({low:this.chrom_start_index-v});return}if(ai==="next"){af.load_chroms({low:this.chrom_start_index+v});return}var aj=$.grep(af.chrom_data,function(al,am){return al.chrom===ai})[0];if(aj===undefined){af.load_chroms({chrom:ai},function(){af.change_chrom(ai,ae,ak)});return}else{if(ai!==af.chrom){af.chrom=ai;af.chrom_select.val(af.chrom);af.max_high=aj.len-1;af.reset();for(var ah=0,ad=af.drawables.length;ah<ad;ah++){var ag=af.drawables[ah];if(ag.init){ag.init()}}if(af.reference_track){af.reference_track.init()}}if(ae&&ak){af.low=Math.max(ae,0);af.high=Math.min(ak,af.max_high)}else{af.low=0;af.high=af.max_high}af.reset_overview();af.request_redraw()}},go_to:function(ah){ah=ah.replace(/,/g,"");ah=ah.replace(/:|\-/g," ");var ae=ah.split(/\s+/),ag=ae[0],af=(ae[1]?parseInt(ae[1],10):null),ad=(ae[2]?parseInt(ae[2],10):null);if(!ad){af=af-15;ad=af+15}this.change_chrom(ag,af,ad)},move_fraction:function(af){var ad=this;var ae=ad.high-ad.low;this.move_delta(af*ae)},move_delta:function(ag){var ad=this;var af=ad.high-ad.low;if(ad.low-ag<ad.max_low){ad.low=ad.max_low;ad.high=ad.max_low+af}else{if(ad.high-ag>ad.max_high){ad.high=ad.max_high;ad.low=ad.max_high-af}else{ad.high-=ag;ad.low-=ag}}ad.request_redraw({data_fetch:false});if(this.redraw_on_move_fn){clearTimeout(this.redraw_on_move_fn)}this.redraw_on_move_fn=setTimeout(function(){ad.request_redraw()},200);var ae=ad.chrom_select.val();this.trigger_navigate(ae,ad.low,ad.high,true)},add_drawable:function(ad){y.prototype.add_drawable.call(this,ad);ad.init();this.changed();this.update_intro_div()},add_label_track:function(ad){ad.view=this;ad.init();this.label_tracks.push(ad)},remove_drawable:function(af,ae){y.prototype.remove_drawable.call(this,af);if(ae){var ad=this;af.container_div.hide(0,function(){$(this).remove();ad.update_intro_div()})}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(ae,af){var ad=this,ag=(af?[af]:ad.drawables);ac.each(ag,function(ah){var ai=ac.find(ad.tracks_to_be_redrawn,function(aj){return aj[0]===ah});if(ai){ai[1]=ae}else{ad.tracks_to_be_redrawn.push([ah,ae])}});if(!this.requested_redraw){requestAnimationFrame(function(){ad._redraw()});this.requested_redraw=true}},_redraw:function(){this.requested_redraw=false;var ad=this.low,ah=this.high;if(ad<this.max_low){ad=this.max_low}if(ah>this.max_high){ah=this.max_high}var ae=this.high-this.low;if(this.high!==0&&ae<this.min_separation){ah=ad+this.min_separation}this.low=Math.floor(ad);this.high=Math.ceil(ah);this.update_location(this.low,this.high);this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var ag=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ai=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var af=13;this.overview_box.css({left:ag,width:Math.max(af,ai)}).show();if(ai<af){this.overview_box.css("left",ag-(af-ai)/2)}if(this.overview_highlight){this.overview_highlight.css({left:ag,width:ai})}ac.each(this.tracks_to_be_redrawn,function(al){var aj=al[0],ak=al[1];if(aj){aj._draw(ak)}});this.tracks_to_be_redrawn=[];ac.each(this.label_tracks,function(aj){aj._draw()})},zoom_in:function(ae,af){if(this.max_high===0||this.high-this.low<=this.min_separation){return}var ag=this.high-this.low,ah=ag/2+this.low,ad=(ag/this.zoom_factor)/2;if(ae){ah=ae/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(ah-ad);this.high=Math.round(ah+ad);this.changed();this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var ae=this.high-this.low,af=ae/2+this.low,ad=(ae*this.zoom_factor)/2;this.low=Math.round(af-ad);this.high=Math.round(af+ad);this.changed();this.request_redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());this.request_redraw()},set_overview:function(af){if(this.overview_drawable){if(this.overview_drawable.dataset.id===af.dataset.id){return}this.overview_viewport.find(".track").remove()}var ae=af.copy({content_div:this.overview_viewport}),ad=this;ae.header_div.hide();ae.is_overview=true;ad.overview_drawable=ae;this.overview_drawable.postdraw_actions=function(){ad.overview_highlight.show().height(ad.overview_drawable.content_div.height());ad.overview_viewport.height(ad.overview_drawable.content_div.height()+ad.overview_box.outerHeight());ad.overview_close.show();ad.resize_window()};ad.overview_drawable.request_draw();this.changed()},reset_overview:function(){$(".tooltip").remove();this.overview_viewport.find(".track-tile").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide();view.resize_window();view.overview_drawable=null}});var q=R.Tool.extend({defaults:{track:null},initialize:function(ad){if(ad.tool_state!==undefined){this.set("hidden",ad.tool_state.hidden)}this.remove_inputs(["data","hidden_data","conditional"])},state_dict:function(ad){return ac.extend(this.get_inputs_dict(),{hidden:!this.is_visible()})}});var t=Backbone.View.extend({events:{"change input":"update_value"},render:function(){var af=this.$el.addClass("param-row"),ag=this.model;var ad=$("<div>").addClass("param-label").text(ag.get("label")).appendTo(af);var ae=$("<div/>").addClass("param-input").html(ag.get("html")).appendTo(af);ae.find(":input").val(ag.get("value"));$("<div style='clear: both;'/>").appendTo(af)},update_value:function(ad){this.model.set_value($(ad.target).val())}});var ab=Backbone.View.extend({initialize:function(ad){this.model.on("change:hidden",this.set_visible,this)},render:function(){var ae=this;tool=this.model,parent_div=this.$el.addClass("dynamic-tool").hide();parent_div.bind("drag",function(ai){ai.stopPropagation()}).click(function(ai){ai.stopPropagation()}).bind("dblclick",function(ai){ai.stopPropagation()}).keydown(function(ai){ai.stopPropagation()});var af=$("<div class='tool-name'>").appendTo(parent_div).text(tool.get("name"));tool.get("inputs").each(function(aj){var ai=new t({model:aj});ai.render();parent_div.append(ai.$el)});parent_div.find("input").click(function(){$(this).select()});var ag=$("<div>").addClass("param-row").appendTo(parent_div);var ah=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(ag);var ad=$("<input type='submit'>").attr("value","Run on visible region").css("margin-left","3em").appendTo(ag);ad.click(function(){ae.run_on_region()});ah.click(function(){ae.run_on_dataset()});if(tool.is_visible()){this.$el.show()}},set_visible:function(){if(this.model.is_visible()){this.$el.show()}else{this.$el.hide()}},update_params:function(){for(var ad=0;ad<this.params.length;ad++){this.params[ad].update_value()}},run_on_dataset:function(){var ad=this.model;this.run({target_dataset_id:this.model.get("track").dataset.id,action:"rerun",tool_id:ad.id},null,function(ae){Galaxy.modal.show({title:ad.get("name")+" is Running",body:ad.get("name")+" is running on the complete dataset. Tool outputs are in dataset's history.",buttons:{Close:function(){Galaxy.modal.hide()}}})})},run_on_region:function(){var ae=this.model.get("track"),ah=this.model,aj=new w.GenomeRegion({chrom:ae.view.chrom,start:ae.view.low,end:ae.view.high}),ak={target_dataset_id:ae.dataset.id,action:"rerun",tool_id:ah.id,regions:[aj.toJSON()]},ai=ae,am=ak.tool_id+ai.tool_region_and_parameters_str(aj),ad;if(ai.container===view){var al=new P(view,view,{name:this.name});var ag=ai.container.replace_drawable(ai,al,false);al.container_div.insertBefore(ai.view.content_div.children()[ag]);al.add_drawable(ai);ai.container_div.appendTo(al.content_div);ad=al}else{ad=ai.container}var af=new ai.constructor(view,ad,{name:am,hda_ldda:"hda"});af.init_for_tool_data();af.change_mode(ai.mode);af.set_filters_manager(ai.filters_manager.copy(af));af.update_icons();ad.add_drawable(af);af.tiles_div.text("Starting job.");this.run(ak,af,function(an){af.set_dataset(new X.Dataset(an));af.tiles_div.text("Running job.");af.init()})},run:function(ad,af,ag){ad.inputs=this.model.get_inputs_dict();var ae=new j.ServerStateDeferred({ajax_settings:{url:galaxy_config.root+"api/tools",data:JSON.stringify(ad),dataType:"json",contentType:"application/json",type:"POST"},interval:2000,success_fn:function(ah){return ah!=="pending"}});$.when(ae.go()).then(function(ah){if(ah==="no converter"){af.container_div.addClass("error");af.content_div.text(J)}else{if(ah.error){af.container_div.addClass("error");af.content_div.text(x+ah.message)}else{ag(ah)}}})}});var C=function(ad,ae){L.Scaler.call(this,ae);this.filter=ad};C.prototype.gen_val=function(ad){if(this.filter.high===Number.MAX_VALUE||this.filter.low===-Number.MAX_VALUE||this.filter.low===this.filter.high){return this.default_val}return((parseFloat(ad[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var F=function(ad){this.track=ad.track;this.params=ad.params;this.values={};this.restore_values((ad.saved_values?ad.saved_values:{}));this.onchange=ad.onchange};o(F.prototype,{set_param_default_value:function(ae,ad){var af=ac.find(this.params,function(ag){return ag.key===ae});if(af){af.default_value=ad}},set_param_value:function(ad,ae){var af=ac.find(this.params,function(ag){return ag.key===ad});if(af){if(typeof ae==="string"||ae instanceof String){if(ae.trim()===""){ae=af.default_value}else{if(af.type==="float"){ae=parseFloat(ae)}else{if(af.type==="int"){ae=parseInt(ae,10)}}}}}if(this.values[ad]!==ae){this.values[ad]=ae;return true}else{return false}},restore_values:function(ad){var ae=this;$.each(this.params,function(af,ag){if(ad[ag.key]!==undefined){ae.values[ag.key]=ad[ag.key]}else{ae.values[ag.key]=ag.default_value}})},build_form:function(){var ag=this;var ad=$("<div />");var af;function ae(al,ah){for(var ap=0;ap<al.length;ap++){af=al[ap];if(af.hidden){continue}var aj="param_"+ap;var au=ag.values[af.key];var aw=$("<div class='form-row' />").appendTo(ah);aw.append($("<label />").attr("for",aj).text(af.label+":"));if(af.type==="bool"){aw.append($('<input type="checkbox" />').attr("id",aj).attr("name",aj).attr("checked",au))}else{if(af.type==="text"){aw.append($('<input type="text"/>').attr("id",aj).val(au).click(function(){$(this).select()}))}else{if(af.type==="select"){var ar=$("<select />").attr("id",aj);for(var an=0;an<af.options.length;an++){$("<option/>").text(af.options[an].label).attr("value",af.options[an].value).appendTo(ar)}ar.val(au);aw.append(ar)}else{if(af.type==="color"){var av=$("<div/>").appendTo(aw),aq=$("<input />").attr("id",aj).attr("name",aj).val(au).css("float","left").appendTo(av).click(function(ay){$(".tooltip").removeClass("in");var ax=$(this).siblings(".tooltip").addClass("in");ax.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(ax).height()/2)+($(this).height()/2)}).show();ax.click(function(az){az.stopPropagation()});$(document).bind("click.color-picker",function(){ax.hide();$(document).unbind("click.color-picker")});ay.stopPropagation()}),ao=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(av).attr("title","Set new random color").tooltip(),at=$("<div class='tooltip right' style='position: absolute;' />").appendTo(av).hide(),ak=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(at),ai=$("<div class='tooltip-arrow'></div>").appendTo(at),am=$.farbtastic(ak,{width:100,height:100,callback:aq,color:au});av.append($("<div/>").css("clear","both"));(function(ax){ao.click(function(){ax.setColor(j.get_random_color())})})(am)}else{aw.append($("<input />").attr("id",aj).attr("name",aj).val(au))}}}}if(af.help){aw.append($("<div class='help'/>").text(af.help))}}}ae(this.params,ad);return ad},update_from_form:function(ad){var af=this;var ae=false;$.each(this.params,function(ag,ai){if(!ai.hidden){var aj="param_"+ag;var ah=ad.find("#"+aj).val();if(ai.type==="bool"){ah=ad.find("#"+aj).is(":checked")}ae=af.set_param_value(ai.key,ah)||ae}});if(ae){this.onchange();this.track.changed()}}});var b=function(ad,ag,ah,ae,af){this.track=ad;this.region=ag;this.low=ag.get("start");this.high=ag.get("end");this.w_scale=ah;this.html_elt=$("<div class='track-tile'/>").append(ae).height($(ae).attr("height"));this.data=af;this.stale=false};b.prototype.predisplay_actions=function(){};var M=function(ad,ag,ah,ae,af){b.call(this,ad,ag,ah,ae,af)};M.prototype.predisplay_actions=function(){};var O=function(ag,an,ap,af,ai,aj,aq,ae,am){b.call(this,ag,an,ap,af,ai);this.mode=aj;this.all_slotted=ae;this.feature_mapper=am;this.has_icons=false;if(aq){this.has_icons=true;var ak=this;af=this.html_elt.children()[0],message_div=$("<div/>").addClass("tile-message").css({height:D-1,width:af.width}).prependTo(this.html_elt);var al=new w.GenomeRegion({chrom:ag.view.chrom,start:this.low,end:this.high}),ao=ai.length,ah=$("<a href='javascript:void(0);'/>").addClass("icon more-down").attr("title","For speed, only the first "+ao+" features in this region were obtained from server. Click to get more data including depth").tooltip().appendTo(message_div),ad=$("<a href='javascript:void(0);'/>").addClass("icon more-across").attr("title","For speed, only the first "+ao+" features in this region were obtained from server. Click to get more data excluding depth").tooltip().appendTo(message_div);ah.click(function(){ak.stale=true;ag.data_manager.get_more_data(al,ag.mode,1/ak.w_scale,{},ag.data_manager.DEEP_DATA_REQ);$(".tooltip").hide();ag.request_draw()}).dblclick(function(ar){ar.stopPropagation()});ad.click(function(){ak.stale=true;ag.data_manager.get_more_data(al,ag.mode,1/ak.w_scale,{},ag.data_manager.BROAD_DATA_REQ);$(".tooltip").hide();ag.request_draw()}).dblclick(function(ar){ar.stopPropagation()})}};o(O.prototype,b.prototype);O.prototype.predisplay_actions=function(){var ae=this,ad={};if(ae.mode!=="Pack"){return}$(this.html_elt).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()}).mousemove(function(ap){if(!this.hovered){return}var ak=$(this).offset(),ao=ap.pageX-ak.left,an=ap.pageY-ak.top,au=ae.feature_mapper.get_feature_data(ao,an),al=(au?au[0]:null);$(this).parents(".track-content").children(".overlay").children(".feature-popup").each(function(){if(!al||$(this).attr("id")!==al.toString()){$(this).remove()}});if(au){var ag=ad[al];if(!ag){var al=au[0],aq={name:au[3],start:au[1],end:au[2],strand:au[4]},aj=ae.track.filters_manager.filters,ai;for(var am=0;am<aj.length;am++){ai=aj[am];aq[ai.name]=au[ai.index]}var ag=$("<div/>").attr("id",al).addClass("feature-popup"),av=$("<table/>"),at,ar,aw;for(at in aq){ar=aq[at];aw=$("<tr/>").appendTo(av);$("<th/>").appendTo(aw).text(at);$("<td/>").attr("align","left").appendTo(aw).text(typeof(ar)==="number"?V(ar,2):ar)}ag.append($("<div class='feature-popup-inner'>").append(av));ad[al]=ag}ag.appendTo($(this).parents(".track-content").children(".overlay"));var ah=ao+parseInt(ae.html_elt.css("left"))-ag.width()/2,af=an+parseInt(ae.html_elt.css("top"))+7;ag.css("left",ah+"px").css("top",af+"px")}else{if(!ap.isPropagationStopped()){ap.stopPropagation();$(this).siblings().each(function(){$(this).trigger(ap)})}}}).mouseleave(function(){$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()})};var f=function(ae,ad,af){o(af,{drag_handle_class:"draghandle"});p.call(this,ae,ad,af);this.dataset=null;if(af.dataset){this.dataset=(af.dataset instanceof Backbone.Model?af.dataset:X.Dataset.findOrCreate(af.dataset))}this.dataset_check_type="converted_datasets_state";this.data_url_extra_params={};this.data_query_wait=("data_query_wait" in af?af.data_query_wait:K);this.data_manager=("data_manager" in af?af.data_manager:new w.GenomeDataManager({dataset:this.dataset,genome:new w.Genome({key:ae.dbkey,chroms_info:{chrom_info:ae.chrom_data}}),data_mode_compatible:this.data_and_mode_compatible,can_subset:this.can_subset}));this.min_height_px=16;this.max_height_px=800;this.visible_height_px=0;this.content_div=$("<div class='track-content'>").appendTo(this.container_div);if(this.container){this.container.content_div.append(this.container_div);if(!("resize" in af)||af.resize){this.add_resize_handle()}}};o(f.prototype,p.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},p.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"overview-icon",on_click_fn:function(ad){ad.view.set_overview(ad)}},p.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(ad){if(ad.filters_manager.visible()){ad.filters_manager.clear_filters()}else{ad.filters_manager.init_filters()}ad.filters_manager.toggle()}},{name:"tools_icon",title:"Tool",css_class:"hammer",on_click_fn:function(ad){ad.tool.toggle();if(ad.tool.is_visible()){ad.set_name(ad.name+ad.tool_region_and_parameters_str())}else{ad.revert_name()}$(".tooltip").remove()}},{name:"param_space_viz_icon",title:"Tool parameter space visualization",css_class:"arrow-split",on_click_fn:function(ad){var ag='<strong>Tool</strong>: <%= track.tool.name %><br/><strong>Dataset</strong>: <%= track.name %><br/><strong>Region(s)</strong>: <select name="regions"><option value="cur">current viewing area</option><option value="bookmarks">bookmarks</option><option value="both">current viewing area and bookmarks</option></select>',af=ac.template(ag,{track:ad});var ai=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},ae=function(){var ak=$('select[name="regions"] option:selected').val(),am,aj=new w.GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),al=ac.map($(".bookmark"),function(an){return new w.GenomeRegion({from_str:$(an).children(".position").text()})});if(ak==="cur"){am=[aj]}else{if(ak==="bookmarks"){am=al}else{am=[aj].concat(al)}}Galaxy.modal.hide();window.location.href=galaxy_config.root+"visualization/sweepster?"+$.param({dataset_id:ad.dataset.id,hda_ldda:ad.dataset.get("hda_ldda"),regions:JSON.stringify(new Backbone.Collection(am).toJSON())})},ah=function(aj){if((aj.keyCode||aj.which)===27){ai()}else{if((aj.keyCode||aj.which)===13){ae()}}};Galaxy.modal.show({title:"Visualize tool parameter space and output from different parameter settings?",body:af,buttons:{No:ai,Yes:ae}})}},p.prototype.action_icons_def[2]],can_draw:function(){return this.dataset&&p.prototype.can_draw.call(this)},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id).css("position","relative")},build_header_div:function(){var ad=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(ad)}this.name_div=$("<div/>").addClass("track-name").appendTo(ad).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return ad},set_dataset:function(ad){this.dataset=ad;this.data_manager.set("dataset",ad)},on_resize:function(){this.request_draw({clear_tile_cache:true})},add_resize_handle:function(){var ad=this;var ag=false;var af=false;var ae=$("<div class='track-resize'>");$(ad.container_div).hover(function(){if(ad.content_visible){ag=true;ae.show()}},function(){ag=false;if(!af){ae.hide()}});ae.hide().bind("dragstart",function(ah,ai){af=true;ai.original_height=$(ad.content_div).height()}).bind("drag",function(ai,aj){var ah=Math.min(Math.max(aj.original_height+aj.deltaY,ad.min_height_px),ad.max_height_px);$(ad.tiles_div).css("height",ah);ad.visible_height_px=(ad.max_height_px===ah?0:ah);ad.on_resize()}).bind("dragend",function(ah,ai){ad.tile_cache.clear();af=false;if(!ag){ae.hide()}ad.config.values.height=ad.visible_height_px;ad.changed()}).appendTo(ad.container_div)},set_display_modes:function(ag,aj){this.display_modes=ag;this.mode=(aj?aj:(this.config&&this.config.values.mode?this.config.values.mode:this.display_modes[0]));this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var ae=this,ah={};for(var af=0,ad=ae.display_modes.length;af<ad;af++){var ai=ae.display_modes[af];ah[ai]=function(ak){return function(){ae.change_mode(ak);ae.icons_div.show();ae.container_div.mouseleave(function(){ae.icons_div.hide()})}}(ai)}make_popupmenu(this.action_icons.mode_icon,ah)},build_action_icons:function(){p.prototype.build_action_icons.call(this,this.action_icons_def);if(this.display_modes!==undefined){this.set_display_modes(this.display_modes)}},hide_contents:function(){this.tiles_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.tiles_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof W){return"LabelTrack"}else{if(this instanceof B){return"ReferenceTrack"}else{if(this instanceof g){return"LineTrack"}else{if(this instanceof T){return"ReadTrack"}else{if(this instanceof Z){return"VariantTrack"}else{if(this instanceof e){return"CompositeTrack"}else{if(this instanceof c){return"FeatureTrack"}}}}}}}return""},init:function(af){var ae=this;ae.enabled=false;ae.tile_cache.clear();ae.data_manager.clear();ae.tiles_div.css("height","auto");ae.tiles_div.text("").children().remove();ae.container_div.removeClass("nodata error pending");if(!ae.dataset.id){return}var ad=$.Deferred(),ag={hda_ldda:ae.dataset.get("hda_ldda"),data_type:this.dataset_check_type,chrom:ae.view.chrom,retry:af};$.getJSON(this.dataset.url(),ag,function(ah){if(!ah||ah==="error"||ah.kind==="error"){ae.container_div.addClass("error");ae.tiles_div.text(m);if(ah.message){ae.tiles_div.append($("<a href='javascript:void(0);'></a>").text("View error").click(function(){Galaxy.modal.show({title:"Trackster Error",body:"<pre>"+ah.message+"</pre>",buttons:{Close:function(){Galaxy.modal.hide()}}})}));ae.tiles_div.append($("<span/>").text(" "));ae.tiles_div.append($("<a href='javascript:void(0);'></a>").text("Try again").click(function(){ae.init(true)}))}}else{if(ah==="no converter"){ae.container_div.addClass("error");ae.tiles_div.text(J)}else{if(ah==="no data"||(ah.data!==undefined&&(ah.data===null||ah.data.length===0))){ae.container_div.addClass("nodata");ae.tiles_div.text(E)}else{if(ah==="pending"){ae.container_div.addClass("pending");ae.tiles_div.html(u);setTimeout(function(){ae.init()},ae.data_query_wait)}else{if(ah==="data"||ah.status==="data"){if(ah.valid_chroms){ae.valid_chroms=ah.valid_chroms;ae.update_icons()}ae.tiles_div.text(U);if(ae.view.chrom){ae.tiles_div.text("");ae.tiles_div.css("height",ae.visible_height_px+"px");ae.enabled=true;$.when.apply($,ae.predraw_init()).done(function(){ad.resolve();ae.container_div.removeClass("nodata error pending");ae.request_draw()})}else{ad.resolve()}}}}}}});this.update_icons();return ad},predraw_init:function(){var ad=this;return $.getJSON(ad.dataset.url(),{data_type:"data",stats:true,chrom:ad.view.chrom,low:0,high:ad.view.max_high,hda_ldda:ad.dataset.get("hda_ldda")},function(ae){ad.container_div.addClass("line-track");var ag=ae.data;if(ag&&ag.min&&ag.max){var af=ag.min,ah=ag.max;af=Math.floor(Math.min(0,Math.max(af,ag.mean-2*ag.sd)));ah=Math.ceil(Math.max(0,Math.min(ah,ag.mean+2*ag.sd)));ad.config.set_param_default_value("min_value",af);ad.config.set_param_default_value("max_value",ah);ad.prefs.min_value=af;ad.prefs.max_value=ah}})},get_drawables:function(){return this}});var N=function(af,ae,ah){f.call(this,af,ae,ah);var ad=this;k(ad.container_div,ad.drag_handle_class,".group",ad);this.filters_manager=new h.FiltersManager(this,("filters" in ah?ah.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=(ah.tool?new q(ac.extend(ah.tool,{track:this,tool_state:ah.tool_state})):null);this.tile_cache=new w.Cache(Q);this.left_offset=0;if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){var ag=new ab({model:this.tool});ag.render();this.dynamic_tool_div=ag.$el;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(ah.mode){this.change_mode(ah.mode)}};o(N.prototype,p.prototype,f.prototype,{action_icons_def:f.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(ad){$(".tooltip").remove();ad.slotters[ad.view.resolution_px_b].max_rows*=2;ad.request_draw({clear_tile_cache:true})},hide:true}]),copy:function(ad){var ae=this.to_dict();o(ae,{data_manager:this.data_manager});var af=new this.constructor(this.view,ad,ae);af.change_mode(this.mode);af.enabled=this.enabled;return af},set_filters_manager:function(ad){this.filters_manager=ad;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),name:this.name,dataset:{id:this.dataset.id,hda_ldda:this.dataset.get("hda_ldda")},prefs:this.prefs,mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},set_min_max:function(){var ad=this;return $.getJSON(ad.dataset.url(),{data_type:"data",stats:true,chrom:ad.view.chrom,low:0,high:ad.view.max_high,hda_ldda:ad.dataset.get("hda_ldda")},function(ae){var ag=ae.data;if(isNaN(parseFloat(ad.prefs.min_value))||isNaN(parseFloat(ad.prefs.max_value))){var af=ag.min,ah=ag.max;af=Math.floor(Math.min(0,Math.max(af,ag.mean-2*ag.sd)));ah=Math.ceil(Math.max(0,Math.min(ah,ag.mean+2*ag.sd)));ad.prefs.min_value=af;ad.prefs.max_value=ah}})},change_mode:function(ae){var ad=this;ad.mode=ae;ad.config.values.mode=ae;if(ae==="Auto"){this.data_manager.clear()}ad.request_draw({clear_tile_cache:true});this.action_icons.mode_icon.attr("title","Set display mode (now: "+ad.mode+")");return ad},update_icons:function(){var ad=this;if(ad.filters_available){ad.action_icons.filters_icon.show()}else{ad.action_icons.filters_icon.hide()}if(ad.tool){ad.action_icons.tools_icon.show();ad.action_icons.param_space_viz_icon.show()}else{ad.action_icons.tools_icon.hide();ad.action_icons.param_space_viz_icon.hide()}},_gen_tile_cache_key:function(ae,ad){return ae+"_"+ad},request_draw:function(ad){if(ad&&ad.clear_tile_cache){this.tile_cache.clear()}this.view.request_redraw(ad,this)},before_draw:function(){this.max_height_px=0},_draw:function(ar){if(!this.can_draw()){return}var ao=ar&&ar.clear_after,am=this.view.low,ah=this.view.high,ak=ah-am,ae=this.view.container.width(),aq=this.view.resolution_px_b,ag=1/aq;if(this.is_overview){am=this.view.max_low;ah=this.view.max_high;aq=ae/(view.max_high-view.max_low);ag=1/aq}this.before_draw();this.tiles_div.children().addClass("remove");var ad=Math.floor(am/(ag*S)),al,an,ai,aj=[],ap=[];while((ad*S*ag)<ah){al=Math.floor(ad*S*ag);an=new w.GenomeRegion({chrom:this.view.chrom,start:al,end:Math.min(al+Math.ceil(S*ag),this.view.max_high)});ai=this.draw_helper(an,aq,ar);aj.push(ai);$.when(ai).then(function(at){ap.push(at)});ad+=1}if(!ao){this.tiles_div.children(".remove").removeClass("remove").remove()}var af=this;$.when.apply($,aj).then(function(){af.tiles_div.children(".remove").remove();ap=ac.filter(ap,function(at){return at!==null});if(ap.length!==0){af.postdraw_actions(ap,ae,aq,ao)}})},_add_yaxis_label:function(ag,ai){var ae=this,ah=(ag==="max"?"top":"bottom"),aj=(ag==="max"?"max":"min"),ad=(ag==="max"?"max_value":"min_value"),ai=ai||function(){ae.request_draw({clear_tile_cache:true})},af=this.container_div.find(".yaxislabel."+ah);if(af.length!==0){af.text(ae.prefs[ad])}else{af=$("<div/>").text(ae.prefs[ad]).make_text_editable({num_cols:12,on_finish:function(ak){$(".tooltip").remove();ae.config.set_param_value(ad,ak);ai()},help_text:"Set "+aj+" value"}).addClass("yaxislabel "+ah).css("color",this.prefs.label_color);this.container_div.prepend(af)}},postdraw_actions:function(ag,ah,aj,ad){var af=ac.filter(ag,function(ak){return(ak instanceof M)});if(af.length>0){this.max_height_px=0;var ae=this;ac.each(ag,function(ak){if(!(ak instanceof M)){ak.html_elt.remove();ae.draw_helper(ak.region,aj,{force:true,mode:"Coverage"})}});ae._add_yaxis_label("max")}else{this.container_div.find(".yaxislabel").remove();var ai=ac.find(ag,function(ak){return ak.has_icons});if(ai){ac.each(ag,function(ak){if(!ak.has_icons){ak.html_elt.css("padding-top",D)}})}}},get_mode:function(ad){return this.mode},update_auto_mode:function(ad){},_get_drawables:function(){return[this]},draw_helper:function(am,ap,aq){if(!aq){aq={}}var ae=aq.force,ao=!(aq.data_fetch===false),aj=aq.mode||this.mode,ag=1/ap,af=this,ah=this._get_drawables(),an=this._gen_tile_cache_key(ap,am),ai=function(ar){return(ar&&"track" in ar)};var ak=(ae?undefined:af.tile_cache.get_elt(an));if(ak){if(ai(ak)){af.show_tile(ak,ap)}return ak}if(!ao){return null}var al=function(){var ar=(ac.find(A,function(au){return au===aj})?"Coverage":aj);var at=ac.map(ah,function(au){return au.data_manager.get_data(am,ar,ag,af.data_url_extra_params)});if(view.reference_track){at.push(view.reference_track.data_manager.get_data(am,aj,ag,view.reference_track.data_url_extra_params))}return at};var ad=$.Deferred();af.tile_cache.set_elt(an,ad);$.when.apply($,al()).then(function(){var ar=al(),ax=ar,aD;if(view.reference_track){aD=view.reference_track.data_manager.subset_entry(ar.pop(),am)}var ay=[],av=[];ac.each(ah,function(aI,aF){var aH=aI.mode,aG=ax[aF];if(aH==="Auto"){aH=aI.get_mode(aG);aI.update_auto_mode(aH)}ay.push(aH);av.push(aI.get_canvas_height(aG,aH,ap,at))});var aw=af.view.canvas_manager.new_canvas(),az=am.get("start"),aE=am.get("end"),au=0,at=Math.ceil((aE-az)*ap)+af.left_offset,aB=ac.max(av),aA;aw.width=at;aw.height=(aq.height||aB);var aC=aw.getContext("2d");aC.translate(af.left_offset,0);if(ah.length>1){aC.globalAlpha=0.5;aC.globalCompositeOperation="source-over"}ac.each(ah,function(aG,aF){aA=aG.draw_tile(ax[aF],aC,ay[aF],am,ap,aD)});if(aA!==undefined){af.tile_cache.set_elt(an,aA);af.show_tile(aA,ap)}ad.resolve(aA)});return ad},get_canvas_height:function(ad,af,ag,ae){return this.visible_height_px},_draw_line_track_tile:function(ad,af,ai,ah,aj){var ag=af.canvas,ae=new L.LinePainter(ad.data,ah.get("start"),ah.get("end"),this.prefs,ai);ae.draw(af,ag.width,ag.height,aj);return new M(this,ah,aj,ag,ad.data)},draw_tile:function(ad,ae,ah,ag,ai,af){},show_tile:function(af,ai){var ae=this,ad=af.html_elt;af.predisplay_actions();var ah=(af.low-(this.is_overview?this.view.max_low:this.view.low))*ai;if(this.left_offset){ah-=this.left_offset}ad.css({position:"absolute",top:0,left:ah});if(ad.hasClass("remove")){ad.removeClass("remove")}else{this.tiles_div.append(ad)}af.html_elt.height("auto");this.max_height_px=Math.max(this.max_height_px,af.html_elt.height());af.html_elt.parent().children().css("height",this.max_height_px+"px");var ag=this.max_height_px;if(this.visible_height_px!==0){ag=Math.min(this.max_height_px,this.visible_height_px)}this.tiles_div.css("height",ag+"px")},tool_region_and_parameters_str:function(ag){var ad=this,af=(ag!==undefined?ag.toString():"all"),ae=ac.values(ad.tool.get_inputs_dict()).join(", ");return" - region=["+af+"], parameters=["+ae+"]"},data_and_mode_compatible:function(ad,ae){if(ae==="Auto"){return true}else{if(ae==="Coverage"){return ad.dataset_type==="bigwig"}else{if(ad.dataset_type==="bigwig"||ad.extra_info==="no_detail"){return false}else{return true}}}},can_subset:function(ad){if(ad.message||ad.extra_info==="no_detail"){return false}else{if(ad.dataset_type==="bigwig"){return(ad.data[1][0]-ad.data[0][0]===1)}}return true},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data");this.data_query_wait=1000;this.dataset_check_type="state"}});var W=function(ae,ad){var af={resize:false};f.call(this,ae,ad,af);this.container_div.addClass("label-track")};o(W.prototype,f.prototype,{build_header_div:function(){},init:function(){this.enabled=true},predraw_init:function(){},_draw:function(ah){var af=this.view,ag=af.high-af.low,ak=Math.floor(Math.pow(10,Math.floor(Math.log(ag)/Math.log(10)))),ad=Math.floor(af.low/ak)*ak,ai=this.view.container.width(),ae=$("<div style='position: relative; height: 1.3em;'></div>");while(ad<af.high){var aj=(ad-af.low)/ag*ai;ae.append($("<div class='label'>"+commatize(ad)+"</div>").css({position:"absolute",left:aj-1}));ad+=ak}this.content_div.children(":first").remove();this.content_div.append(ae)}});var e=function(ae,ad,ah){this.display_modes=A;N.call(this,ae,ad,ah);this.drawables=[];if("drawables" in ah){var ag;for(var af=0;af<ah.drawables.length;af++){ag=ah.drawables[af];this.drawables[af]=n(ag,ae,null);if(ag.left_offset>this.left_offset){this.left_offset=ag.left_offset}}this.enabled=true}ac.each(this.drawables,function(ai){if(ai instanceof c||ai instanceof T){ai.change_mode("Coverage")}});this.update_icons();this.obj_type="CompositeTrack"};o(e.prototype,N.prototype,{action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(ad){$(".tooltip").remove();ad.show_group()}}].concat(N.prototype.action_icons_def),to_dict:y.prototype.to_dict,add_drawable:y.prototype.add_drawable,unpack_drawables:y.prototype.unpack_drawables,change_mode:function(ad){N.prototype.change_mode.call(this,ad);for(var ae=0;ae<this.drawables.length;ae++){this.drawables[ae].change_mode(ad)}},init:function(){var af=[];for(var ae=0;ae<this.drawables.length;ae++){af.push(this.drawables[ae].init())}var ad=this;$.when.apply($,af).then(function(){ad.enabled=true;ad.request_draw()})},update_icons:function(){this.action_icons.filters_icon.hide();this.action_icons.tools_icon.hide();this.action_icons.param_space_viz_icon.hide()},can_draw:p.prototype.can_draw,_get_drawables:function(){return this.drawables},show_group:function(){var ag=new P(this.view,this.container,{name:this.name}),ad;for(var af=0;af<this.drawables.length;af++){ad=this.drawables[af];ad.update_icons();ag.add_drawable(ad);ad.container=ag;ag.content_div.append(ad.container_div)}var ae=this.container.replace_drawable(this,ag,true);ag.request_draw({clear_tile_cache:true})},before_draw:function(){N.prototype.before_draw.call(this);var ae=ac.min(ac.map(this.drawables,function(af){return af.prefs.min_value})),ad=ac.max(ac.map(this.drawables,function(af){return af.prefs.max_value}));this.prefs.min_value=ae;this.prefs.max_value=ad;ac.each(this.drawables,function(af){af.prefs.min_value=ae;af.prefs.max_value=ad})},update_all_min_max:function(){var ad=this;ac.each(this.drawables,function(ae){ae.prefs.min_value=ad.prefs.min_value;ae.prefs.max_value=ad.prefs.max_value});this.request_draw({clear_tile_cache:true})},postdraw_actions:function(aj,ad,am,ai){N.prototype.postdraw_actions.call(this,aj,ad,am,ai);var ah=-1;for(var af=0;af<aj.length;af++){var ak=aj[af].html_elt.find("canvas").height();if(ak>ah){ah=ak}}for(var af=0;af<aj.length;af++){var ag=aj[af];if(ag.html_elt.find("canvas").height()!==ah){this.draw_helper(ag.region,am,{force:true,height:ah});ag.html_elt.remove()}}var ae=this,al=function(){ae.update_all_min_max()};this._add_yaxis_label("min",al);this._add_yaxis_label("max",al)}});var B=function(ad){N.call(this,ad,{content_div:ad.top_labeltrack},{resize:false});ad.reference_track=this;this.left_offset=200;this.visible_height_px=12;this.container_div.addClass("reference-track");this.content_div.css("background","none");this.content_div.css("min-height","0px");this.content_div.css("border","none");this.data_url=galaxy_config.root+"api/genomes//"+this.view.dbkey;this.data_url_extra_params={reference:true};this.data_manager=new w.GenomeReferenceDataManager({data_url:this.data_url,can_subset:this.can_subset});this.hide_contents()};o(B.prototype,p.prototype,N.prototype,{build_header_div:function(){},init:function(){this.data_manager.clear();this.enabled=true},predraw_init:function(){},can_draw:p.prototype.can_draw,draw_helper:function(ae,af,ad){if(af>this.view.canvas_manager.char_width_px){this.tiles_div.show();return N.prototype.draw_helper.call(this,ae,af,ad)}else{this.tiles_div.hide();return null}},can_subset:function(ad){return true},draw_tile:function(af,al,ag,ai,am){var ae=this.data_manager.subset_entry(af,ai),ak=ae.data;var ad=al.canvas;al.font=al.canvas.manager.default_font;al.textAlign="center";for(var ah=0,aj=ak.length;ah<aj;ah++){al.fillStyle=this.view.get_base_color(ak[ah]);al.fillText(ak[ah],Math.floor(ah*am),10)}return new b(this,ai,am,ad,ae)}});var g=function(af,ae,ag){var ad=this;this.display_modes=A;this.mode="Histogram";N.call(this,af,ae,ag);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"color",label:"Color",type:"color",default_value:j.get_random_color()},{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true}],saved_values:ag.prefs,onchange:function(){ad.set_name(ad.prefs.name);ad.request_redraw({clear_tile_cache:true})}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height};o(g.prototype,p.prototype,N.prototype,{before_draw:function(){},draw_tile:function(ad,ae,ag,af,ah){return this._draw_line_track_tile(ad,ae,ag,af,ah)},can_subset:function(ad){return(ad.data[1][0]-ad.data[0][0]===1)},postdraw_actions:function(ae,af,ag,ad){this._add_yaxis_label("max");this._add_yaxis_label("min")}});var r=function(af,ae,ag){var ad=this;this.display_modes=["Heatmap"];this.mode="Heatmap";N.call(this,af,ae,ag);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"min_value",label:"Min Value",type:"float",default_value:-1},{key:"max_value",label:"Max Value",type:"float",default_value:1},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:500,hidden:true}],saved_values:ag.prefs,onchange:function(){ad.set_name(ad.prefs.name);this.request_redraw({clear_tile_cache:true})}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height};o(r.prototype,p.prototype,N.prototype,{draw_tile:function(ad,af,ai,ah,aj){var ag=af.canvas,ae=new L.DiagonalHeatmapPainter(ad.data,ah.get("start"),ah.get("end"),this.prefs,ai);ae.draw(af,ag.width,ag.height,aj);return new b(this,ah,aj,ag,ad.data)}});var c=function(ag,af,ai){var ae=this;this.display_modes=["Auto","Coverage","Dense","Squish","Pack"];N.call(this,ag,af,ai);var ah=j.get_random_color(),ad=j.get_random_color([ah,"#FFFFFF"]);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:ah},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:ad},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"connector_style",label:"Connector style",type:"select",default_value:"fishbones",options:[{label:"Line with arrows",value:"fishbone"},{label:"Arcs",value:"arcs"}]},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:this.visible_height_px,hidden:true}],saved_values:ai.prefs,onchange:function(){ae.set_name(ae.prefs.name);ae.set_painter_from_config();ae.request_draw({clear_tile_cache:true})}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.container_div.addClass("feature-track");this.show_labels_scale=0.001;this.showing_details=false;this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};o(c.prototype,p.prototype,N.prototype,{set_painter_from_config:function(){if(this.config.values.connector_style==="arcs"){this.painter=L.ArcLinkedFeaturePainter}else{this.painter=L.LinkedFeaturePainter}},postdraw_actions:function(an,af,ao,am){N.prototype.postdraw_actions.call(this,an,af,ao,am);var ah=this,ai;if(ah.filters_manager){var ag=ah.filters_manager.filters;for(var ak=0;ak<ag.length;ak++){ag[ak].update_ui_elt()}var aj=false,al,ae;for(ai=0;ai<an.length;ai++){if(an[ai].data.length){al=an[ai].data[0];for(var ak=0;ak<ag.length;ak++){ae=ag[ak];if(ae.applies_to(al)&&ae.min!==ae.max){aj=true;break}}}}if(ah.filters_available!==aj){ah.filters_available=aj;if(!ah.filters_available){ah.filters_manager.hide()}ah.update_icons()}}if(an[0] instanceof O){var ad=true;for(ai=0;ai<an.length;ai++){if(!an[ai].all_slotted){ad=false;break}}if(!ad){this.action_icons.show_more_rows_icon.show()}else{this.action_icons.show_more_rows_icon.hide()}}else{this.action_icons.show_more_rows_icon.hide()}},update_auto_mode:function(ad){var ad;if(this.mode==="Auto"){if(ad==="no_detail"){ad="feature spans"}this.action_icons.mode_icon.attr("title","Set display mode (now: Auto/"+ad+")")}},incremental_slots:function(ah,ad,ag){var ae=this.view.canvas_manager.dummy_context,af=this.slotters[ah];if(!af||(af.mode!==ag)){af=new (s.FeatureSlotter)(ah,ag,z,function(ai){return ae.measureText(ai)});this.slotters[ah]=af}return af.slot_features(ad)},get_mode:function(ad){if(ad.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>I){mode="Squish"}else{mode="Pack"}}return mode},get_canvas_height:function(ad,ah,ai,ae){if(ah==="Coverage"||ad.dataset_type==="bigwig"){return this.summary_draw_height}else{var ag=this.incremental_slots(ai,ad.data,ah);var af=new (this.painter)(null,null,null,this.prefs,ah);return Math.max(aa,af.get_required_height(ag,ae))}},draw_tile:function(an,ar,ap,ag,ak,af){var aq=this,ae=ar.canvas,ay=ag.get("start"),ad=ag.get("end"),ah=this.left_offset;if(an.dataset_type==="bigwig"){return this._draw_line_track_tile(an,ar,ap,ag,ak)}var aj=[],ao=this.slotters[ak].slots;all_slotted=true;if(an.data){var al=this.filters_manager.filters;for(var at=0,av=an.data.length;at<av;at++){var ai=an.data[at];var au=false;var am;for(var ax=0,aC=al.length;ax<aC;ax++){am=al[ax];am.update_attrs(ai);if(!am.keep(ai)){au=true;break}}if(!au){aj.push(ai);if(!(ai[0] in ao)){all_slotted=false}}}}var aB=(this.filters_manager.alpha_filter?new C(this.filters_manager.alpha_filter):null),az=(this.filters_manager.height_filter?new C(this.filters_manager.height_filter):null),aA=new (this.painter)(aj,ay,ad,this.prefs,ap,aB,az,af,function(aD){return aq.view.get_base_color(aD)});var aw=null;ar.fillStyle=this.prefs.block_color;ar.font=ar.canvas.manager.default_font;ar.textAlign="right";if(an.data){aw=aA.draw(ar,ae.width,ae.height,ak,ao);aw.translation=-ah}return new O(aq,ag,ak,ae,an.data,ap,an.message,all_slotted,aw)}});var Z=function(ae,ad,af){this.display_modes=["Auto","Coverage","Dense","Squish","Pack"];N.call(this,ae,ad,af);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"color",label:"Histogram color",type:"color",default_value:j.get_random_color()},{key:"show_sample_data",label:"Show sample data",type:"bool",default_value:true},{key:"show_labels",label:"Show summary and sample labels",type:"bool",default_value:true},{key:"summary_height",label:"Locus summary height",type:"float",default_value:20},{key:"mode",type:"string",default_value:this.mode,hidden:true}],saved_values:af.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.request_draw({clear_tile_cache:true})}});this.prefs=this.config.values;this.painter=L.VariantPainter;this.summary_draw_height=30;this.left_offset=30};o(Z.prototype,p.prototype,N.prototype,{draw_tile:function(ad,ag,ai,ah,aj){if(ad.dataset_type==="bigwig"){return this._draw_line_track_tile(ad,ag,"Histogram",ah,aj)}else{var af=this.view,ae=new (this.painter)(ad.data,ah.get("start"),ah.get("end"),this.prefs,ai,function(ak){return af.get_base_color(ak)});ae.draw(ag,ag.canvas.width,ag.canvas.height,aj);return new b(this,ah,aj,ag.canvas,ad.data)}},get_canvas_height:function(ad,ah,ai,af){if(ad.dataset_type==="bigwig"){return this.summary_draw_height}else{var ag=new (this.painter)(null,null,null,this.prefs,ah);var ae=(this.dataset.get_metadata("sample_names")?this.dataset.get_metadata("sample_names").length:0);if(ae===0&&ad.data.length!==0){ae=ad.data[0][7].match(/,/g);if(ae===null){ae=1}else{ae=ae.length+1}}return ag.get_required_height(ae)}},predraw_init:function(){var ad=[f.prototype.predraw_init.call(this)];if(!this.dataset.get_metadata("sample_names")){ad.push(this.dataset.fetch())}return ad},postdraw_actions:function(ag,ah,aj,ae){N.prototype.postdraw_actions.call(this,ag,ah,aj,ae);var af=ac.filter(ag,function(ak){return(ak instanceof M)});if(af.length===0&&this.prefs.show_labels){var ad;if(this.container_div.find(".yaxislabel.variant").length===0){ad=this.prefs.summary_height/2;this.tiles_div.prepend($("<div/>").text("Summary").addClass("yaxislabel variant top").css({"font-size":ad+"px",top:(this.prefs.summary_height-ad)/2+"px"}));if(this.prefs.show_sample_data){var ai=this.dataset.get("metadata").get("sample_names").join("<br/>");this.tiles_div.prepend($("<div/>").html(ai).addClass("yaxislabel variant top sample").css({top:this.prefs.summary_height+2,}))}}ad=(this.mode==="Squish"?5:10)+"px";$(this.tiles_div).find(".sample").css({"font-size":ad,"line-height":ad});$(this.tiles_div).find(".yaxislabel").css("color",this.prefs.label_color)}else{this.container_div.find(".yaxislabel.variant").remove()}}});var T=function(af,ae,ah){c.call(this,af,ae,ah);var ag=j.get_random_color(),ad=j.get_random_color([ag,"#ffffff"]);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block and sense strand color",type:"color",default_value:ag},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:ad},{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},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"}],saved_values:ah.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.request_draw({clear_tile_cache:true})}});this.prefs=this.config.values;this.painter=(af.reference_track?L.RefBasedReadPainter:L.ReadPainter);this.update_icons()};o(T.prototype,p.prototype,N.prototype,c.prototype);var d={CompositeTrack:e,DrawableGroup:P,DiagonalHeatmapTrack:r,FeatureTrack:c,LineTrack:g,ReadTrack:T,VariantTrack:Z,VcfTrack:Z};var n=function(af,ae,ad){if("copy" in af){return af.copy(ad)}else{var ag=af.obj_type;if(!ag){ag=af.track_type}return new d[ag](ae,ad,af)}};return{TracksterView:Y,DrawableGroup:P,LineTrack:g,FeatureTrack:c,DiagonalHeatmapTrack:r,ReadTrack:T,VariantTrack:Z,CompositeTrack:e,object_from_template:n}});
\ No newline at end of file
+define(["libs/underscore","viz/visualization","viz/trackster/util","viz/trackster/slotting","viz/trackster/painters","viz/trackster/filters","mvc/data","mvc/tools"],function(ac,w,j,s,L,h,X,R){var o=ac.extend;var l={};var i=function(ad,ae){l[ad.attr("id")]=ae};var k=function(ad,af,ah,ag){ah=".group";var ae={};l[ad.attr("id")]=ag;ad.bind("drag",{handle:"."+af,relative:true},function(ap,aq){var ao=$(this),au=$(this).parent(),al=au.children(),an=l[$(this).attr("id")],ak,aj,ar,ai,am;aj=$(this).parents(ah);if(aj.length!==0){ar=aj.position().top;ai=ar+aj.outerHeight();if(aq.offsetY<ar){$(this).insertBefore(aj);var at=l[aj.attr("id")];at.remove_drawable(an);at.container.add_drawable_before(an,at);return}else{if(aq.offsetY>ai){$(this).insertAfter(aj);var at=l[aj.attr("id")];at.remove_drawable(an);at.container.add_drawable(an);return}}}aj=null;for(am=0;am<al.length;am++){ak=$(al.get(am));ar=ak.position().top;ai=ar+ak.outerHeight();if(ak.is(ah)&&this!==ak.get(0)&&aq.offsetY>=ar&&aq.offsetY<=ai){if(aq.offsetY-ar<ai-aq.offsetY){ak.find(".content-div").prepend(this)}else{ak.find(".content-div").append(this)}if(an.container){an.container.remove_drawable(an)}l[ak.attr("id")].add_drawable(an);return}}for(am=0;am<al.length;am++){ak=$(al.get(am));if(aq.offsetY<ak.position().top&&!(ak.hasClass("reference-track")||ak.hasClass("intro"))){break}}if(am===al.length){if(this!==al.get(am-1)){au.append(this);l[au.attr("id")].move_drawable(an,am)}}else{if(this!==al.get(am)){$(this).insertBefore(al.get(am));l[au.attr("id")].move_drawable(an,(aq.deltaY>0?am-1:am))}}}).bind("dragstart",function(){ae["border-top"]=ad.css("border-top");ae["border-bottom"]=ad.css("border-bottom");$(this).css({"border-top":"1px solid blue","border-bottom":"1px solid blue"})}).bind("dragend",function(){$(this).css(ae)})};var aa=16,G=9,D=20,z=100,I=12000,S=400,K=5000,v=100,m="Cannot display dataset due to an error. ",J="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",E="No data for this chrom/contig.",u="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",x="Tool cannot be rerun: ",a="Loading data...",U="Ready for display",Q=10,H=20,A=["Histogram","Line","Filled","Intensity"];function V(ae,ad){if(!ad){ad=0}var af=Math.pow(10,ad);return Math.round(ae*af)/af}var p=function(ae,ad,ag){if(!p.id_counter){p.id_counter=0}this.id=p.id_counter++;this.name=ag.name;this.view=ae;this.container=ad;this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name}],saved_values:ag.prefs,onchange:function(){this.track.set_name(this.track.config.values.name)}});this.prefs=this.config.values;this.drag_handle_class=ag.drag_handle_class;this.is_overview=false;this.action_icons={};this.content_visible=true;this.container_div=this.build_container_div();this.header_div=this.build_header_div();if(this.header_div){this.container_div.append(this.header_div);this.icons_div=$("<div/>").css("float","left").hide().appendTo(this.header_div);this.build_action_icons(this.action_icons_def);this.header_div.append($("<div style='clear: both'/>"));this.header_div.dblclick(function(ah){ah.stopPropagation()});var af=this;this.container_div.hover(function(){af.icons_div.show()},function(){af.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};p.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(ad){if(ad.content_visible){ad.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");ad.hide_contents();ad.content_visible=false}else{ad.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");ad.content_visible=true;ad.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"settings-icon",on_click_fn:function(ae){var ag=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},ad=function(){ae.config.update_from_form($(Galaxy.modal.el));Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},af=function(ah){if((ah.keyCode||ah.which)===27){ag()}else{if((ah.keyCode||ah.which)===13){ad()}}};$(window).bind("keypress.check_enter_esc",af);Galaxy.modal.show({title:"Configure",body:ae.config.build_form(),buttons:{Cancel:ag,Ok:ad}})}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(ad){$(".tooltip").remove();ad.remove()}}];o(p.prototype,{init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.content_visible){return true}return false},request_draw:function(){},_draw:function(ad){},to_dict:function(){},set_name:function(ad){this.old_name=this.name;this.name=ad;this.name_div.text(this.name)},revert_name:function(){if(this.old_name){this.name=this.old_name;this.name_div.text(this.name)}},remove:function(){this.changed();this.container.remove_drawable(this);var ad=this.view;this.container_div.hide(0,function(){$(this).remove();ad.update_intro_div()})},build_container_div:function(){},build_header_div:function(){},add_action_icon:function(ae,aj,ai,ah,ad,ag){var af=this;this.action_icons[ae]=$("<a/>").attr("href","javascript:void(0);").attr("title",aj).addClass("icon-button").addClass(ai).tooltip().click(function(){ah(af)}).appendTo(this.icons_div);if(ag){this.action_icons[ae].hide()}},build_action_icons:function(ad){var af;for(var ae=0;ae<ad.length;ae++){af=ad[ae];this.add_action_icon(af.name,af.title,af.css_class,af.on_click_fn,af.prepend,af.hide)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){},get_drawables:function(){}});var y=function(ae,ad,af){p.call(this,ae,ad,af);this.obj_type=af.obj_type;this.drawables=[]};o(y.prototype,p.prototype,{unpack_drawables:function(af){this.drawables=[];var ae;for(var ad=0;ad<af.length;ad++){ae=n(af[ad],this.view,this);this.add_drawable(ae)}},init:function(){for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad].init()}},_draw:function(ad){for(var ae=0;ae<this.drawables.length;ae++){this.drawables[ae]._draw(ad)}},to_dict:function(){var ae=[];for(var ad=0;ad<this.drawables.length;ad++){ae.push(this.drawables[ad].to_dict())}return{name:this.name,prefs:this.prefs,obj_type:this.obj_type,drawables:ae}},add_drawable:function(ad){this.drawables.push(ad);ad.container=this;this.changed()},add_drawable_before:function(af,ad){this.changed();var ae=this.drawables.indexOf(ad);if(ae!==-1){this.drawables.splice(ae,0,af);return true}return false},replace_drawable:function(af,ad,ae){var ag=this.drawables.indexOf(af);if(ag!==-1){this.drawables[ag]=ad;if(ae){af.container_div.replaceWith(ad.container_div)}this.changed()}return ag},remove_drawable:function(ae){var ad=this.drawables.indexOf(ae);if(ad!==-1){this.drawables.splice(ad,1);ae.container=null;this.changed();return true}return false},move_drawable:function(ae,af){var ad=this.drawables.indexOf(ae);if(ad!==-1){this.drawables.splice(ad,1);this.drawables.splice(af,0,ae);this.changed();return true}return false},get_drawables:function(){return this.drawables},get_tracks:function(ag){var ad=this.drawables.slice(0),ae=[],af;while(ad.length!==0){af=ad.shift();if(af instanceof ag){ae.push(af)}else{if(af.drawables){ad=ad.concat(af.drawables)}}}return ae}});var P=function(ae,ad,ag){o(ag,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});y.call(this,ae,ad,ag);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);i(this.container_div,this);i(this.content_div,this);k(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new h.FiltersManager(this);this.header_div.after(this.filters_manager.parent_div);this.saved_filters_managers=[];if("drawables" in ag){this.unpack_drawables(ag.drawables)}if("filters" in ag){var af=this.filters_manager;this.filters_manager=new h.FiltersManager(this,ag.filters);af.parent_div.replaceWith(this.filters_manager.parent_div);if(ag.filters.visible){this.setup_multitrack_filtering()}}};o(P.prototype,p.prototype,y.prototype,{action_icons_def:[p.prototype.action_icons_def[0],p.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(ad){$(".tooltip").remove();ad.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(ad){if(ad.filters_manager.visible()){ad.filters_manager.clear_filters();ad._restore_filter_managers()}else{ad.setup_multitrack_filtering();ad.request_draw({clear_tile_cache:true})}ad.filters_manager.toggle()}},p.prototype.action_icons_def[2]],build_container_div:function(){var ad=$("<div/>").addClass("group").attr("id","group_"+this.id);if(this.container){this.container.content_div.append(ad)}return ad},build_header_div:function(){var ad=$("<div/>").addClass("track-header");ad.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.name).appendTo(ad);return ad},hide_contents:function(){this.tiles_div.hide()},show_contents:function(){this.tiles_div.show();this.request_draw()},update_icons:function(){var af=this.drawables.length;if(af===0){this.action_icons.composite_icon.hide();this.action_icons.filters_icon.hide()}else{if(af===1){if(this.drawables[0] instanceof e){this.action_icons.composite_icon.show()}this.action_icons.filters_icon.hide()}else{var am,al,aj,ap=true,ah=this.drawables[0].get_type(),ad=0;for(am=0;am<af;am++){aj=this.drawables[am];if(aj.get_type()!==ah){can_composite=false;break}if(aj instanceof c){ad++}}if(ap||ad===1){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".tooltip").remove()}if(ad>1&&ad===this.drawables.length){var aq={},ae;aj=this.drawables[0];for(al=0;al<aj.filters_manager.filters.length;al++){ae=aj.filters_manager.filters[al];aq[ae.name]=[ae]}for(am=1;am<this.drawables.length;am++){aj=this.drawables[am];for(al=0;al<aj.filters_manager.filters.length;al++){ae=aj.filters_manager.filters[al];if(ae.name in aq){aq[ae.name].push(ae)}}}this.filters_manager.remove_all();var ag,ai,ak,an;for(var ao in aq){ag=aq[ao];if(ag.length===ad){ai=new h.NumberFilter({name:ag[0].name,index:ag[0].index});this.filters_manager.add_filter(ai)}}if(this.filters_manager.filters.length>0){this.action_icons.filters_icon.show()}else{this.action_icons.filters_icon.hide()}}else{this.action_icons.filters_icon.hide()}}}},_restore_filter_managers:function(){for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad].filters_manager=this.saved_filters_managers[ad]}this.saved_filters_managers=[]},setup_multitrack_filtering:function(){if(this.filters_manager.filters.length>0){this.saved_filters_managers=[];for(var ad=0;ad<this.drawables.length;ad++){drawable=this.drawables[ad];this.saved_filters_managers.push(drawable.filters_manager);drawable.filters_manager=this.filters_manager}}this.filters_manager.init_filters()},show_composite_track:function(){var ae=new e(this.view,this.view,{name:this.name,drawables:this.drawables});var ad=this.container.replace_drawable(this,ae,true);ae.request_draw()},add_drawable:function(ad){y.prototype.add_drawable.call(this,ad);this.update_icons()},remove_drawable:function(ad){y.prototype.remove_drawable.call(this,ad);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var ad=o(y.prototype.to_dict.call(this),{filters:this.filters_manager.to_dict()});if(this.filters_manager.visible()){this.setup_multitrack_filtering()}return ad},request_draw:function(ad){ac.each(this.drawables,function(ae){ae.request_draw(ad)})}});var Y=Backbone.View.extend({initialize:function(ad){o(ad,{obj_type:"View"});y.call(this,"View",ad.container,ad);this.chrom=null;this.vis_id=ad.vis_id;this.dbkey=ad.dbkey;this.label_tracks=[];this.tracks_to_be_redrawn=[];this.max_low=0;this.max_high=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.load_chroms_deferred=null;this.render();this.canvas_manager=new w.CanvasManager(this.container.get(0).ownerDocument);this.reset();this.config=new F({track:this,params:[{key:"a_color",label:"A Color",type:"color",default_value:"#FF0000"},{key:"c_color",label:"C Color",type:"color",default_value:"#00FF00"},{key:"g_color",label:"G Color",type:"color",default_value:"#0000FF"},{key:"t_color",label:"T Color",type:"color",default_value:"#FF00FF"},{key:"n_color",label:"N Color",type:"color",default_value:"#AAAAAA"}],saved_values:ad.prefs,onchange:function(){track.request_redraw({clear_tile_cache:true})}})},render:function(){this.requested_redraw=false;var af=this.container,ad=this;this.top_container=$("<div/>").addClass("top-container").appendTo(af);this.browser_content_div=$("<div/>").addClass("content").css("position","relative").appendTo(af);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(af);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("<div/>").addClass("viewport-container").attr("id","viewport-container").appendTo(this.browser_content_div);this.content_div=this.viewport_container;i(this.viewport_container,ad);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var ag=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){w.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":ad.dbkey},function(ah){ac.each(ah,function(ai){ad.add_drawable(n(ai,ad,ad))})})});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("trackster-nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("trackster-nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("<a/>").attr("href","javascript:void(0);").attr("title","Close overview").addClass("icon-button overview-close tooltip").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div/>").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var ae=function(ah){if(ah.type==="focusout"||(ah.keyCode||ah.which)===13||(ah.keyCode||ah.which)===27){if((ah.keyCode||ah.which)!==27){ad.go_to($(this).val())}$(this).hide();$(this).val("");ad.location_span.show();ad.chrom_select.show()}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ae).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("original-title","Click to change location").tooltip({placement:"bottom"}).appendTo(this.nav_controls);this.location_span.click(function(){ad.location_span.hide();ad.chrom_select.hide();ad.nav_input.val(ad.chrom+":"+ad.low+"-"+ad.high);ad.nav_input.css("display","inline-block");ad.nav_input.select();ad.nav_input.focus();ad.nav_input.autocomplete({source:function(aj,ah){var ak=[],ai=$.map(ad.get_tracks(c),function(al){return al.data_manager.search_features(aj.term).success(function(am){ak=ak.concat(am)})});$.when.apply($,ai).done(function(){ah($.map(ak,function(al){return{label:al[0],value:al[1]}}))})}})});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.nav_controls)}this.zo_link=$("<a/>").attr("id","zoom-out").attr("title","Zoom out").tooltip({placement:"bottom"}).click(function(){ad.zoom_out();ad.request_redraw()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tooltip({placement:"bottom"}).click(function(){ad.zoom_in();ad.request_redraw()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){ad.change_chrom(ad.chrom_select.val())});this.browser_content_div.click(function(ah){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(ah){ad.zoom_in(ah.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(ah,ai){this.current_x=ai.offsetX}).bind("drag",function(ah,aj){var ak=aj.offsetX-this.current_x;this.current_x=aj.offsetX;var ai=Math.round(ak/ad.viewport_container.width()*(ad.max_high-ad.max_low));ad.move_delta(-ai)});this.overview_close.click(function(){ad.reset_overview()});this.viewport_container.bind("draginit",function(ah,ai){if(ah.clientX>ad.viewport_container.width()-16){return false}}).bind("dragstart",function(ah,ai){ai.original_low=ad.low;ai.current_height=ah.clientY;ai.current_x=ai.offsetX}).bind("drag",function(aj,al){var ah=$(this);var am=al.offsetX-al.current_x;var ai=ah.scrollTop()-(aj.clientY-al.current_height);ah.scrollTop(ai);al.current_height=aj.clientY;al.current_x=al.offsetX;var ak=Math.round(am/ad.viewport_container.width()*(ad.high-ad.low));ad.move_delta(ak)}).bind("mousewheel",function(aj,al,ai,ah){if(ai){ai*=50;var ak=Math.round(-ai/ad.viewport_container.width()*(ad.high-ad.low));ad.move_delta(ak)}});this.top_labeltrack.bind("dragstart",function(ah,ai){return $("<div />").css({height:ad.browser_content_div.height()+ad.top_labeltrack.height()+ad.nav_labeltrack.height()+1,top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag",function(al,am){$(am.proxy).css({left:Math.min(al.pageX,am.startX)-ad.container.offset().left,width:Math.abs(al.pageX-am.startX)});var ai=Math.min(al.pageX,am.startX)-ad.container.offset().left,ah=Math.max(al.pageX,am.startX)-ad.container.offset().left,ak=(ad.high-ad.low),aj=ad.viewport_container.width();ad.update_location(Math.round(ai/aj*ak)+ad.low,Math.round(ah/aj*ak)+ad.low)}).bind("dragend",function(am,an){var ai=Math.min(am.pageX,an.startX),ah=Math.max(am.pageX,an.startX),ak=(ad.high-ad.low),aj=ad.viewport_container.width(),al=ad.low;ad.low=Math.round(ai/aj*ak)+al;ad.high=Math.round(ah/aj*ak)+al;$(an.proxy).remove();ad.request_redraw()});this.add_label_track(new W(this,{content_div:this.top_labeltrack}));this.add_label_track(new W(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){if(this.resize_timer){clearTimeout(this.resize_timer)}this.resize_timer=setTimeout(function(){ad.resize_window()},500)});$(document).bind("redraw",function(){ad.redraw()});this.reset();$(window).trigger("resize")},get_base_color:function(ad){return this.config.values[ad.toLowerCase()+"_color"]||this.config.values.n_color}});o(Y.prototype,y.prototype,{changed:function(){this.has_changes=true},update_intro_div:function(){if(this.drawables.length===0){this.intro_div.show()}else{this.intro_div.hide()}},trigger_navigate:function(ae,ag,ad,ah){if(this.timer){clearTimeout(this.timer)}if(ah){var af=this;this.timer=setTimeout(function(){af.trigger("navigate",ae+":"+ag+"-"+ad)},500)}else{view.trigger("navigate",ae+":"+ag+"-"+ad)}},update_location:function(ad,af){this.location_span.text(commatize(ad)+" - "+commatize(af));this.nav_input.val(this.chrom+":"+commatize(ad)+"-"+commatize(af));var ae=view.chrom_select.val();if(ae!==""){this.trigger_navigate(ae,view.low,view.high,true)}},load_chroms:function(af){af.num=v;var ad=this,ae=$.Deferred();$.ajax({url:galaxy_config.root+"api/genomes/"+this.dbkey,data:af,dataType:"json",success:function(ah){if(ah.chrom_info.length===0){return}if(ah.reference){ad.add_label_track(new B(ad))}ad.chrom_data=ah.chrom_info;var ak='<option value="">Select Chrom/Contig</option>';for(var aj=0,ag=ad.chrom_data.length;aj<ag;aj++){var ai=ad.chrom_data[aj].chrom;ak+='<option value="'+ai+'">'+ai+"</option>"}if(ah.prev_chroms){ak+='<option value="previous">Previous '+v+"</option>"}if(ah.next_chroms){ak+='<option value="next">Next '+v+"</option>"}ad.chrom_select.html(ak);ad.chrom_start_index=ah.start_index;ae.resolve(ah.chrom_info)},error:function(){alert("Could not load chroms for this dbkey:",ad.dbkey)}});return ae},change_chrom:function(ai,ae,ak){var af=this;if(!af.chrom_data){af.load_chroms_deferred.then(function(){af.change_chrom(ai,ae,ak)});return}if(!ai||ai==="None"){return}if(ai==="previous"){af.load_chroms({low:this.chrom_start_index-v});return}if(ai==="next"){af.load_chroms({low:this.chrom_start_index+v});return}var aj=$.grep(af.chrom_data,function(al,am){return al.chrom===ai})[0];if(aj===undefined){af.load_chroms({chrom:ai},function(){af.change_chrom(ai,ae,ak)});return}else{if(ai!==af.chrom){af.chrom=ai;af.chrom_select.val(af.chrom);af.max_high=aj.len-1;af.reset();for(var ah=0,ad=af.drawables.length;ah<ad;ah++){var ag=af.drawables[ah];if(ag.init){ag.init()}}if(af.reference_track){af.reference_track.init()}}if(ae&&ak){af.low=Math.max(ae,0);af.high=Math.min(ak,af.max_high)}else{af.low=0;af.high=af.max_high}af.reset_overview();af.request_redraw()}},go_to:function(ah){ah=ah.replace(/,/g,"");ah=ah.replace(/:|\-/g," ");var ae=ah.split(/\s+/),ag=ae[0],af=(ae[1]?parseInt(ae[1],10):null),ad=(ae[2]?parseInt(ae[2],10):null);if(!ad){af=af-15;ad=af+15}this.change_chrom(ag,af,ad)},move_fraction:function(af){var ad=this;var ae=ad.high-ad.low;this.move_delta(af*ae)},move_delta:function(ag){var ad=this;var af=ad.high-ad.low;if(ad.low-ag<ad.max_low){ad.low=ad.max_low;ad.high=ad.max_low+af}else{if(ad.high-ag>ad.max_high){ad.high=ad.max_high;ad.low=ad.max_high-af}else{ad.high-=ag;ad.low-=ag}}ad.request_redraw({data_fetch:false});if(this.redraw_on_move_fn){clearTimeout(this.redraw_on_move_fn)}this.redraw_on_move_fn=setTimeout(function(){ad.request_redraw()},200);var ae=ad.chrom_select.val();this.trigger_navigate(ae,ad.low,ad.high,true)},add_drawable:function(ad){y.prototype.add_drawable.call(this,ad);ad.init();this.changed();this.update_intro_div()},add_label_track:function(ad){ad.view=this;ad.init();this.label_tracks.push(ad)},remove_drawable:function(af,ae){y.prototype.remove_drawable.call(this,af);if(ae){var ad=this;af.container_div.hide(0,function(){$(this).remove();ad.update_intro_div()})}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(ae,af){var ad=this,ag=(af?[af]:ad.drawables);ac.each(ag,function(ah){var ai=ac.find(ad.tracks_to_be_redrawn,function(aj){return aj[0]===ah});if(ai){ai[1]=ae}else{ad.tracks_to_be_redrawn.push([ah,ae])}});if(!this.requested_redraw){requestAnimationFrame(function(){ad._redraw()});this.requested_redraw=true}},_redraw:function(){this.requested_redraw=false;var ad=this.low,ah=this.high;if(ad<this.max_low){ad=this.max_low}if(ah>this.max_high){ah=this.max_high}var ae=this.high-this.low;if(this.high!==0&&ae<this.min_separation){ah=ad+this.min_separation}this.low=Math.floor(ad);this.high=Math.ceil(ah);this.update_location(this.low,this.high);this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var ag=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ai=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var af=13;this.overview_box.css({left:ag,width:Math.max(af,ai)}).show();if(ai<af){this.overview_box.css("left",ag-(af-ai)/2)}if(this.overview_highlight){this.overview_highlight.css({left:ag,width:ai})}ac.each(this.tracks_to_be_redrawn,function(al){var aj=al[0],ak=al[1];if(aj){aj._draw(ak)}});this.tracks_to_be_redrawn=[];ac.each(this.label_tracks,function(aj){aj._draw()})},zoom_in:function(ae,af){if(this.max_high===0||this.high-this.low<=this.min_separation){return}var ag=this.high-this.low,ah=ag/2+this.low,ad=(ag/this.zoom_factor)/2;if(ae){ah=ae/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(ah-ad);this.high=Math.round(ah+ad);this.changed();this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var ae=this.high-this.low,af=ae/2+this.low,ad=(ae*this.zoom_factor)/2;this.low=Math.round(af-ad);this.high=Math.round(af+ad);this.changed();this.request_redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());this.request_redraw()},set_overview:function(af){if(this.overview_drawable){if(this.overview_drawable.dataset.id===af.dataset.id){return}this.overview_viewport.find(".track").remove()}var ae=af.copy({content_div:this.overview_viewport}),ad=this;ae.header_div.hide();ae.is_overview=true;ad.overview_drawable=ae;this.overview_drawable.postdraw_actions=function(){ad.overview_highlight.show().height(ad.overview_drawable.content_div.height());ad.overview_viewport.height(ad.overview_drawable.content_div.height()+ad.overview_box.outerHeight());ad.overview_close.show();ad.resize_window()};ad.overview_drawable.request_draw();this.changed()},reset_overview:function(){$(".tooltip").remove();this.overview_viewport.find(".track-tile").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide();view.resize_window();view.overview_drawable=null}});var q=R.Tool.extend({defaults:{track:null},initialize:function(ad){if(ad.tool_state!==undefined){this.set("hidden",ad.tool_state.hidden)}this.remove_inputs(["data","hidden_data","conditional"])},state_dict:function(ad){return ac.extend(this.get_inputs_dict(),{hidden:!this.is_visible()})}});var t=Backbone.View.extend({events:{"change input":"update_value"},render:function(){var af=this.$el.addClass("param-row"),ag=this.model;var ad=$("<div>").addClass("param-label").text(ag.get("label")).appendTo(af);var ae=$("<div/>").addClass("param-input").html(ag.get("html")).appendTo(af);ae.find(":input").val(ag.get("value"));$("<div style='clear: both;'/>").appendTo(af)},update_value:function(ad){this.model.set_value($(ad.target).val())}});var ab=Backbone.View.extend({initialize:function(ad){this.model.on("change:hidden",this.set_visible,this)},render:function(){var ae=this;tool=this.model,parent_div=this.$el.addClass("dynamic-tool").hide();parent_div.bind("drag",function(ai){ai.stopPropagation()}).click(function(ai){ai.stopPropagation()}).bind("dblclick",function(ai){ai.stopPropagation()}).keydown(function(ai){ai.stopPropagation()});var af=$("<div class='tool-name'>").appendTo(parent_div).text(tool.get("name"));tool.get("inputs").each(function(aj){var ai=new t({model:aj});ai.render();parent_div.append(ai.$el)});parent_div.find("input").click(function(){$(this).select()});var ag=$("<div>").addClass("param-row").appendTo(parent_div);var ah=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(ag);var ad=$("<input type='submit'>").attr("value","Run on visible region").css("margin-left","3em").appendTo(ag);ad.click(function(){ae.run_on_region()});ah.click(function(){ae.run_on_dataset()});if(tool.is_visible()){this.$el.show()}},set_visible:function(){if(this.model.is_visible()){this.$el.show()}else{this.$el.hide()}},update_params:function(){for(var ad=0;ad<this.params.length;ad++){this.params[ad].update_value()}},run_on_dataset:function(){var ad=this.model;this.run({target_dataset_id:this.model.get("track").dataset.id,action:"rerun",tool_id:ad.id},null,function(ae){Galaxy.modal.show({title:ad.get("name")+" is Running",body:ad.get("name")+" is running on the complete dataset. Tool outputs are in dataset's history.",buttons:{Close:function(){Galaxy.modal.hide()}}})})},run_on_region:function(){var ae=this.model.get("track"),ah=this.model,aj=new w.GenomeRegion({chrom:ae.view.chrom,start:ae.view.low,end:ae.view.high}),ak={target_dataset_id:ae.dataset.id,action:"rerun",tool_id:ah.id,regions:[aj.toJSON()]},ai=ae,am=ak.tool_id+ai.tool_region_and_parameters_str(aj),ad;if(ai.container===view){var al=new P(view,view,{name:this.name});var ag=ai.container.replace_drawable(ai,al,false);al.container_div.insertBefore(ai.view.content_div.children()[ag]);al.add_drawable(ai);ai.container_div.appendTo(al.content_div);ad=al}else{ad=ai.container}var af=new ai.constructor(view,ad,{name:am,hda_ldda:"hda"});af.init_for_tool_data();af.change_mode(ai.mode);af.set_filters_manager(ai.filters_manager.copy(af));af.update_icons();ad.add_drawable(af);af.tiles_div.text("Starting job.");this.run(ak,af,function(an){af.set_dataset(new X.Dataset(an));af.tiles_div.text("Running job.");af.init()})},run:function(ad,af,ag){ad.inputs=this.model.get_inputs_dict();var ae=new j.ServerStateDeferred({ajax_settings:{url:galaxy_config.root+"api/tools",data:JSON.stringify(ad),dataType:"json",contentType:"application/json",type:"POST"},interval:2000,success_fn:function(ah){return ah!=="pending"}});$.when(ae.go()).then(function(ah){if(ah==="no converter"){af.container_div.addClass("error");af.content_div.text(J)}else{if(ah.error){af.container_div.addClass("error");af.content_div.text(x+ah.message)}else{ag(ah)}}})}});var C=function(ad,ae){L.Scaler.call(this,ae);this.filter=ad};C.prototype.gen_val=function(ad){if(this.filter.high===Number.MAX_VALUE||this.filter.low===-Number.MAX_VALUE||this.filter.low===this.filter.high){return this.default_val}return((parseFloat(ad[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var F=function(ad){this.track=ad.track;this.params=ad.params;this.values={};this.restore_values((ad.saved_values?ad.saved_values:{}));this.onchange=ad.onchange};o(F.prototype,{set_param_default_value:function(ae,ad){var af=ac.find(this.params,function(ag){return ag.key===ae});if(af){af.default_value=ad}},set_param_value:function(ad,ae){var af=ac.find(this.params,function(ag){return ag.key===ad});if(af){if(typeof ae==="string"||ae instanceof String){if(ae.trim()===""){ae=af.default_value}else{if(af.type==="float"){ae=parseFloat(ae)}else{if(af.type==="int"){ae=parseInt(ae,10)}}}}}if(this.values[ad]!==ae){this.values[ad]=ae;return true}else{return false}},restore_values:function(ad){var ae=this;$.each(this.params,function(af,ag){if(ad[ag.key]!==undefined){ae.values[ag.key]=ad[ag.key]}else{ae.values[ag.key]=ag.default_value}})},build_form:function(){var ag=this;var ad=$("<div />");var af;function ae(al,ah){for(var ap=0;ap<al.length;ap++){af=al[ap];if(af.hidden){continue}var aj="param_"+ap;var au=ag.values[af.key];var aw=$("<div class='form-row' />").appendTo(ah);aw.append($("<label />").attr("for",aj).text(af.label+":"));if(af.type==="bool"){aw.append($('<input type="checkbox" />').attr("id",aj).attr("name",aj).attr("checked",au))}else{if(af.type==="text"){aw.append($('<input type="text"/>').attr("id",aj).val(au).click(function(){$(this).select()}))}else{if(af.type==="select"){var ar=$("<select />").attr("id",aj);for(var an=0;an<af.options.length;an++){$("<option/>").text(af.options[an].label).attr("value",af.options[an].value).appendTo(ar)}ar.val(au);aw.append(ar)}else{if(af.type==="color"){var av=$("<div/>").appendTo(aw),aq=$("<input />").attr("id",aj).attr("name",aj).val(au).css("float","left").appendTo(av).click(function(ay){$(".tooltip").removeClass("in");var ax=$(this).siblings(".tooltip").addClass("in");ax.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top+Galaxy.modal.scrollTop()-($(ax).height()/2)+($(this).height()/2)}).show();ax.click(function(az){az.stopPropagation()});$(document).bind("click.color-picker",function(){ax.hide();$(document).unbind("click.color-picker")});ay.stopPropagation()}),ao=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(av).attr("title","Set new random color").tooltip(),at=$("<div class='tooltip right' style='position: absolute;' />").appendTo(av).hide(),ak=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(at),ai=$("<div class='tooltip-arrow'></div>").appendTo(at),am=$.farbtastic(ak,{width:100,height:100,callback:aq,color:au});av.append($("<div/>").css("clear","both"));(function(ax){ao.click(function(){ax.setColor(j.get_random_color())})})(am)}else{aw.append($("<input />").attr("id",aj).attr("name",aj).val(au))}}}}if(af.help){aw.append($("<div class='help'/>").text(af.help))}}}ae(this.params,ad);return ad},update_from_form:function(ad){var af=this;var ae=false;$.each(this.params,function(ag,ai){if(!ai.hidden){var aj="param_"+ag;var ah=ad.find("#"+aj).val();if(ai.type==="bool"){ah=ad.find("#"+aj).is(":checked")}ae=af.set_param_value(ai.key,ah)||ae}});if(ae){this.onchange();this.track.changed()}}});var b=function(ad,ag,ah,ae,af){this.track=ad;this.region=ag;this.low=ag.get("start");this.high=ag.get("end");this.w_scale=ah;this.html_elt=$("<div class='track-tile'/>").append(ae).height($(ae).attr("height"));this.data=af;this.stale=false};b.prototype.predisplay_actions=function(){};var M=function(ad,ag,ah,ae,af){b.call(this,ad,ag,ah,ae,af)};M.prototype.predisplay_actions=function(){};var O=function(ag,an,ap,af,ai,aj,aq,ae,am){b.call(this,ag,an,ap,af,ai);this.mode=aj;this.all_slotted=ae;this.feature_mapper=am;this.has_icons=false;if(aq){this.has_icons=true;var ak=this;af=this.html_elt.children()[0],message_div=$("<div/>").addClass("tile-message").css({height:D-1,width:af.width}).prependTo(this.html_elt);var al=new w.GenomeRegion({chrom:ag.view.chrom,start:this.low,end:this.high}),ao=ai.length,ah=$("<a href='javascript:void(0);'/>").addClass("icon more-down").attr("title","For speed, only the first "+ao+" features in this region were obtained from server. Click to get more data including depth").tooltip().appendTo(message_div),ad=$("<a href='javascript:void(0);'/>").addClass("icon more-across").attr("title","For speed, only the first "+ao+" features in this region were obtained from server. Click to get more data excluding depth").tooltip().appendTo(message_div);ah.click(function(){ak.stale=true;ag.data_manager.get_more_data(al,ag.mode,1/ak.w_scale,{},ag.data_manager.DEEP_DATA_REQ);$(".tooltip").hide();ag.request_draw()}).dblclick(function(ar){ar.stopPropagation()});ad.click(function(){ak.stale=true;ag.data_manager.get_more_data(al,ag.mode,1/ak.w_scale,{},ag.data_manager.BROAD_DATA_REQ);$(".tooltip").hide();ag.request_draw()}).dblclick(function(ar){ar.stopPropagation()})}};o(O.prototype,b.prototype);O.prototype.predisplay_actions=function(){var ae=this,ad={};if(ae.mode!=="Pack"){return}$(this.html_elt).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()}).mousemove(function(ap){if(!this.hovered){return}var ak=$(this).offset(),ao=ap.pageX-ak.left,an=ap.pageY-ak.top,au=ae.feature_mapper.get_feature_data(ao,an),al=(au?au[0]:null);$(this).parents(".track-content").children(".overlay").children(".feature-popup").each(function(){if(!al||$(this).attr("id")!==al.toString()){$(this).remove()}});if(au){var ag=ad[al];if(!ag){var al=au[0],aq={name:au[3],start:au[1],end:au[2],strand:au[4]},aj=ae.track.filters_manager.filters,ai;for(var am=0;am<aj.length;am++){ai=aj[am];aq[ai.name]=au[ai.index]}var ag=$("<div/>").attr("id",al).addClass("feature-popup"),av=$("<table/>"),at,ar,aw;for(at in aq){ar=aq[at];aw=$("<tr/>").appendTo(av);$("<th/>").appendTo(aw).text(at);$("<td/>").attr("align","left").appendTo(aw).text(typeof(ar)==="number"?V(ar,2):ar)}ag.append($("<div class='feature-popup-inner'>").append(av));ad[al]=ag}ag.appendTo($(this).parents(".track-content").children(".overlay"));var ah=ao+parseInt(ae.html_elt.css("left"))-ag.width()/2,af=an+parseInt(ae.html_elt.css("top"))+7;ag.css("left",ah+"px").css("top",af+"px")}else{if(!ap.isPropagationStopped()){ap.stopPropagation();$(this).siblings().each(function(){$(this).trigger(ap)})}}}).mouseleave(function(){$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()})};var f=function(ae,ad,af){o(af,{drag_handle_class:"draghandle"});p.call(this,ae,ad,af);this.dataset=null;if(af.dataset){this.dataset=(af.dataset instanceof Backbone.Model?af.dataset:X.Dataset.findOrCreate(af.dataset))}this.dataset_check_type="converted_datasets_state";this.data_url_extra_params={};this.data_query_wait=("data_query_wait" in af?af.data_query_wait:K);this.data_manager=("data_manager" in af?af.data_manager:new w.GenomeDataManager({dataset:this.dataset,genome:new w.Genome({key:ae.dbkey,chroms_info:{chrom_info:ae.chrom_data}}),data_mode_compatible:this.data_and_mode_compatible,can_subset:this.can_subset}));this.min_height_px=16;this.max_height_px=800;this.visible_height_px=0;this.content_div=$("<div class='track-content'>").appendTo(this.container_div);if(this.container){this.container.content_div.append(this.container_div);if(!("resize" in af)||af.resize){this.add_resize_handle()}}};o(f.prototype,p.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},p.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"overview-icon",on_click_fn:function(ad){ad.view.set_overview(ad)}},p.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(ad){if(ad.filters_manager.visible()){ad.filters_manager.clear_filters()}else{ad.filters_manager.init_filters()}ad.filters_manager.toggle()}},{name:"tools_icon",title:"Tool",css_class:"hammer",on_click_fn:function(ad){ad.tool.toggle();if(ad.tool.is_visible()){ad.set_name(ad.name+ad.tool_region_and_parameters_str())}else{ad.revert_name()}$(".tooltip").remove()}},{name:"param_space_viz_icon",title:"Tool parameter space visualization",css_class:"arrow-split",on_click_fn:function(ad){var ag='<strong>Tool</strong>: <%= track.tool.name %><br/><strong>Dataset</strong>: <%= track.name %><br/><strong>Region(s)</strong>: <select name="regions"><option value="cur">current viewing area</option><option value="bookmarks">bookmarks</option><option value="both">current viewing area and bookmarks</option></select>',af=ac.template(ag,{track:ad});var ai=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},ae=function(){var ak=$('select[name="regions"] option:selected').val(),am,aj=new w.GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),al=ac.map($(".bookmark"),function(an){return new w.GenomeRegion({from_str:$(an).children(".position").text()})});if(ak==="cur"){am=[aj]}else{if(ak==="bookmarks"){am=al}else{am=[aj].concat(al)}}Galaxy.modal.hide();window.location.href=galaxy_config.root+"visualization/sweepster?"+$.param({dataset_id:ad.dataset.id,hda_ldda:ad.dataset.get("hda_ldda"),regions:JSON.stringify(new Backbone.Collection(am).toJSON())})},ah=function(aj){if((aj.keyCode||aj.which)===27){ai()}else{if((aj.keyCode||aj.which)===13){ae()}}};Galaxy.modal.show({title:"Visualize tool parameter space and output from different parameter settings?",body:af,buttons:{No:ai,Yes:ae}})}},p.prototype.action_icons_def[2]],can_draw:function(){return this.dataset&&p.prototype.can_draw.call(this)},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id).css("position","relative")},build_header_div:function(){var ad=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(ad)}this.name_div=$("<div/>").addClass("track-name").appendTo(ad).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return ad},set_dataset:function(ad){this.dataset=ad;this.data_manager.set("dataset",ad)},on_resize:function(){this.request_draw({clear_tile_cache:true})},add_resize_handle:function(){var ad=this;var ag=false;var af=false;var ae=$("<div class='track-resize'>");$(ad.container_div).hover(function(){if(ad.content_visible){ag=true;ae.show()}},function(){ag=false;if(!af){ae.hide()}});ae.hide().bind("dragstart",function(ah,ai){af=true;ai.original_height=$(ad.content_div).height()}).bind("drag",function(ai,aj){var ah=Math.min(Math.max(aj.original_height+aj.deltaY,ad.min_height_px),ad.max_height_px);$(ad.tiles_div).css("height",ah);ad.visible_height_px=(ad.max_height_px===ah?0:ah);ad.on_resize()}).bind("dragend",function(ah,ai){ad.tile_cache.clear();af=false;if(!ag){ae.hide()}ad.config.values.height=ad.visible_height_px;ad.changed()}).appendTo(ad.container_div)},set_display_modes:function(ag,aj){this.display_modes=ag;this.mode=(aj?aj:(this.config&&this.config.values.mode?this.config.values.mode:this.display_modes[0]));this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var ae=this,ah={};for(var af=0,ad=ae.display_modes.length;af<ad;af++){var ai=ae.display_modes[af];ah[ai]=function(ak){return function(){ae.change_mode(ak);ae.icons_div.show();ae.container_div.mouseleave(function(){ae.icons_div.hide()})}}(ai)}make_popupmenu(this.action_icons.mode_icon,ah)},build_action_icons:function(){p.prototype.build_action_icons.call(this,this.action_icons_def);if(this.display_modes!==undefined){this.set_display_modes(this.display_modes)}},hide_contents:function(){this.tiles_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.tiles_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof W){return"LabelTrack"}else{if(this instanceof B){return"ReferenceTrack"}else{if(this instanceof g){return"LineTrack"}else{if(this instanceof T){return"ReadTrack"}else{if(this instanceof Z){return"VariantTrack"}else{if(this instanceof e){return"CompositeTrack"}else{if(this instanceof c){return"FeatureTrack"}}}}}}}return""},init:function(af){var ae=this;ae.enabled=false;ae.tile_cache.clear();ae.data_manager.clear();ae.tiles_div.css("height","auto");ae.tiles_div.text("").children().remove();ae.container_div.removeClass("nodata error pending");if(!ae.dataset.id){return}var ad=$.Deferred(),ag={hda_ldda:ae.dataset.get("hda_ldda"),data_type:this.dataset_check_type,chrom:ae.view.chrom,retry:af};$.getJSON(this.dataset.url(),ag,function(ah){if(!ah||ah==="error"||ah.kind==="error"){ae.container_div.addClass("error");ae.tiles_div.text(m);if(ah.message){ae.tiles_div.append($("<a href='javascript:void(0);'></a>").text("View error").click(function(){Galaxy.modal.show({title:"Trackster Error",body:"<pre>"+ah.message+"</pre>",buttons:{Close:function(){Galaxy.modal.hide()}}})}));ae.tiles_div.append($("<span/>").text(" "));ae.tiles_div.append($("<a href='javascript:void(0);'></a>").text("Try again").click(function(){ae.init(true)}))}}else{if(ah==="no converter"){ae.container_div.addClass("error");ae.tiles_div.text(J)}else{if(ah==="no data"||(ah.data!==undefined&&(ah.data===null||ah.data.length===0))){ae.container_div.addClass("nodata");ae.tiles_div.text(E)}else{if(ah==="pending"){ae.container_div.addClass("pending");ae.tiles_div.html(u);setTimeout(function(){ae.init()},ae.data_query_wait)}else{if(ah==="data"||ah.status==="data"){if(ah.valid_chroms){ae.valid_chroms=ah.valid_chroms;ae.update_icons()}ae.tiles_div.text(U);if(ae.view.chrom){ae.tiles_div.text("");ae.tiles_div.css("height",ae.visible_height_px+"px");ae.enabled=true;$.when.apply($,ae.predraw_init()).done(function(){ad.resolve();ae.container_div.removeClass("nodata error pending");ae.request_draw()})}else{ad.resolve()}}}}}}});this.update_icons();return ad},predraw_init:function(){var ad=this;return $.getJSON(ad.dataset.url(),{data_type:"data",stats:true,chrom:ad.view.chrom,low:0,high:ad.view.max_high,hda_ldda:ad.dataset.get("hda_ldda")},function(ae){ad.container_div.addClass("line-track");var ag=ae.data;if(ag&&ag.min&&ag.max){var af=ag.min,ah=ag.max;af=Math.floor(Math.min(0,Math.max(af,ag.mean-2*ag.sd)));ah=Math.ceil(Math.max(0,Math.min(ah,ag.mean+2*ag.sd)));ad.config.set_param_default_value("min_value",af);ad.config.set_param_default_value("max_value",ah);ad.prefs.min_value=af;ad.prefs.max_value=ah}})},get_drawables:function(){return this}});var N=function(af,ae,ah){f.call(this,af,ae,ah);var ad=this;k(ad.container_div,ad.drag_handle_class,".group",ad);this.filters_manager=new h.FiltersManager(this,("filters" in ah?ah.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=(ah.tool?new q(ac.extend(ah.tool,{track:this,tool_state:ah.tool_state})):null);this.tile_cache=new w.Cache(Q);this.left_offset=0;if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){var ag=new ab({model:this.tool});ag.render();this.dynamic_tool_div=ag.$el;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(ah.mode){this.change_mode(ah.mode)}};o(N.prototype,p.prototype,f.prototype,{action_icons_def:f.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(ad){$(".tooltip").remove();ad.slotters[ad.view.resolution_px_b].max_rows*=2;ad.request_draw({clear_tile_cache:true})},hide:true}]),copy:function(ad){var ae=this.to_dict();o(ae,{data_manager:this.data_manager});var af=new this.constructor(this.view,ad,ae);af.change_mode(this.mode);af.enabled=this.enabled;return af},set_filters_manager:function(ad){this.filters_manager=ad;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),name:this.name,dataset:{id:this.dataset.id,hda_ldda:this.dataset.get("hda_ldda")},prefs:this.prefs,mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},set_min_max:function(){var ad=this;return $.getJSON(ad.dataset.url(),{data_type:"data",stats:true,chrom:ad.view.chrom,low:0,high:ad.view.max_high,hda_ldda:ad.dataset.get("hda_ldda")},function(ae){var ag=ae.data;if(isNaN(parseFloat(ad.prefs.min_value))||isNaN(parseFloat(ad.prefs.max_value))){var af=ag.min,ah=ag.max;af=Math.floor(Math.min(0,Math.max(af,ag.mean-2*ag.sd)));ah=Math.ceil(Math.max(0,Math.min(ah,ag.mean+2*ag.sd)));ad.prefs.min_value=af;ad.prefs.max_value=ah}})},change_mode:function(ae){var ad=this;ad.mode=ae;ad.config.values.mode=ae;if(ae==="Auto"){this.data_manager.clear()}ad.request_draw({clear_tile_cache:true});this.action_icons.mode_icon.attr("title","Set display mode (now: "+ad.mode+")");return ad},update_icons:function(){var ad=this;if(ad.filters_available){ad.action_icons.filters_icon.show()}else{ad.action_icons.filters_icon.hide()}if(ad.tool){ad.action_icons.tools_icon.show();ad.action_icons.param_space_viz_icon.show()}else{ad.action_icons.tools_icon.hide();ad.action_icons.param_space_viz_icon.hide()}},_gen_tile_cache_key:function(ae,ad){return ae+"_"+ad},request_draw:function(ad){if(ad&&ad.clear_tile_cache){this.tile_cache.clear()}this.view.request_redraw(ad,this)},before_draw:function(){this.max_height_px=0},_draw:function(ar){if(!this.can_draw()){return}var ao=ar&&ar.clear_after,am=this.view.low,ah=this.view.high,ak=ah-am,ae=this.view.container.width(),aq=this.view.resolution_px_b,ag=1/aq;if(this.is_overview){am=this.view.max_low;ah=this.view.max_high;aq=ae/(view.max_high-view.max_low);ag=1/aq}this.before_draw();this.tiles_div.children().addClass("remove");var ad=Math.floor(am/(ag*S)),al,an,ai,aj=[],ap=[];while((ad*S*ag)<ah){al=Math.floor(ad*S*ag);an=new w.GenomeRegion({chrom:this.view.chrom,start:al,end:Math.min(al+Math.ceil(S*ag),this.view.max_high)});ai=this.draw_helper(an,aq,ar);aj.push(ai);$.when(ai).then(function(at){ap.push(at)});ad+=1}if(!ao){this.tiles_div.children(".remove").removeClass("remove").remove()}var af=this;$.when.apply($,aj).then(function(){af.tiles_div.children(".remove").remove();ap=ac.filter(ap,function(at){return at!==null});if(ap.length!==0){af.postdraw_actions(ap,ae,aq,ao)}})},_add_yaxis_label:function(ag,ai){var ae=this,ah=(ag==="max"?"top":"bottom"),aj=(ag==="max"?"max":"min"),ad=(ag==="max"?"max_value":"min_value"),ai=ai||function(){ae.request_draw({clear_tile_cache:true})},af=this.container_div.find(".yaxislabel."+ah);if(af.length!==0){af.text(ae.prefs[ad])}else{af=$("<div/>").text(ae.prefs[ad]).make_text_editable({num_cols:12,on_finish:function(ak){$(".tooltip").remove();ae.config.set_param_value(ad,ak);ai()},help_text:"Set "+aj+" value"}).addClass("yaxislabel "+ah).css("color",this.prefs.label_color);this.container_div.prepend(af)}},postdraw_actions:function(ag,ah,aj,ad){var af=ac.filter(ag,function(ak){return(ak instanceof M)});if(af.length>0){this.max_height_px=0;var ae=this;ac.each(ag,function(ak){if(!(ak instanceof M)){ak.html_elt.remove();ae.draw_helper(ak.region,aj,{force:true,mode:"Coverage"})}});ae._add_yaxis_label("max")}else{this.container_div.find(".yaxislabel").remove();var ai=ac.find(ag,function(ak){return ak.has_icons});if(ai){ac.each(ag,function(ak){if(!ak.has_icons){ak.html_elt.css("padding-top",D)}})}}},get_mode:function(ad){return this.mode},update_auto_mode:function(ad){},_get_drawables:function(){return[this]},draw_helper:function(am,ap,aq){if(!aq){aq={}}var ae=aq.force,ao=!(aq.data_fetch===false),aj=aq.mode||this.mode,ag=1/ap,af=this,ah=this._get_drawables(),an=this._gen_tile_cache_key(ap,am),ai=function(ar){return(ar&&"track" in ar)};var ak=(ae?undefined:af.tile_cache.get_elt(an));if(ak){if(ai(ak)){af.show_tile(ak,ap)}return ak}if(!ao){return null}var al=function(){var ar=(ac.find(A,function(au){return au===aj})?"Coverage":aj);var at=ac.map(ah,function(au){return au.data_manager.get_data(am,ar,ag,af.data_url_extra_params)});if(view.reference_track){at.push(view.reference_track.data_manager.get_data(am,aj,ag,view.reference_track.data_url_extra_params))}return at};var ad=$.Deferred();af.tile_cache.set_elt(an,ad);$.when.apply($,al()).then(function(){var ar=al(),ax=ar,aD;if(view.reference_track){aD=view.reference_track.data_manager.subset_entry(ar.pop(),am)}var ay=[],av=[];ac.each(ah,function(aI,aF){var aH=aI.mode,aG=ax[aF];if(aH==="Auto"){aH=aI.get_mode(aG);aI.update_auto_mode(aH)}ay.push(aH);av.push(aI.get_canvas_height(aG,aH,ap,at))});var aw=af.view.canvas_manager.new_canvas(),az=am.get("start"),aE=am.get("end"),au=0,at=Math.ceil((aE-az)*ap)+af.left_offset,aB=ac.max(av),aA;aw.width=at;aw.height=(aq.height||aB);var aC=aw.getContext("2d");aC.translate(af.left_offset,0);if(ah.length>1){aC.globalAlpha=0.5;aC.globalCompositeOperation="source-over"}ac.each(ah,function(aG,aF){aA=aG.draw_tile(ax[aF],aC,ay[aF],am,ap,aD)});if(aA!==undefined){af.tile_cache.set_elt(an,aA);af.show_tile(aA,ap)}ad.resolve(aA)});return ad},get_canvas_height:function(ad,af,ag,ae){return this.visible_height_px},_draw_line_track_tile:function(ad,af,ai,ah,aj){var ag=af.canvas,ae=new L.LinePainter(ad.data,ah.get("start"),ah.get("end"),this.prefs,ai);ae.draw(af,ag.width,ag.height,aj);return new M(this,ah,aj,ag,ad.data)},draw_tile:function(ad,ae,ah,ag,ai,af){},show_tile:function(af,ai){var ae=this,ad=af.html_elt;af.predisplay_actions();var ah=(af.low-(this.is_overview?this.view.max_low:this.view.low))*ai;if(this.left_offset){ah-=this.left_offset}ad.css({position:"absolute",top:0,left:ah});if(ad.hasClass("remove")){ad.removeClass("remove")}else{this.tiles_div.append(ad)}af.html_elt.height("auto");this.max_height_px=Math.max(this.max_height_px,af.html_elt.height());af.html_elt.parent().children().css("height",this.max_height_px+"px");var ag=this.max_height_px;if(this.visible_height_px!==0){ag=Math.min(this.max_height_px,this.visible_height_px)}this.tiles_div.css("height",ag+"px")},tool_region_and_parameters_str:function(ag){var ad=this,af=(ag!==undefined?ag.toString():"all"),ae=ac.values(ad.tool.get_inputs_dict()).join(", ");return" - region=["+af+"], parameters=["+ae+"]"},data_and_mode_compatible:function(ad,ae){if(ae==="Auto"){return true}else{if(ae==="Coverage"){return ad.dataset_type==="bigwig"}else{if(ad.dataset_type==="bigwig"||ad.extra_info==="no_detail"){return false}else{return true}}}},can_subset:function(ad){if(ad.message||ad.extra_info==="no_detail"){return false}else{if(ad.dataset_type==="bigwig"){return(ad.data[1][0]-ad.data[0][0]===1)}}return true},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data");this.data_query_wait=1000;this.dataset_check_type="state"}});var W=function(ae,ad){var af={resize:false};f.call(this,ae,ad,af);this.container_div.addClass("label-track")};o(W.prototype,f.prototype,{build_header_div:function(){},init:function(){this.enabled=true},predraw_init:function(){},_draw:function(ah){var af=this.view,ag=af.high-af.low,ak=Math.floor(Math.pow(10,Math.floor(Math.log(ag)/Math.log(10)))),ad=Math.floor(af.low/ak)*ak,ai=this.view.container.width(),ae=$("<div style='position: relative; height: 1.3em;'></div>");while(ad<af.high){var aj=(ad-af.low)/ag*ai;ae.append($("<div class='label'>"+commatize(ad)+"</div>").css({position:"absolute",left:aj-1}));ad+=ak}this.content_div.children(":first").remove();this.content_div.append(ae)}});var e=function(ae,ad,ah){this.display_modes=A;N.call(this,ae,ad,ah);this.drawables=[];if("drawables" in ah){var ag;for(var af=0;af<ah.drawables.length;af++){ag=ah.drawables[af];this.drawables[af]=n(ag,ae,null);if(ag.left_offset>this.left_offset){this.left_offset=ag.left_offset}}this.enabled=true}ac.each(this.drawables,function(ai){if(ai instanceof c||ai instanceof T){ai.change_mode("Coverage")}});this.update_icons();this.obj_type="CompositeTrack"};o(e.prototype,N.prototype,{action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(ad){$(".tooltip").remove();ad.show_group()}}].concat(N.prototype.action_icons_def),to_dict:y.prototype.to_dict,add_drawable:y.prototype.add_drawable,unpack_drawables:y.prototype.unpack_drawables,change_mode:function(ad){N.prototype.change_mode.call(this,ad);for(var ae=0;ae<this.drawables.length;ae++){this.drawables[ae].change_mode(ad)}},init:function(){var af=[];for(var ae=0;ae<this.drawables.length;ae++){af.push(this.drawables[ae].init())}var ad=this;$.when.apply($,af).then(function(){ad.enabled=true;ad.request_draw()})},update_icons:function(){this.action_icons.filters_icon.hide();this.action_icons.tools_icon.hide();this.action_icons.param_space_viz_icon.hide()},can_draw:p.prototype.can_draw,_get_drawables:function(){return this.drawables},show_group:function(){var ag=new P(this.view,this.container,{name:this.name}),ad;for(var af=0;af<this.drawables.length;af++){ad=this.drawables[af];ad.update_icons();ag.add_drawable(ad);ad.container=ag;ag.content_div.append(ad.container_div)}var ae=this.container.replace_drawable(this,ag,true);ag.request_draw({clear_tile_cache:true})},before_draw:function(){N.prototype.before_draw.call(this);var ae=ac.min(ac.map(this.drawables,function(af){return af.prefs.min_value})),ad=ac.max(ac.map(this.drawables,function(af){return af.prefs.max_value}));this.prefs.min_value=ae;this.prefs.max_value=ad;ac.each(this.drawables,function(af){af.prefs.min_value=ae;af.prefs.max_value=ad})},update_all_min_max:function(){var ad=this;ac.each(this.drawables,function(ae){ae.prefs.min_value=ad.prefs.min_value;ae.prefs.max_value=ad.prefs.max_value});this.request_draw({clear_tile_cache:true})},postdraw_actions:function(aj,ad,am,ai){N.prototype.postdraw_actions.call(this,aj,ad,am,ai);var ah=-1;for(var af=0;af<aj.length;af++){var ak=aj[af].html_elt.find("canvas").height();if(ak>ah){ah=ak}}for(var af=0;af<aj.length;af++){var ag=aj[af];if(ag.html_elt.find("canvas").height()!==ah){this.draw_helper(ag.region,am,{force:true,height:ah});ag.html_elt.remove()}}var ae=this,al=function(){ae.update_all_min_max()};this._add_yaxis_label("min",al);this._add_yaxis_label("max",al)}});var B=function(ad){N.call(this,ad,{content_div:ad.top_labeltrack},{resize:false});ad.reference_track=this;this.left_offset=200;this.visible_height_px=12;this.container_div.addClass("reference-track");this.content_div.css("background","none");this.content_div.css("min-height","0px");this.content_div.css("border","none");this.data_url=galaxy_config.root+"api/genomes//"+this.view.dbkey;this.data_url_extra_params={reference:true};this.data_manager=new w.GenomeReferenceDataManager({data_url:this.data_url,can_subset:this.can_subset});this.hide_contents()};o(B.prototype,p.prototype,N.prototype,{build_header_div:function(){},init:function(){this.data_manager.clear();this.enabled=true},predraw_init:function(){},can_draw:p.prototype.can_draw,draw_helper:function(ae,af,ad){if(af>this.view.canvas_manager.char_width_px){this.tiles_div.show();return N.prototype.draw_helper.call(this,ae,af,ad)}else{this.tiles_div.hide();return null}},can_subset:function(ad){return true},draw_tile:function(af,al,ag,ai,am){var ae=this.data_manager.subset_entry(af,ai),ak=ae.data;var ad=al.canvas;al.font=al.canvas.manager.default_font;al.textAlign="center";for(var ah=0,aj=ak.length;ah<aj;ah++){al.fillStyle=this.view.get_base_color(ak[ah]);al.fillText(ak[ah],Math.floor(ah*am),10)}return new b(this,ai,am,ad,ae)}});var g=function(af,ae,ag){var ad=this;this.display_modes=A;this.mode="Histogram";N.call(this,af,ae,ag);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"color",label:"Color",type:"color",default_value:j.get_random_color()},{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true}],saved_values:ag.prefs,onchange:function(){ad.set_name(ad.prefs.name);ad.request_redraw({clear_tile_cache:true})}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height};o(g.prototype,p.prototype,N.prototype,{before_draw:function(){},draw_tile:function(ad,ae,ag,af,ah){return this._draw_line_track_tile(ad,ae,ag,af,ah)},can_subset:function(ad){return(ad.data[1][0]-ad.data[0][0]===1)},postdraw_actions:function(ae,af,ag,ad){this._add_yaxis_label("max");this._add_yaxis_label("min")}});var r=function(af,ae,ag){var ad=this;this.display_modes=["Heatmap"];this.mode="Heatmap";N.call(this,af,ae,ag);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"min_value",label:"Min Value",type:"float",default_value:-1},{key:"max_value",label:"Max Value",type:"float",default_value:1},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:500,hidden:true}],saved_values:ag.prefs,onchange:function(){ad.set_name(ad.prefs.name);this.request_redraw({clear_tile_cache:true})}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height};o(r.prototype,p.prototype,N.prototype,{draw_tile:function(ad,af,ai,ah,aj){var ag=af.canvas,ae=new L.DiagonalHeatmapPainter(ad.data,ah.get("start"),ah.get("end"),this.prefs,ai);ae.draw(af,ag.width,ag.height,aj);return new b(this,ah,aj,ag,ad.data)}});var c=function(ag,af,ai){var ae=this;this.display_modes=["Auto","Coverage","Dense","Squish","Pack"];N.call(this,ag,af,ai);var ah=j.get_random_color(),ad=j.get_random_color([ah,"#FFFFFF"]);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:ah},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:ad},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"connector_style",label:"Connector style",type:"select",default_value:"fishbones",options:[{label:"Line with arrows",value:"fishbone"},{label:"Arcs",value:"arcs"}]},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:this.visible_height_px,hidden:true}],saved_values:ai.prefs,onchange:function(){ae.set_name(ae.prefs.name);ae.set_painter_from_config();ae.request_draw({clear_tile_cache:true})}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.container_div.addClass("feature-track");this.show_labels_scale=0.001;this.showing_details=false;this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};o(c.prototype,p.prototype,N.prototype,{set_painter_from_config:function(){if(this.config.values.connector_style==="arcs"){this.painter=L.ArcLinkedFeaturePainter}else{this.painter=L.LinkedFeaturePainter}},postdraw_actions:function(an,af,ao,am){N.prototype.postdraw_actions.call(this,an,af,ao,am);var ah=this,ai;if(ah.filters_manager){var ag=ah.filters_manager.filters;for(var ak=0;ak<ag.length;ak++){ag[ak].update_ui_elt()}var aj=false,al,ae;for(ai=0;ai<an.length;ai++){if(an[ai].data.length){al=an[ai].data[0];for(var ak=0;ak<ag.length;ak++){ae=ag[ak];if(ae.applies_to(al)&&ae.min!==ae.max){aj=true;break}}}}if(ah.filters_available!==aj){ah.filters_available=aj;if(!ah.filters_available){ah.filters_manager.hide()}ah.update_icons()}}if(an[0] instanceof O){var ad=true;for(ai=0;ai<an.length;ai++){if(!an[ai].all_slotted){ad=false;break}}if(!ad){this.action_icons.show_more_rows_icon.show()}else{this.action_icons.show_more_rows_icon.hide()}}else{this.action_icons.show_more_rows_icon.hide()}},update_auto_mode:function(ad){var ad;if(this.mode==="Auto"){if(ad==="no_detail"){ad="feature spans"}this.action_icons.mode_icon.attr("title","Set display mode (now: Auto/"+ad+")")}},incremental_slots:function(ah,ad,ag){var ae=this.view.canvas_manager.dummy_context,af=this.slotters[ah];if(!af||(af.mode!==ag)){af=new (s.FeatureSlotter)(ah,ag,z,function(ai){return ae.measureText(ai)});this.slotters[ah]=af}return af.slot_features(ad)},get_mode:function(ad){if(ad.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>I){mode="Squish"}else{mode="Pack"}}return mode},get_canvas_height:function(ad,ah,ai,ae){if(ah==="Coverage"||ad.dataset_type==="bigwig"){return this.summary_draw_height}else{var ag=this.incremental_slots(ai,ad.data,ah);var af=new (this.painter)(null,null,null,this.prefs,ah);return Math.max(aa,af.get_required_height(ag,ae))}},draw_tile:function(an,ar,ap,ag,ak,af){var aq=this,ae=ar.canvas,ay=ag.get("start"),ad=ag.get("end"),ah=this.left_offset;if(an.dataset_type==="bigwig"){return this._draw_line_track_tile(an,ar,ap,ag,ak)}var aj=[],ao=this.slotters[ak].slots;all_slotted=true;if(an.data){var al=this.filters_manager.filters;for(var at=0,av=an.data.length;at<av;at++){var ai=an.data[at];var au=false;var am;for(var ax=0,aC=al.length;ax<aC;ax++){am=al[ax];am.update_attrs(ai);if(!am.keep(ai)){au=true;break}}if(!au){aj.push(ai);if(!(ai[0] in ao)){all_slotted=false}}}}var aB=(this.filters_manager.alpha_filter?new C(this.filters_manager.alpha_filter):null),az=(this.filters_manager.height_filter?new C(this.filters_manager.height_filter):null),aA=new (this.painter)(aj,ay,ad,this.prefs,ap,aB,az,af,function(aD){return aq.view.get_base_color(aD)});var aw=null;ar.fillStyle=this.prefs.block_color;ar.font=ar.canvas.manager.default_font;ar.textAlign="right";if(an.data){aw=aA.draw(ar,ae.width,ae.height,ak,ao);aw.translation=-ah}return new O(aq,ag,ak,ae,an.data,ap,an.message,all_slotted,aw)}});var Z=function(ae,ad,af){this.display_modes=["Auto","Coverage","Dense","Squish","Pack"];N.call(this,ae,ad,af);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"color",label:"Histogram color",type:"color",default_value:j.get_random_color()},{key:"show_sample_data",label:"Show sample data",type:"bool",default_value:true},{key:"show_labels",label:"Show summary and sample labels",type:"bool",default_value:true},{key:"summary_height",label:"Locus summary height",type:"float",default_value:20},{key:"mode",type:"string",default_value:this.mode,hidden:true}],saved_values:af.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.request_draw({clear_tile_cache:true})}});this.prefs=this.config.values;this.painter=L.VariantPainter;this.summary_draw_height=30;this.left_offset=30};o(Z.prototype,p.prototype,N.prototype,{draw_tile:function(ad,ag,ai,ah,aj){if(ad.dataset_type==="bigwig"){return this._draw_line_track_tile(ad,ag,"Histogram",ah,aj)}else{var af=this.view,ae=new (this.painter)(ad.data,ah.get("start"),ah.get("end"),this.prefs,ai,function(ak){return af.get_base_color(ak)});ae.draw(ag,ag.canvas.width,ag.canvas.height,aj);return new b(this,ah,aj,ag.canvas,ad.data)}},get_canvas_height:function(ad,ah,ai,af){if(ad.dataset_type==="bigwig"){return this.summary_draw_height}else{var ae=(this.dataset.get_metadata("sample_names")?this.dataset.get_metadata("sample_names").length:0);if(ae===0&&ad.data.length!==0){ae=ad.data[0][7].match(/,/g);if(ae===null){ae=1}else{ae=ae.length+1}}var ag=new (this.painter)(null,null,null,this.prefs,ah);return ag.get_required_height(ae)}},predraw_init:function(){var ad=[f.prototype.predraw_init.call(this)];if(!this.dataset.get_metadata("sample_names")){ad.push(this.dataset.fetch())}return ad},postdraw_actions:function(ah,ai,ak,ae){N.prototype.postdraw_actions.call(this,ah,ai,ak,ae);var ag=ac.filter(ah,function(al){return(al instanceof M)});var af=this.dataset.get_metadata("sample_names");if(ag.length===0&&this.prefs.show_labels&&af){var ad;if(this.container_div.find(".yaxislabel.variant").length===0){ad=this.prefs.summary_height/2;this.tiles_div.prepend($("<div/>").text("Summary").addClass("yaxislabel variant top").css({"font-size":ad+"px",top:(this.prefs.summary_height-ad)/2+"px"}));if(this.prefs.show_sample_data){var aj=af.join("<br/>");this.tiles_div.prepend($("<div/>").html(aj).addClass("yaxislabel variant top sample").css({top:this.prefs.summary_height+2,}))}}ad=(this.mode==="Squish"?5:10)+"px";$(this.tiles_div).find(".sample").css({"font-size":ad,"line-height":ad});$(this.tiles_div).find(".yaxislabel").css("color",this.prefs.label_color)}else{this.container_div.find(".yaxislabel.variant").remove()}}});var T=function(af,ae,ah){c.call(this,af,ae,ah);var ag=j.get_random_color(),ad=j.get_random_color([ag,"#ffffff"]);this.config=new F({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block and sense strand color",type:"color",default_value:ag},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:ad},{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},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"}],saved_values:ah.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.request_draw({clear_tile_cache:true})}});this.prefs=this.config.values;this.painter=(af.reference_track?L.RefBasedReadPainter:L.ReadPainter);this.update_icons()};o(T.prototype,p.prototype,N.prototype,c.prototype);var d={CompositeTrack:e,DrawableGroup:P,DiagonalHeatmapTrack:r,FeatureTrack:c,LineTrack:g,ReadTrack:T,VariantTrack:Z,VcfTrack:Z};var n=function(af,ae,ad){if("copy" in af){return af.copy(ad)}else{var ag=af.obj_type;if(!ag){ag=af.track_type}return new d[ag](ae,ad,af)}};return{TracksterView:Y,DrawableGroup:P,LineTrack:g,FeatureTrack:c,DiagonalHeatmapTrack:r,ReadTrack:T,VariantTrack:Z,CompositeTrack:e,object_from_template:n}});
\ No newline at end of file
diff -r 78eaaca131e3251ba9cc78f7546ace6a713a4f02 -r f76c280cdee539b65315ac583f93bfa52feb790d static/scripts/packed/viz/trackster_ui.js
--- a/static/scripts/packed/viz/trackster_ui.js
+++ b/static/scripts/packed/viz/trackster_ui.js
@@ -1,1 +1,1 @@
-define(["base","libs/underscore","viz/trackster/slotting","viz/trackster/painters","viz/trackster/tracks","viz/visualization"],function(a,f,e,c,d,h){var j=d.object_from_template;var b=function(l,k){if(!k){k={}}var m=new IconButtonCollection(f.map(l,function(n){return new IconButton(f.extend(n,k))}));return new IconButtonMenuView({collection:m})};var g=a.Base.extend({initialize:function(k){this.baseURL=k},createButtonMenu:function(){var k=this,l=b([{icon_class:"plus-button",title:"Add tracks",on_click:function(){h.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":view.dbkey},function(m){f.each(m,function(n){view.add_drawable(j(n,view,view))})})}},{icon_class:"block--plus",title:"Add group",on_click:function(){view.add_drawable(new d.DrawableGroup(view,view,{name:"New Group"}))}},{icon_class:"bookmarks",title:"Bookmarks",on_click:function(){force_right_panel(($("div#right").css("right")=="0px"?"hide":"show"))}},{icon_class:"globe",title:"Circster",on_click:function(){window.location=k.baseURL+"visualization/circster?id="+view.vis_id}},{icon_class:"disk--arrow",title:"Save",on_click:function(){Galaxy.modal.show({title:"Saving...",body:"progress"});var m=[];$(".bookmark").each(function(){m.push({position:$(this).children(".position").text(),annotation:$(this).children(".annotation").text()})});var n=(view.overview_drawable?view.overview_drawable.name:null),o={view:view.to_dict(),viewport:{chrom:view.chrom,start:view.low,end:view.high,overview:n},bookmarks:m};$.ajax({url:galaxy_config.root+"visualization/save",type:"POST",dataType:"json",data:{id:view.vis_id,title:view.name,dbkey:view.dbkey,type:"trackster",vis_json:JSON.stringify(o)}}).success(function(p){Galaxy.modal.hide();view.vis_id=p.vis_id;view.has_changes=false;window.history.pushState({},"",p.url+window.location.hash)}).error(function(){Galaxy.modal.show({title:"Could Not Save",body:"Could not save visualization. Please try again later.",buttons:{Cancel:function(){Galaxy.modal.hide()}}})})}}],{tooltip_config:{placement:"bottom"}});this.buttonMenu=l;return l},add_bookmarks:function(){var k=this,l=this.baseURL;Galaxy.modal.show({title:"Select dataset for new bookmarks",body:"progress"});$.ajax({url:this.baseURL+"/visualization/list_histories",data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(m){Galaxy.modal.show({title:"Select dataset for new bookmarks",body:m,buttons:{Cancel:function(){Galaxy.modal.hide()},Insert:function(){$("input[name=id]:checked,input[name=ldda_ids]:checked").first().each(function(){var n,o=$(this).val();if($(this).attr("name")==="id"){n={hda_id:o}}else{n={ldda_id:o}}$.ajax({url:this.baseURL+"/visualization/bookmarks_from_dataset",data:n,dataType:"json"}).then(function(p){for(i=0;i<p.data.length;i++){var q=p.data[i];k.add_bookmark(q[0],q[1])}})});Galaxy.modal.hide()}}})}})},add_bookmark:function(o,m,k){var q=$("#right .unified-panel-body"),s=$("<div/>").addClass("bookmark").appendTo(q);var t=$("<div/>").addClass("position").appendTo(s),p=$("<a href=''/>").text(o).appendTo(t).click(function(){view.go_to(o);return false}),n=$("<div/>").text(m).appendTo(s);if(k){var r=$("<div/>").addClass("delete-icon-container").prependTo(s).click(function(){s.slideUp("fast");s.remove();view.has_changes=true;return false}),l=$("<a href=''/>").addClass("icon-button delete").appendTo(r);n.make_text_editable({num_rows:3,use_textarea:true,help_text:"Edit bookmark note"}).addClass("annotation")}view.has_changes=true;return s},create_visualization:function(p,k,o,q,n){var m=this,l=new d.TracksterView(p);l.editor=true;$.when(l.load_chroms_deferred).then(function(B){if(k){var z=k.chrom,r=k.start,w=k.end,t=k.overview;if(z&&(r!==undefined)&&w){l.change_chrom(z,r,w)}else{l.change_chrom(B[0].chrom)}}else{l.change_chrom(B[0].chrom)}if(o){var u,s,v;for(var x=0;x<o.length;x++){l.add_drawable(j(o[x],l,l))}}l.update_intro_div();var A;for(var x=0;x<l.drawables.length;x++){if(l.drawables[x].name===t){l.set_overview(l.drawables[x]);break}}if(q){var y;for(var x=0;x<q.length;x++){y=q[x];m.add_bookmark(y.position,y.annotation,n)}}l.has_changes=false});return l},init_keyboard_nav:function(k){$(document).keydown(function(l){if($(l.srcElement).is(":input")){return}switch(l.which){case 37:k.move_fraction(0.25);break;case 38:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()-20);break;case 39:k.move_fraction(-0.25);break;case 40:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()+20);break}})}});return{object_from_template:j,TracksterUI:g}});
\ No newline at end of file
+define(["base","libs/underscore","viz/trackster/slotting","viz/trackster/painters","viz/trackster/tracks","viz/visualization"],function(a,f,e,c,d,h){var j=d.object_from_template;var b=function(l,k){if(!k){k={}}var m=new IconButtonCollection(f.map(l,function(n){return new IconButton(f.extend(n,k))}));return new IconButtonMenuView({collection:m})};var g=a.Base.extend({initialize:function(k){this.baseURL=k},createButtonMenu:function(){var k=this,l=b([{icon_class:"plus-button",title:"Add tracks",on_click:function(){h.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":view.dbkey},function(m){f.each(m,function(n){view.add_drawable(j(n,view,view))})})}},{icon_class:"block--plus",title:"Add group",on_click:function(){view.add_drawable(new d.DrawableGroup(view,view,{name:"New Group"}))}},{icon_class:"bookmarks",title:"Bookmarks",on_click:function(){force_right_panel(($("div#right").css("right")=="0px"?"hide":"show"))}},{icon_class:"globe",title:"Circster",on_click:function(){window.location=k.baseURL+"visualization/circster?id="+view.vis_id}},{icon_class:"disk--arrow",title:"Save",on_click:function(){Galaxy.modal.show({title:"Saving...",body:"progress"});var m=[];$(".bookmark").each(function(){m.push({position:$(this).children(".position").text(),annotation:$(this).children(".annotation").text()})});var n=(view.overview_drawable?view.overview_drawable.name:null),o={view:view.to_dict(),viewport:{chrom:view.chrom,start:view.low,end:view.high,overview:n},bookmarks:m};$.ajax({url:galaxy_config.root+"visualization/save",type:"POST",dataType:"json",data:{id:view.vis_id,title:view.name,dbkey:view.dbkey,type:"trackster",vis_json:JSON.stringify(o)}}).success(function(p){Galaxy.modal.hide();view.vis_id=p.vis_id;view.has_changes=false;window.history.pushState({},"",p.url+window.location.hash)}).error(function(){Galaxy.modal.show({title:"Could Not Save",body:"Could not save visualization. Please try again later.",buttons:{Cancel:function(){Galaxy.modal.hide()}}})})}},{icon_class:"cross-circle",title:"Close",on_click:function(){window.location=galaxy_config.root+"visualization"}}],{tooltip_config:{placement:"bottom"}});this.buttonMenu=l;return l},add_bookmarks:function(){var k=this,l=this.baseURL;Galaxy.modal.show({title:"Select dataset for new bookmarks",body:"progress"});$.ajax({url:this.baseURL+"/visualization/list_histories",data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(m){Galaxy.modal.show({title:"Select dataset for new bookmarks",body:m,buttons:{Cancel:function(){Galaxy.modal.hide()},Insert:function(){$("input[name=id]:checked,input[name=ldda_ids]:checked").first().each(function(){var n,o=$(this).val();if($(this).attr("name")==="id"){n={hda_id:o}}else{n={ldda_id:o}}$.ajax({url:this.baseURL+"/visualization/bookmarks_from_dataset",data:n,dataType:"json"}).then(function(p){for(i=0;i<p.data.length;i++){var q=p.data[i];k.add_bookmark(q[0],q[1])}})});Galaxy.modal.hide()}}})}})},add_bookmark:function(o,m,k){var q=$("#right .unified-panel-body"),s=$("<div/>").addClass("bookmark").appendTo(q);var t=$("<div/>").addClass("position").appendTo(s),p=$("<a href=''/>").text(o).appendTo(t).click(function(){view.go_to(o);return false}),n=$("<div/>").text(m).appendTo(s);if(k){var r=$("<div/>").addClass("delete-icon-container").prependTo(s).click(function(){s.slideUp("fast");s.remove();view.has_changes=true;return false}),l=$("<a href=''/>").addClass("icon-button delete").appendTo(r);n.make_text_editable({num_rows:3,use_textarea:true,help_text:"Edit bookmark note"}).addClass("annotation")}view.has_changes=true;return s},create_visualization:function(p,k,o,q,n){var m=this,l=new d.TracksterView(p);l.editor=true;$.when(l.load_chroms_deferred).then(function(B){if(k){var z=k.chrom,r=k.start,w=k.end,t=k.overview;if(z&&(r!==undefined)&&w){l.change_chrom(z,r,w)}else{l.change_chrom(B[0].chrom)}}else{l.change_chrom(B[0].chrom)}if(o){var u,s,v;for(var x=0;x<o.length;x++){l.add_drawable(j(o[x],l,l))}}l.update_intro_div();var A;for(var x=0;x<l.drawables.length;x++){if(l.drawables[x].name===t){l.set_overview(l.drawables[x]);break}}if(q){var y;for(var x=0;x<q.length;x++){y=q[x];m.add_bookmark(y.position,y.annotation,n)}}l.has_changes=false});return l},init_keyboard_nav:function(k){$(document).keydown(function(l){if($(l.srcElement).is(":input")){return}switch(l.which){case 37:k.move_fraction(0.25);break;case 38:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()-20);break;case 39:k.move_fraction(-0.25);break;case 40:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()+20);break}})}});return{object_from_template:j,TracksterUI:g}});
\ 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: Dave Bouvier: Fix for update manager functional test failing.
by commits-noreply@bitbucket.org 07 Oct '13
by commits-noreply@bitbucket.org 07 Oct '13
07 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4db4660f5b12/
Changeset: 4db4660f5b12
User: Dave Bouvier
Date: 2013-10-07 22:46:17
Summary: Fix for update manager functional test failing.
Affected #: 2 files
diff -r f3bdde9002def4b09eae778dbd250d40512a30ac -r 4db4660f5b121eee69a822b9c1294e4316855715 test/tool_shed/base/twilltestcase.py
--- a/test/tool_shed/base/twilltestcase.py
+++ b/test/tool_shed/base/twilltestcase.py
@@ -1093,6 +1093,10 @@
self.visit_galaxy_url( url )
self.check_for_strings( strings_displayed, strings_not_displayed )
+ def update_tool_shed_status( self ):
+ url = '/admin_toolshed/update_tool_shed_status_for_installed_repository?all_installed_repositories=True'
+ self.visit_galaxy_url( url )
+
def upload_file( self,
repository,
filename,
diff -r f3bdde9002def4b09eae778dbd250d40512a30ac -r 4db4660f5b121eee69a822b9c1294e4316855715 test/tool_shed/functional/test_1410_update_manager.py
--- a/test/tool_shed/functional/test_1410_update_manager.py
+++ b/test/tool_shed/functional/test_1410_update_manager.py
@@ -121,5 +121,8 @@
time.sleep( 3 )
self.galaxy_logout()
self.galaxy_login( email=common.admin_email, username=common.admin_username )
- self.display_galaxy_browse_repositories_page( strings_displayed=[ 'state-color-running' ] )
+ repository = test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
+ self.update_tool_shed_status()
+ strings_displayed = [ '"%s"><img src="/static/images/icon_warning_sml.gif"' % self.security.encode_id( repository.id ) ]
+ self.display_galaxy_browse_repositories_page( strings_displayed=strings_displayed )
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: Fix javascript function to check all check boxes for select lists in the tool shed and Galaxy that handle resetting metadata on selected repositories and installing and uninstalling selected tool dependencies.
by commits-noreply@bitbucket.org 07 Oct '13
by commits-noreply@bitbucket.org 07 Oct '13
07 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/f3bdde9002de/
Changeset: f3bdde9002de
User: greg
Date: 2013-10-07 22:35:38
Summary: Fix javascript function to check all check boxes for select lists in the tool shed and Galaxy that handle resetting metadata on selected repositories and installing and uninstalling selected tool dependencies.
Affected #: 4 files
diff -r 09a383314143244d37b5d40a565e75b131dff2b5 -r f3bdde9002def4b09eae778dbd250d40512a30ac templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
--- a/templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
+++ b/templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
@@ -39,7 +39,8 @@
trans.model.ToolDependency.installation_status.UNINSTALLED ]:
can_install = True
if not can_uninstall:
- if tool_dependency.status not in [ trans.model.ToolDependency.installation_status.UNINSTALLED ]:
+ if tool_dependency.status not in [ trans.model.ToolDependency.installation_status.NEVER_INSTALLED,
+ trans.model.ToolDependency.installation_status.UNINSTALLED ]:
can_uninstall = True
%><tr>
@@ -67,7 +68,7 @@
</div><div style="clear: both"></div><div class="form-row">
- <input type="checkbox" id="checkAllUninstalled" name="select_all_uninstalled_tool_dependencies_checkbox" value="true" onclick="checkAllFields( 'uninstalled_tool_dependency_ids', 'checkAllUninstalled' );"/><input type="hidden" name="select_all_uninstalled_tool_dependencies_checkbox" value="true"/><b>Select/unselect all tool dependencies</b>
+ <input type="checkbox" id="checkAllUninstalled" name="select_all_uninstalled_tool_dependencies_checkbox" value="true" onclick="checkAllUninstalledToolDependencyIdFields(1);"/><input type="hidden" name="select_all_uninstalled_tool_dependencies_checkbox" value="true"/><b>Select/unselect all tool dependencies</b></div><div style="clear: both"></div><div class="form-row">
@@ -88,7 +89,7 @@
</div><div style="clear: both"></div><div class="form-row">
- <input type="checkbox" id="checkAllInstalled" name="select_all_installed_tool_dependencies_checkbox" value="true" onclick="checkAllFields( 'inst_td_ids', 'checkAllInstalled' );"/><input type="hidden" name="select_all_installed_tool_dependencies_checkbox" value="true"/><b>Select/unselect all tool dependencies</b>
+ <input type="checkbox" id="checkAllInstalled" name="select_all_installed_tool_dependencies_checkbox" value="true" onclick="checkAllInstalledToolDependencyIdFields(1);"/><input type="hidden" name="select_all_installed_tool_dependencies_checkbox" value="true"/><b>Select/unselect all tool dependencies</b></div><div style="clear: both"></div><div class="form-row">
diff -r 09a383314143244d37b5d40a565e75b131dff2b5 -r f3bdde9002def4b09eae778dbd250d40512a30ac templates/admin/tool_shed_repository/reset_metadata_on_selected_repositories.mako
--- a/templates/admin/tool_shed_repository/reset_metadata_on_selected_repositories.mako
+++ b/templates/admin/tool_shed_repository/reset_metadata_on_selected_repositories.mako
@@ -24,7 +24,7 @@
</div><div style="clear: both"></div><div class="form-row">
- <input type="checkbox" id="checkAll" name="select_all_repositories_checkbox" value="true" onclick="checkAllFields('repository_ids');"/><input type="hidden" name="select_all_repositories_checkbox" value="true"/><b>Select/unselect all repositories</b>
+ <input type="checkbox" id="checkAll" name="select_all_repositories_checkbox" value="true" onclick="checkAllRepositoryIdFields(1);"/><input type="hidden" name="select_all_repositories_checkbox" value="true"/><b>Select/unselect all repositories</b></div><div style="clear: both"></div><div class="form-row">
diff -r 09a383314143244d37b5d40a565e75b131dff2b5 -r f3bdde9002def4b09eae778dbd250d40512a30ac templates/webapps/tool_shed/common/common.mako
--- a/templates/webapps/tool_shed/common/common.mako
+++ b/templates/webapps/tool_shed/common/common.mako
@@ -1,15 +1,7 @@
-<%def name="common_misc_javascripts( element_id=None )">
+<%def name="common_misc_javascripts()"><script type="text/javascript">
- function checkAllFields( name, element_id=element_id )
+ function checkAllFields( chkAll, name )
{
- if ( element_id )
- {
- chkAll = document.getElementById( element_id );
- }
- else
- {
- var chkAll = document.getElementById( 'checkAll' );
- }
var checks = document.getElementsByTagName( 'input' );
var boxLength = checks.length;
var allChecked = false;
@@ -18,9 +10,9 @@
{
for ( i=0; i < boxLength; i++ )
{
- if ( checks[i].name.indexOf( name ) != -1 )
+ if ( checks[ i ].name.indexOf( name ) != -1 )
{
- checks[i].checked = true;
+ checks[ i ].checked = true;
}
}
}
@@ -28,13 +20,34 @@
{
for ( i=0; i < boxLength; i++ )
{
- if ( checks[i].name.indexOf( name ) != -1 )
+ if ( checks[ i ].name.indexOf( name ) != -1 )
{
- checks[i].checked = false
+ checks[ i ].checked = false;
}
}
}
}
+
+ function checkAllRepositoryIdFields()
+ {
+ var chkAll = document.getElementById( 'checkAll' );
+ var name = 'repository_ids';
+ checkAllFields( chkAll, name );
+ }
+
+ function checkAllInstalledToolDependencyIdFields()
+ {
+ var chkAll = document.getElementById( 'checkAllInstalled' );
+ var name = 'inst_td_ids';
+ checkAllFields( chkAll, name );
+ }
+
+ function checkAllUninstalledToolDependencyIdFields()
+ {
+ var chkAll = document.getElementById( 'checkAllUninstalled' );
+ var name = 'uninstalled_tool_dependency_ids';
+ checkAllFields( chkAll, name );
+ }
</script></%def>
diff -r 09a383314143244d37b5d40a565e75b131dff2b5 -r f3bdde9002def4b09eae778dbd250d40512a30ac templates/webapps/tool_shed/common/reset_metadata_on_selected_repositories.mako
--- a/templates/webapps/tool_shed/common/reset_metadata_on_selected_repositories.mako
+++ b/templates/webapps/tool_shed/common/reset_metadata_on_selected_repositories.mako
@@ -33,7 +33,7 @@
</div><div style="clear: both"></div><div class="form-row">
- <input type="checkbox" id="checkAll" name="select_all_repositories_checkbox" value="true" onclick="checkAllFields('repository_ids');"/><input type="hidden" name="select_all_repositories_checkbox" value="true"/><b>Select/unselect all repositories</b>
+ <input type="checkbox" id="checkAll" name="select_all_repositories_checkbox" value="true" onclick="checkAllRepositoryIdFields(1);"/><input type="hidden" name="select_all_repositories_checkbox" value="true"/><b>Select/unselect all repositories</b></div><div style="clear: both"></div><div class="form-row">
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 auto-detect metadata tool running second set metadata call.
by commits-noreply@bitbucket.org 07 Oct '13
by commits-noreply@bitbucket.org 07 Oct '13
07 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/09a383314143/
Changeset: 09a383314143
User: dan
Date: 2013-10-07 22:27:08
Summary: Fix for auto-detect metadata tool running second set metadata call.
Affected #: 3 files
diff -r 3679add0809c669e752c913ade2fc8e45adcff46 -r 09a383314143244d37b5d40a565e75b131dff2b5 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -1495,6 +1495,12 @@
just copy these files directly to the ulimate destination.
"""
return output_path
+
+ @property
+ def requires_setting_metadata( self ):
+ if self.tool:
+ return self.tool.requires_setting_metadata
+ return False
class TaskWrapper(JobWrapper):
diff -r 3679add0809c669e752c913ade2fc8e45adcff46 -r 09a383314143244d37b5d40a565e75b131dff2b5 lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -176,7 +176,7 @@
# Append metadata setting commands, we don't want to overwrite metadata
# that was copied over in init_meta(), as per established behavior
- if include_metadata:
+ if include_metadata and job_wrapper.requires_setting_metadata:
commands += "; cd %s; " % os.path.abspath( os.getcwd() )
commands += job_wrapper.setup_external_metadata(
exec_dir = os.path.abspath( os.getcwd() ),
diff -r 3679add0809c669e752c913ade2fc8e45adcff46 -r 09a383314143244d37b5d40a565e75b131dff2b5 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -971,6 +971,7 @@
"""
tool_type = 'default'
+ requires_setting_metadata = True
default_tool_action = DefaultToolAction
dict_collection_visible_keys = ( 'id', 'name', 'version', 'description' )
@@ -3134,6 +3135,8 @@
dataset.
"""
tool_type = 'set_metadata'
+ requires_setting_metadata = False
+
def exec_after_process( self, app, inp_data, out_data, param_dict, job = None ):
for name, dataset in inp_data.iteritems():
external_metadata = JobExternalOutputMetadataWrapper( job )
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/74e5b4de14e4/
Changeset: 74e5b4de14e4
User: jgoecks
Date: 2013-10-07 18:36:34
Summary: Add close button to Trackster.
Affected #: 1 file
diff -r 16e3de8d3f13e700ae1cc02d0e9146b8ea351155 -r 74e5b4de14e4db132d196de1d18eaaca6e82a59e static/scripts/viz/trackster_ui.js
--- a/static/scripts/viz/trackster_ui.js
+++ b/static/scripts/viz/trackster_ui.js
@@ -108,7 +108,14 @@
buttons : { "Cancel": function() { Galaxy.modal.hide() } }
});
});
- } }
+ } },
+ {
+ icon_class: 'cross-circle',
+ title: 'Close',
+ on_click: function() {
+ window.location = galaxy_config.root + 'visualization';
+ }
+ }
],
{
tooltip_config: { placement: 'bottom' }
https://bitbucket.org/galaxy/galaxy-central/commits/3679add0809c/
Changeset: 3679add0809c
User: jgoecks
Date: 2013-10-07 22:17:45
Summary: Automated merge
Affected #: 3 files
diff -r 74e5b4de14e4db132d196de1d18eaaca6e82a59e -r 3679add0809c669e752c913ade2fc8e45adcff46 lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -148,7 +148,8 @@
"""Method called from Galaxy to generate the current list of tool dependency ids for an installed tool shed repository."""
tool_dependencies_select_field = SelectField( name=name, multiple=multiple, display=display )
for tool_dependency in tool_shed_repository.tool_dependencies:
- if uninstalled and tool_dependency.status != trans.model.ToolDependency.installation_status.UNINSTALLED:
+ if uninstalled and tool_dependency.status not in [ trans.model.ToolDependency.installation_status.NEVER_INSTALLED,
+ trans.model.ToolDependency.installation_status.UNINSTALLED ]:
continue
option_label = '%s version %s' % ( str( tool_dependency.name ), str( tool_dependency.version ) )
option_value = trans.security.encode_id( tool_dependency.id )
diff -r 74e5b4de14e4db132d196de1d18eaaca6e82a59e -r 3679add0809c669e752c913ade2fc8e45adcff46 templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
--- a/templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
+++ b/templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
@@ -35,7 +35,8 @@
else:
error_message = ''
if not can_install:
- if tool_dependency.status in [ trans.model.ToolDependency.installation_status.UNINSTALLED ]:
+ if tool_dependency.status in [ trans.model.ToolDependency.installation_status.NEVER_INSTALLED,
+ trans.model.ToolDependency.installation_status.UNINSTALLED ]:
can_install = True
if not can_uninstall:
if tool_dependency.status not in [ trans.model.ToolDependency.installation_status.UNINSTALLED ]:
diff -r 74e5b4de14e4db132d196de1d18eaaca6e82a59e -r 3679add0809c669e752c913ade2fc8e45adcff46 test/install_and_test_tool_shed_repositories/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/functional_tests.py
@@ -275,6 +275,7 @@
def extract_log_data( test_result, from_tool_test=True ):
'''Extract any useful data from the test_result.failures and test_result.errors attributes.'''
+ log_data = []
for failure in test_result.failures + test_result.errors:
# Record the twill test identifier and information about the tool, so the repository owner can discover which test is failing.
test_id = str( failure[0] )
@@ -314,7 +315,8 @@
for output_type in [ 'stderr', 'traceback' ]:
if output_type in tmp_output:
test_status[ output_type ] = '\n'.join( tmp_output[ output_type ] )
- return test_status
+ log_data.append( test_status )
+ return log_data
def get_api_url( base, parts=[], params=None, key=None ):
if 'api' in parts and parts.index( 'api' ) != 0:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: Dave Bouvier: Make extract_log_data return a list of dicts rather than a dict.
by commits-noreply@bitbucket.org 07 Oct '13
by commits-noreply@bitbucket.org 07 Oct '13
07 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/6b0c3592dde1/
Changeset: 6b0c3592dde1
User: Dave Bouvier
Date: 2013-10-07 20:28:43
Summary: Make extract_log_data return a list of dicts rather than a dict.
Affected #: 1 file
diff -r 592d6dd40cd24eaf30aad2554c104c46144fb6c2 -r 6b0c3592dde1f816a49645353ca1adabb37fe89c test/install_and_test_tool_shed_repositories/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/functional_tests.py
@@ -275,6 +275,7 @@
def extract_log_data( test_result, from_tool_test=True ):
'''Extract any useful data from the test_result.failures and test_result.errors attributes.'''
+ log_data = []
for failure in test_result.failures + test_result.errors:
# Record the twill test identifier and information about the tool, so the repository owner can discover which test is failing.
test_id = str( failure[0] )
@@ -314,7 +315,8 @@
for output_type in [ 'stderr', 'traceback' ]:
if output_type in tmp_output:
test_status[ output_type ] = '\n'.join( tmp_output[ output_type ] )
- return test_status
+ log_data.append( test_status )
+ return log_data
def get_api_url( base, parts=[], params=None, key=None ):
if 'api' in parts and parts.index( 'api' ) != 0:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Allow tool dependencies with a status of NEVER_INSTALLED to be installed.
by commits-noreply@bitbucket.org 07 Oct '13
by commits-noreply@bitbucket.org 07 Oct '13
07 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/592d6dd40cd2/
Changeset: 592d6dd40cd2
User: greg
Date: 2013-10-07 18:48:25
Summary: Allow tool dependencies with a status of NEVER_INSTALLED to be installed.
Affected #: 2 files
diff -r 16e3de8d3f13e700ae1cc02d0e9146b8ea351155 -r 592d6dd40cd24eaf30aad2554c104c46144fb6c2 lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -148,7 +148,8 @@
"""Method called from Galaxy to generate the current list of tool dependency ids for an installed tool shed repository."""
tool_dependencies_select_field = SelectField( name=name, multiple=multiple, display=display )
for tool_dependency in tool_shed_repository.tool_dependencies:
- if uninstalled and tool_dependency.status != trans.model.ToolDependency.installation_status.UNINSTALLED:
+ if uninstalled and tool_dependency.status not in [ trans.model.ToolDependency.installation_status.NEVER_INSTALLED,
+ trans.model.ToolDependency.installation_status.UNINSTALLED ]:
continue
option_label = '%s version %s' % ( str( tool_dependency.name ), str( tool_dependency.version ) )
option_value = trans.security.encode_id( tool_dependency.id )
diff -r 16e3de8d3f13e700ae1cc02d0e9146b8ea351155 -r 592d6dd40cd24eaf30aad2554c104c46144fb6c2 templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
--- a/templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
+++ b/templates/admin/tool_shed_repository/manage_repository_tool_dependencies.mako
@@ -35,7 +35,8 @@
else:
error_message = ''
if not can_install:
- if tool_dependency.status in [ trans.model.ToolDependency.installation_status.UNINSTALLED ]:
+ if tool_dependency.status in [ trans.model.ToolDependency.installation_status.NEVER_INSTALLED,
+ trans.model.ToolDependency.installation_status.UNINSTALLED ]:
can_install = True
if not can_uninstall:
if tool_dependency.status not in [ trans.model.ToolDependency.installation_status.UNINSTALLED ]:
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: Revert addition GALAXY_RUN_ALL=1 in run.sh
by commits-noreply@bitbucket.org 07 Oct '13
by commits-noreply@bitbucket.org 07 Oct '13
07 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/16e3de8d3f13/
Changeset: 16e3de8d3f13
User: dannon
Date: 2013-10-07 17:14:25
Summary: Revert addition GALAXY_RUN_ALL=1 in run.sh
Affected #: 1 file
diff -r 51bcb9cdb8bd109e5b89eac5c2ad83d6f3e3e0fc -r 16e3de8d3f13e700ae1cc02d0e9146b8ea351155 run.sh
--- a/run.sh
+++ b/run.sh
@@ -1,7 +1,6 @@
#!/bin/sh
cd `dirname $0`
-export GALAXY_RUN_ALL=1
python ./scripts/check_python.py
[ $? -ne 0 ] && exit 1
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Fix setting mimetype for image files in tool shed repositories.
by commits-noreply@bitbucket.org 07 Oct '13
by commits-noreply@bitbucket.org 07 Oct '13
07 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/51bcb9cdb8bd/
Changeset: 51bcb9cdb8bd
User: greg
Date: 2013-10-07 16:32:33
Summary: Fix setting mimetype for image files in tool shed repositories.
Affected #: 2 files
diff -r 656b965c5d030b7a6b61b727380a8141c12c2c37 -r 51bcb9cdb8bd109e5b89eac5c2ad83d6f3e3e0fc lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -306,9 +306,14 @@
path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
if os.path.exists( path_to_file ):
file_name = os.path.basename( relative_path_to_image_file )
- mimetype = trans.app.datatypes_registry.get_mimetype_by_extension( file_name, None )
- if mimetype:
- trans.response.set_content_type( mimetype )
+ try:
+ extension = file_name.split( '.' )[ -1 ]
+ except Exception, e:
+ extension = None
+ if extension:
+ mimetype = trans.app.datatypes_registry.get_mimetype_by_extension( extension )
+ if mimetype:
+ trans.response.set_content_type( mimetype )
return open( path_to_file, 'r' )
return None
diff -r 656b965c5d030b7a6b61b727380a8141c12c2c37 -r 51bcb9cdb8bd109e5b89eac5c2ad83d6f3e3e0fc lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -1038,9 +1038,14 @@
path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
if os.path.exists( path_to_file ):
file_name = os.path.basename( relative_path_to_image_file )
- mimetype = trans.app.datatypes_registry.get_mimetype_by_extension( file_name, None )
- if mimetype:
- trans.response.set_content_type( mimetype )
+ try:
+ extension = file_name.split( '.' )[ -1 ]
+ except Exception, e:
+ extension = None
+ if extension:
+ mimetype = trans.app.datatypes_registry.get_mimetype_by_extension( extension )
+ if mimetype:
+ trans.response.set_content_type( mimetype )
return open( path_to_file, 'r' )
return None
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: Set mimetype when displaying images in tool shed repositories.
by commits-noreply@bitbucket.org 07 Oct '13
by commits-noreply@bitbucket.org 07 Oct '13
07 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/656b965c5d03/
Changeset: 656b965c5d03
User: greg
Date: 2013-10-07 16:14:42
Summary: Set mimetype when displaying images in tool shed repositories.
Affected #: 2 files
diff -r 1dee9c665f38ff06e4fd77e90d0d62b2c080182a -r 656b965c5d030b7a6b61b727380a8141c12c2c37 lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -305,6 +305,10 @@
repo_files_dir = repository.repo_files_directory( trans.app )
path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
if os.path.exists( path_to_file ):
+ file_name = os.path.basename( relative_path_to_image_file )
+ mimetype = trans.app.datatypes_registry.get_mimetype_by_extension( file_name, None )
+ if mimetype:
+ trans.response.set_content_type( mimetype )
return open( path_to_file, 'r' )
return None
diff -r 1dee9c665f38ff06e4fd77e90d0d62b2c080182a -r 656b965c5d030b7a6b61b727380a8141c12c2c37 lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -1037,6 +1037,10 @@
repo_files_dir = repository.repo_path( trans.app )
path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
if os.path.exists( path_to_file ):
+ file_name = os.path.basename( relative_path_to_image_file )
+ mimetype = trans.app.datatypes_registry.get_mimetype_by_extension( file_name, None )
+ if mimetype:
+ trans.response.set_content_type( mimetype )
return open( path_to_file, 'r' )
return None
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: Dave Bouvier: Reimplement fix for some tarballs that incorrectly result in an error state when they attempt to create symlinks to files that do not exist on the destination filesystem.
by commits-noreply@bitbucket.org 07 Oct '13
by commits-noreply@bitbucket.org 07 Oct '13
07 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/1dee9c665f38/
Changeset: 1dee9c665f38
User: Dave Bouvier
Date: 2013-10-07 14:59:31
Summary: Reimplement fix for some tarballs that incorrectly result in an error state when they attempt to create symlinks to files that do not exist on the destination filesystem.
Affected #: 1 file
diff -r 31ebf3e3d90f191d1df87b9dbf4c943f4a7b7b03 -r 1dee9c665f38ff06e4fd77e90d0d62b2c080182a lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
@@ -114,7 +114,7 @@
return False
def open_tar( self, filepath, mode ):
- return tarfile.open( filepath, mode )
+ return tarfile.open( filepath, mode, errorlevel=0 )
def open_zip( self, filepath, mode ):
return zipfile.ZipFile( filepath, mode )
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
12 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/1092b0d54bf6/
Changeset: 1092b0d54bf6
User: ross...(a)gmail.com
Date: 2013-08-04 09:44:45
Summary: update bowtie2 wrapper
Affected #: 3 files
diff -r 8ca6cfea19091f0d44be3eec3b3d18794133a39a -r 1092b0d54bf690b9d148276bcb14baae80f429e2 tools/ngs_rna/tophat2_wrapper.xml
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -273,19 +273,19 @@
</stdio><outputs>
- <data format="tabular" name="fusions" label="${tool.name} on ${on_string}: fusions" from_work_dir="tophat_out/fusions.out">
+ <data format="tabular" name="fusions" label="${on_string}_tophat2fus.xls" from_work_dir="tophat_out/fusions.out"><filter>(params['settingsType'] == 'full' and params['fusion_search']['do_search'] == 'Yes')</filter></data>
- <data format="bed" name="insertions" label="${tool.name} on ${on_string}: insertions" from_work_dir="tophat_out/insertions.bed">
+ <data format="bed" name="insertions" label="${on_string}_tophat2ins.bed" from_work_dir="tophat_out/insertions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="deletions" label="${tool.name} on ${on_string}: deletions" from_work_dir="tophat_out/deletions.bed">
+ <data format="bed" name="deletions" label="${on_string}_tophhat2del.bed" from_work_dir="tophat_out/deletions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="junctions" label="${tool.name} on ${on_string}: splice junctions" from_work_dir="tophat_out/junctions.bed">
+ <data format="bed" name="junctions" label="${on_string}tophat2sj.bed" from_work_dir="tophat_out/junctions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bam" name="accepted_hits" label="${tool.name} on ${on_string}: accepted_hits" from_work_dir="tophat_out/accepted_hits.bam">
+ <data format="bam" name="accepted_hits" label="${on_string}tophat2hits.bam" from_work_dir="tophat_out/accepted_hits.bam"><expand macro="dbKeyActions" /></data></outputs>
diff -r 8ca6cfea19091f0d44be3eec3b3d18794133a39a -r 1092b0d54bf690b9d148276bcb14baae80f429e2 tools/rgenetics/rgFastQC.py
--- a/tools/rgenetics/rgFastQC.py
+++ b/tools/rgenetics/rgFastQC.py
@@ -27,6 +27,7 @@
from rgutils import getFileString
import zipfile
import gzip
+import bz2
class FastQC():
"""wrapper
@@ -62,27 +63,43 @@
infname = self.opts.inputfilename
linf = infname.lower()
trimext = False
+ isgz = False
+ isbz2 = False
+ iszip = False
# decompression at upload currently does NOT remove this now bogus ending - fastqc will barf
# patched may 29 2013 until this is fixed properly
- if ( linf.endswith('.gz') or linf.endswith('.gzip') ):
- f = gzip.open(self.opts.input)
- try:
+ f = gzip.open(self.opts.input)
+ try:
testrow = f.readline()
- except:
- trimext = True
- f.close()
- elif linf.endswith('bz2'):
- f = bz2.open(self.opts.input,'rb')
- try:
- f.readline()
- except:
- trimext = True
- f.close()
- elif linf.endswith('.zip'):
- if not zipfile.is_zipfile(self.opts.input):
- trimext = True
+ if not (linf.endswith('.gz') or linf.endswith('.gzip')):
+ isgz = True
+ except:
+ if linf.endswith('.gz') or linf.endswith('.gzip'):
+ trimext = True
+ f.close()
+ f = bz2.BZ2File(self.opts.input)
+ try:
+ x = read(f,2) # will work if a real bz2
+ if not linf.endswith('bz2'):
+ isbz2 = True
+ except:
+ if linf.endswith('bz2'):
+ trimext = True
+ f.close()
+ if zipfile.is_zipfile(self.opts.input):
+ if not linf.endswith('.zip'):
+ iszip = True
+ else:
+ if linf.endswith('.zip'):
+ trimext = True
if trimext:
infname = os.path.splitext(infname)[0]
+ elif isgz:
+ infname = '%s.gz' % infname
+ elif isbz2:
+ infname = '%s.bzip2' % infname
+ elif iszip:
+ infname = '%s.zip' % infname
fastqinfilename = re.sub(ur'[^a-zA-Z0-9_\-\.]', '_', os.path.basename(infname))
link_name = os.path.join(self.opts.outputdir, fastqinfilename)
os.symlink(self.opts.input, link_name)
diff -r 8ca6cfea19091f0d44be3eec3b3d18794133a39a -r 1092b0d54bf690b9d148276bcb14baae80f429e2 tools/sr_mapping/bowtie2_wrapper.xml
--- a/tools/sr_mapping/bowtie2_wrapper.xml
+++ b/tools/sr_mapping/bowtie2_wrapper.xml
@@ -113,6 +113,8 @@
</stdio><inputs>
+ <param name="jobtitle" type="text" value="bowtie2" label="Memorable short reminder of this job's importance for outputs" />
+
<!-- single/paired --><conditional name="library"><param name="type" type="select" label="Is this library mate-paired?">
@@ -216,7 +218,7 @@
<!-- define outputs --><outputs>
- <data format="fastqsanger" name="output_unaligned_reads_l" label="${tool.name} on ${on_string}: unaligned reads (L)" >
+ <data format="fastqsanger" name="output_unaligned_reads_l" label="${on_string}_{jobtitle}_unaligL.fastq" ><filter>unaligned_file is True</filter><actions><action type="format">
@@ -224,7 +226,7 @@
</action></actions></data>
- <data format="fastqsanger" name="output_unaligned_reads_r" label="${tool.name} on ${on_string}: unaligned reads (R)">
+ <data format="fastqsanger" name="output_unaligned_reads_r" label="${on_string}_{jobtitle}_unaligR.fastq)"><filter>library['type'] == "paired" and unaligned_file is True</filter><actions><action type="format">
@@ -232,7 +234,7 @@
</action></actions></data>
- <data format="bam" name="output" label="${tool.name} on ${on_string}: aligned reads">
+ <data format="bam" name="output" label="${tool.name} on ${on_string}_{jobtitle}_aligned.bam"><actions><conditional name="reference_genome.source"><when value="indexed">
https://bitbucket.org/galaxy/galaxy-central/commits/f0cdc6bebfaa/
Changeset: f0cdc6bebfaa
User: ross...(a)gmail.com
Date: 2013-08-04 09:48:20
Summary: more baker stuff
Affected #: 191 files
diff -r 1092b0d54bf690b9d148276bcb14baae80f429e2 -r f0cdc6bebfaa8757bb093d74508332515d5eec38 tools/rgedgeR/rgToolFactory.py
--- /dev/null
+++ b/tools/rgedgeR/rgToolFactory.py
@@ -0,0 +1,605 @@
+# rgToolFactory.py
+# see https://bitbucket.org/fubar/galaxytoolfactory/wiki/Home
+#
+# copyright ross lazarus (ross stop lazarus at gmail stop com) May 2012
+#
+# all rights reserved
+# Licensed under the LGPL
+# suggestions for improvement and bug fixes welcome at https://bitbucket.org/fubar/galaxytoolfactory/wiki/Home
+#
+# july 2013
+# added ability to combine images and individual log files into html output
+# just make sure there's a log file foo.log and it will be output
+# together with all images named like "foo_*.pdf
+# otherwise old format for html
+#
+# January 2013
+# problem pointed out by Carlos Borroto
+# added escaping for <>$ - thought I did that ages ago...
+#
+# August 11 2012
+# changed to use shell=False and cl as a sequence
+
+# This is a Galaxy tool factory for simple scripts in python, R or whatever ails ye.
+# It also serves as the wrapper for the new tool.
+#
+# you paste and run your script
+# Only works for simple scripts that read one input from the history.
+# Optionally can write one new history dataset,
+# and optionally collect any number of outputs into links on an autogenerated HTML page.
+
+# DO NOT install on a public or important site - please.
+
+# installed generated tools are fine if the script is safe.
+# They just run normally and their user cannot do anything unusually insecure
+# but please, practice safe toolshed.
+# Read the fucking code before you install any tool
+# especially this one
+
+# After you get the script working on some test data, you can
+# optionally generate a toolshed compatible gzip file
+# containing your script safely wrapped as an ordinary Galaxy script in your local toolshed for
+# safe and largely automated installation in a production Galaxy.
+
+# If you opt for an HTML output, you get all the script outputs arranged
+# as a single Html history item - all output files are linked, thumbnails for all the pdfs.
+# Ugly but really inexpensive.
+#
+# Patches appreciated please.
+#
+#
+# long route to June 2012 product
+# Behold the awesome power of Galaxy and the toolshed with the tool factory to bind them
+# derived from an integrated script model
+# called rgBaseScriptWrapper.py
+# Note to the unwary:
+# This tool allows arbitrary scripting on your Galaxy as the Galaxy user
+# There is nothing stopping a malicious user doing whatever they choose
+# Extremely dangerous!!
+# Totally insecure. So, trusted users only
+#
+# preferred model is a developer using their throw away workstation instance - ie a private site.
+# no real risk. The universe_wsgi.ini admin_users string is checked - only admin users are permitted to run this tool.
+#
+
+import sys
+import shutil
+import subprocess
+import os
+import time
+import tempfile
+import optparse
+import tarfile
+import re
+import shutil
+import math
+
+progname = os.path.split(sys.argv[0])[1]
+myversion = 'V000.2 June 2012'
+verbose = False
+debug = False
+toolFactoryURL = 'https://bitbucket.org/fubar/galaxytoolfactory'
+
+def timenow():
+ """return current time as a string
+ """
+ return time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(time.time()))
+
+html_escape_table = {
+ "&": "&",
+ ">": ">",
+ "<": "<",
+ "$": "\$"
+ }
+
+def html_escape(text):
+ """Produce entities within text."""
+ return "".join(html_escape_table.get(c,c) for c in text)
+
+def cmd_exists(cmd):
+ return subprocess.call("type " + cmd, shell=True,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
+
+
+class ScriptRunner:
+ """class is a wrapper for an arbitrary script
+ """
+
+ def __init__(self,opts=None,treatbashSpecial=True):
+ """
+ cleanup inputs, setup some outputs
+
+ """
+ self.useGM = cmd_exists('gm')
+ self.useIM = cmd_exists('convert')
+ self.useGS = cmd_exists('gs')
+ self.treatbashSpecial = treatbashSpecial
+ if opts.output_dir: # simplify for the tool tarball
+ os.chdir(opts.output_dir)
+ self.thumbformat = 'png'
+ self.opts = opts
+ self.toolname = re.sub('[^a-zA-Z0-9_]+', '', opts.tool_name) # a sanitizer now does this but..
+ self.toolid = self.toolname
+ self.myname = sys.argv[0] # get our name because we write ourselves out as a tool later
+ self.pyfile = self.myname # crude but efficient - the cruft won't hurt much
+ self.xmlfile = '%s.xml' % self.toolname
+ s = open(self.opts.script_path,'r').readlines()
+ s = [x.rstrip() for x in s] # remove pesky dos line endings if needed
+ self.script = '\n'.join(s)
+ fhandle,self.sfile = tempfile.mkstemp(prefix=self.toolname,suffix=".%s" % (opts.interpreter))
+ tscript = open(self.sfile,'w') # use self.sfile as script source for Popen
+ tscript.write(self.script)
+ tscript.close()
+ self.indentedScript = '\n'.join([' %s' % x for x in s]) # for restructured text in help
+ self.escapedScript = '\n'.join([html_escape(x) for x in s])
+ self.elog = os.path.join(self.opts.output_dir,"%s_error.log" % self.toolname)
+ if opts.output_dir: # may not want these complexities
+ self.tlog = os.path.join(self.opts.output_dir,"%s_runner.log" % self.toolname)
+ art = '%s.%s' % (self.toolname,opts.interpreter)
+ artpath = os.path.join(self.opts.output_dir,art) # need full path
+ artifact = open(artpath,'w') # use self.sfile as script source for Popen
+ artifact.write(self.script)
+ artifact.close()
+ self.cl = []
+ self.html = []
+ a = self.cl.append
+ a(opts.interpreter)
+ if self.treatbashSpecial and opts.interpreter in ['bash','sh']:
+ a(self.sfile)
+ else:
+ a('-') # stdin
+ a(opts.input_tab)
+ a(opts.output_tab)
+ self.outFormats = 'tabular' # TODO make this an option at tool generation time
+ self.inputFormats = 'tabular' # TODO make this an option at tool generation time
+ self.test1Input = '%s_test1_input.xls' % self.toolname
+ self.test1Output = '%s_test1_output.xls' % self.toolname
+ self.test1HTML = '%s_test1_output.html' % self.toolname
+
+ def makeXML(self):
+ """
+ Create a Galaxy xml tool wrapper for the new script as a string to write out
+ fixme - use templating or something less fugly than this example of what we produce
+
+ <tool id="reverse" name="reverse" version="0.01">
+ <description>a tabular file</description>
+ <command interpreter="python">
+ reverse.py --script_path "$runMe" --interpreter "python"
+ --tool_name "reverse" --input_tab "$input1" --output_tab "$tab_file"
+ </command>
+ <inputs>
+ <param name="input1" type="data" format="tabular" label="Select a suitable input file from your history"/><param name="job_name" type="text" label="Supply a name for the outputs to remind you what they contain" value="reverse"/>
+
+ </inputs>
+ <outputs>
+ <data format="tabular" name="tab_file" label="${job_name}"/>
+
+ </outputs>
+ <help>
+
+**What it Does**
+
+Reverse the columns in a tabular file
+
+ </help>
+ <configfiles>
+ <configfile name="runMe">
+
+# reverse order of columns in a tabular file
+import sys
+inp = sys.argv[1]
+outp = sys.argv[2]
+i = open(inp,'r')
+o = open(outp,'w')
+for row in i:
+ rs = row.rstrip().split('\t')
+ rs.reverse()
+ o.write('\t'.join(rs))
+ o.write('\n')
+i.close()
+o.close()
+
+
+ </configfile>
+ </configfiles>
+ </tool>
+
+ """
+ newXML="""<tool id="%(toolid)s" name="%(toolname)s" version="%(tool_version)s">
+ %(tooldesc)s
+ %(command)s
+ <inputs>
+ %(inputs)s
+ </inputs>
+ <outputs>
+ %(outputs)s
+ </outputs>
+ <configfiles>
+ <configfile name="runMe">
+ %(script)s
+ </configfile>
+ </configfiles>
+ %(tooltests)s
+ <help>
+ %(help)s
+ </help>
+ </tool>""" # needs a dict with toolname, toolid, interpreter, scriptname, command, inputs as a multi line string ready to write, outputs ditto, help ditto
+
+ newCommand="""<command interpreter="python">
+ %(toolname)s.py --script_path "$runMe" --interpreter "%(interpreter)s"
+ --tool_name "%(toolname)s" %(command_inputs)s %(command_outputs)s
+ </command>""" # may NOT be an input or htmlout
+ tooltestsTabOnly = """<tests><test>
+ <param name="input1" value="%(test1Input)s" ftype="tabular"/>
+ <param name="job_name" value="test1"/>
+ <param name="runMe" value="$runMe"/>
+ <output name="tab_file" file="%(test1Output)s" ftype="tabular"/>
+ </test></tests>"""
+ tooltestsHTMLOnly = """<tests><test>
+ <param name="input1" value="%(test1Input)s" ftype="tabular"/>
+ <param name="job_name" value="test1"/>
+ <param name="runMe" value="$runMe"/>
+ <output name="html_file" file="%(test1HTML)s" ftype="html" lines_diff="5"/>
+ </test></tests>"""
+ tooltestsBoth = """<tests><test>
+ <param name="input1" value="%(test1Input)s" ftype="tabular"/>
+ <param name="job_name" value="test1"/>
+ <param name="runMe" value="$runMe"/>
+ <output name="tab_file" file="%(test1Output)s" ftype="tabular" />
+ <output name="html_file" file="%(test1HTML)s" ftype="html" lines_diff="10"/>
+ </test></tests>"""
+ xdict = {}
+ xdict['tool_version'] = self.opts.tool_version
+ xdict['test1Input'] = self.test1Input
+ xdict['test1HTML'] = self.test1HTML
+ xdict['test1Output'] = self.test1Output
+ if self.opts.make_HTML and self.opts.output_tab <> 'None':
+ xdict['tooltests'] = tooltestsBoth % xdict
+ elif self.opts.make_HTML:
+ xdict['tooltests'] = tooltestsHTMLOnly % xdict
+ else:
+ xdict['tooltests'] = tooltestsTabOnly % xdict
+ xdict['script'] = self.escapedScript
+ # configfile is least painful way to embed script to avoid external dependencies
+ # but requires escaping of <, > and $ to avoid Mako parsing
+ if self.opts.help_text:
+ xdict['help'] = open(self.opts.help_text,'r').read()
+ else:
+ xdict['help'] = 'Please ask the tool author for help as none was supplied at tool generation'
+ coda = ['**Script**','Pressing execute will run the following code over your input file and generate some outputs in your history::']
+ coda.append(self.indentedScript)
+ coda.append('**Attribution** This Galaxy tool was created by %s at %s\nusing the Galaxy Tool Factory.' % (self.opts.user_email,timenow()))
+ coda.append('See %s for details of that project' % (toolFactoryURL))
+ coda.append('Please cite: Creating re-usable tools from scripts: The Galaxy Tool Factory. Ross Lazarus; Antony Kaspi; Mark Ziemann; The Galaxy Team. ')
+ coda.append('Bioinformatics 2012; doi: 10.1093/bioinformatics/bts573')
+ xdict['help'] = '%s\n%s' % (xdict['help'],'\n'.join(coda))
+ if self.opts.tool_desc:
+ xdict['tooldesc'] = '<description>%s</description>' % self.opts.tool_desc
+ else:
+ xdict['tooldesc'] = ''
+ xdict['command_outputs'] = ''
+ xdict['outputs'] = ''
+ if self.opts.input_tab <> 'None':
+ xdict['command_inputs'] = '--input_tab "$input1" ' # the space may matter a lot if we append something
+ xdict['inputs'] = '<param name="input1" type="data" format="%s" label="Select a suitable input file from your history"/> \n' % self.inputFormats
+ else:
+ xdict['command_inputs'] = '' # assume no input - eg a random data generator
+ xdict['inputs'] = ''
+ xdict['inputs'] += '<param name="job_name" type="text" label="Supply a name for the outputs to remind you what they contain" value="%s"/> \n' % self.toolname
+ xdict['toolname'] = self.toolname
+ xdict['toolid'] = self.toolid
+ xdict['interpreter'] = self.opts.interpreter
+ xdict['scriptname'] = self.sfile
+ if self.opts.make_HTML:
+ xdict['command_outputs'] += ' --output_dir "$html_file.files_path" --output_html "$html_file" --make_HTML "yes" '
+ xdict['outputs'] += ' <data format="html" name="html_file" label="${job_name}.html"/>\n'
+ if self.opts.output_tab <> 'None':
+ xdict['command_outputs'] += ' --output_tab "$tab_file"'
+ xdict['outputs'] += ' <data format="%s" name="tab_file" label="${job_name}"/>\n' % self.outFormats
+ xdict['command'] = newCommand % xdict
+ xmls = newXML % xdict
+ xf = open(self.xmlfile,'w')
+ xf.write(xmls)
+ xf.write('\n')
+ xf.close()
+ # ready for the tarball
+
+
+ def makeTooltar(self):
+ """
+ a tool is a gz tarball with eg
+ /toolname/tool.xml /toolname/tool.py /toolname/test-data/test1_in.foo ...
+ """
+ retval = self.run()
+ if retval:
+ print >> sys.stderr,'## Run failed. Cannot build yet. Please fix and retry'
+ sys.exit(1)
+ self.makeXML()
+ tdir = self.toolname
+ os.mkdir(tdir)
+ if self.opts.input_tab <> 'None': # no reproducible test otherwise? TODO: maybe..
+ testdir = os.path.join(tdir,'test-data')
+ os.mkdir(testdir) # make tests directory
+ shutil.copyfile(self.opts.input_tab,os.path.join(testdir,self.test1Input))
+ if self.opts.output_tab <> 'None':
+ shutil.copyfile(self.opts.output_tab,os.path.join(testdir,self.test1Output))
+ if self.opts.make_HTML:
+ shutil.copyfile(self.opts.output_html,os.path.join(testdir,self.test1HTML))
+ if self.opts.output_dir:
+ shutil.copyfile(self.tlog,os.path.join(testdir,'test1_out.log'))
+ op = '%s.py' % self.toolname # new name
+ outpiname = os.path.join(tdir,op) # path for the tool tarball
+ pyin = os.path.basename(self.pyfile) # our name - we rewrite ourselves (TM)
+ notes = ['# %s - a self annotated version of %s generated by running %s\n' % (op,pyin,pyin),]
+ notes.append('# to make a new Galaxy tool called %s\n' % self.toolname)
+ notes.append('# User %s at %s\n' % (self.opts.user_email,timenow()))
+ pi = open(self.pyfile,'r').readlines() # our code becomes new tool wrapper (!) - first Galaxy worm
+ notes += pi
+ outpi = open(outpiname,'w')
+ outpi.write(''.join(notes))
+ outpi.write('\n')
+ outpi.close()
+ stname = os.path.join(tdir,self.sfile)
+ if not os.path.exists(stname):
+ shutil.copyfile(self.sfile, stname)
+ xtname = os.path.join(tdir,self.xmlfile)
+ if not os.path.exists(xtname):
+ shutil.copyfile(self.xmlfile,xtname)
+ tarpath = "%s.gz" % self.toolname
+ tar = tarfile.open(tarpath, "w:gz")
+ tar.add(tdir,arcname=self.toolname)
+ tar.close()
+ shutil.copyfile(tarpath,self.opts.new_tool)
+ shutil.rmtree(tdir)
+ ## TODO: replace with optional direct upload to local toolshed?
+ return retval
+
+
+ def compressPDF(self,inpdf=None,thumbformat='png'):
+ """need absolute path to pdf
+ """
+ assert os.path.isfile(inpdf), "## Input %s supplied to %s compressPDF not found" % (inpdf,self.myName)
+ hf,hlog = tempfile.mkstemp(suffix="%s.log" % self.toolname)
+ sto = open(hlog,'w')
+ outpdf = '%s_compressed' % inpdf
+ cl = ["gs", "-sDEVICE=pdfwrite", "-dNOPAUSE", "-dBATCH","-dPDFSETTINGS=/printer", "-sOutputFile=%s" % outpdf,inpdf]
+ x = subprocess.Popen(cl,stdout=sto,stderr=sto,cwd=self.opts.output_dir)
+ retval1 = x.wait()
+ if retval1 == 0:
+ os.unlink(inpdf)
+ shutil.move(outpdf,inpdf)
+ outpng = '%s.%s' % (os.path.splitext(inpdf)[0],thumbformat)
+ if self.useGM:
+ cl2 = ['gm convert', inpdf, outpng]
+ else: # assume imagemagick
+ cl2 = ['convert', inpdf, outpng]
+ x = subprocess.Popen(cl2,stdout=sto,stderr=sto,cwd=self.opts.output_dir)
+ retval2 = x.wait()
+ sto.close()
+ retval = retval1 or retval2
+ return retval
+
+
+ def getfSize(self,fpath,outpath):
+ """
+ format a nice file size string
+ """
+ size = ''
+ fp = os.path.join(outpath,fpath)
+ if os.path.isfile(fp):
+ size = '0 B'
+ n = float(os.path.getsize(fp))
+ if n > 2**20:
+ size = '%1.1f MB' % (n/2**20)
+ elif n > 2**10:
+ size = '%1.1f KB' % (n/2**10)
+ elif n > 0:
+ size = '%d B' % (int(n))
+ return size
+
+ def makeHtml(self):
+ """ Create an HTML file content to list all the artifacts found in the output_dir
+ """
+
+ galhtmlprefix = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="generator" content="Galaxy %s tool output - see http://g2.trac.bx.psu.edu/" />
+ <title></title>
+ <link rel="stylesheet" href="/static/style/base.css" type="text/css" />
+ </head>
+ <body>
+ <div class="toolFormBody">
+ """
+ galhtmlattr = """<hr/><div class="infomessage">This tool (%s) was generated by the <a href="https://bitbucket.org/fubar/galaxytoolfactory/overview">Galaxy Tool Factory</a></div><br/>"""
+ galhtmlpostfix = """</div></body></html>\n"""
+
+ flist = os.listdir(self.opts.output_dir)
+ flist = [x for x in flist if x <> 'Rplots.pdf']
+ flist.sort()
+ html = []
+ html.append(galhtmlprefix % progname)
+ html.append('<div class="infomessage">Galaxy Tool "%s" run at %s</div><br/>' % (self.toolname,timenow()))
+ fhtml = []
+ if len(flist) > 0:
+ logfiles = [x for x in flist if x.lower().endswith('.log')] # log file names determine sections
+ logfiles.sort()
+ logfiles = [x for x in logfiles if os.path.abspath(x) <> os.path.abspath(self.tlog)]
+ logfiles.append(os.path.abspath(self.tlog)) # make it the last one
+ pdflist = []
+ npdf = len([x for x in flist if os.path.splitext(x)[-1].lower() == '.pdf'])
+ for rownum,fname in enumerate(flist):
+ dname,e = os.path.splitext(fname)
+ sfsize = self.getfSize(fname,self.opts.output_dir)
+ if e.lower() == '.pdf' : # compress and make a thumbnail
+ thumb = '%s.%s' % (dname,self.thumbformat)
+ pdff = os.path.join(self.opts.output_dir,fname)
+ retval = self.compressPDF(inpdf=pdff,thumbformat=self.thumbformat)
+ if retval == 0:
+ pdflist.append((fname,thumb))
+ if (rownum+1) % 2 == 0:
+ fhtml.append('<tr class="odd_row"><td><a href="%s">%s</a></td><td>%s</td></tr>' % (fname,fname,sfsize))
+ else:
+ fhtml.append('<tr><td><a href="%s">%s</a></td><td>%s</td></tr>' % (fname,fname,sfsize))
+ for logfname in logfiles: # expect at least tlog - if more
+ if os.path.abspath(logfname) == os.path.abspath(self.tlog): # handled later
+ sectionname = 'All tool run'
+ if (len(logfiles) > 1):
+ sectionname = 'Other'
+ ourpdfs = pdflist
+ else:
+ realname = os.path.basename(logfname)
+ sectionname = os.path.splitext(realname)[0].split('_')[0] # break in case _ added to log
+ ourpdfs = [x for x in pdflist if os.path.basename(x[0]).split('_')[0] == sectionname]
+ pdflist = [x for x in pdflist if os.path.basename(x[0]).split('_')[0] <> sectionname] # remove
+ nacross = 1
+ npdf = len(ourpdfs)
+
+ if npdf > 0:
+ nacross = math.sqrt(npdf) ## int(round(math.log(npdf,2)))
+ if int(nacross)**2 != npdf:
+ nacross += 1
+ nacross = int(nacross)
+ width = min(400,int(1200/nacross))
+ html.append('<div class="toolFormTitle">%s images and outputs</div>' % sectionname)
+ html.append('(Click on a thumbnail image to download the corresponding original PDF image)<br/>')
+ ntogo = nacross # counter for table row padding with empty cells
+ html.append('<div><table class="simple" cellpadding="2" cellspacing="2">\n<tr>')
+ for i,paths in enumerate(ourpdfs):
+ fname,thumb = paths
+ s= """<td><a href="%s"><img src="%s" title="Click to download a PDF of %s" hspace="5" width="%d"
+ alt="Image called %s"/></a></td>\n""" % (fname,thumb,fname,width,fname)
+ if ((i+1) % nacross == 0):
+ s += '</tr>\n'
+ ntogo = 0
+ if i < (npdf - 1): # more to come
+ s += '<tr>'
+ ntogo = nacross
+ else:
+ ntogo -= 1
+ html.append(s)
+ if html[-1].strip().endswith('</tr>'):
+ html.append('</table></div>\n')
+ else:
+ if ntogo > 0: # pad
+ html.append('<td> </td>'*ntogo)
+ html.append('</tr></table></div>\n')
+ logt = open(logfname,'r').readlines()
+ logtext = [x for x in logt if x.strip() > '']
+ html.append('<div class="toolFormTitle">%s log output</div>' % sectionname)
+ if len(logtext) > 1:
+ html.append('\n<pre>\n')
+ html += logtext
+ html.append('\n</pre>\n')
+ else:
+ html.append('%s is empty<br/>' % logfname)
+ if len(fhtml) > 0:
+ fhtml.insert(0,'<div><table class="colored" cellpadding="3" cellspacing="3"><tr><th>Output File Name (click to view)</th><th>Size</th></tr>\n')
+ fhtml.append('</table></div><br/>')
+ html.append('<div class="toolFormTitle">All output files available for downloading</div>\n')
+ html += fhtml # add all non-pdf files to the end of the display
+ else:
+ html.append('<div class="warningmessagelarge">### Error - %s returned no files - please confirm that parameters are sane</div>' % self.opts.interpreter)
+ html.append(galhtmlpostfix)
+ htmlf = file(self.opts.output_html,'w')
+ htmlf.write('\n'.join(html))
+ htmlf.write('\n')
+ htmlf.close()
+ self.html = html
+
+
+ def run(self):
+ """
+ scripts must be small enough not to fill the pipe!
+ """
+ if self.treatbashSpecial and self.opts.interpreter in ['bash','sh']:
+ retval = self.runBash()
+ else:
+ if self.opts.output_dir:
+ ste = open(self.elog,'w')
+ sto = open(self.tlog,'w')
+ sto.write('## Toolfactory generated command line = %s\n' % ' '.join(self.cl))
+ sto.flush()
+ p = subprocess.Popen(self.cl,shell=False,stdout=sto,stderr=ste,stdin=subprocess.PIPE,cwd=self.opts.output_dir)
+ else:
+ p = subprocess.Popen(self.cl,shell=False,stdin=subprocess.PIPE)
+ p.stdin.write(self.script)
+ p.stdin.close()
+ retval = p.wait()
+ if self.opts.output_dir:
+ sto.close()
+ ste.close()
+ err = open(self.elog,'r').readlines()
+ if retval <> 0 and err: # problem
+ print >> sys.stderr,err
+ if self.opts.make_HTML:
+ self.makeHtml()
+ return retval
+
+ def runBash(self):
+ """
+ cannot use - for bash so use self.sfile
+ """
+ if self.opts.output_dir:
+ s = '## Toolfactory generated command line = %s\n' % ' '.join(self.cl)
+ sto = open(self.tlog,'w')
+ sto.write(s)
+ sto.flush()
+ p = subprocess.Popen(self.cl,shell=False,stdout=sto,stderr=sto,cwd=self.opts.output_dir)
+ else:
+ p = subprocess.Popen(self.cl,shell=False)
+ retval = p.wait()
+ if self.opts.output_dir:
+ sto.close()
+ if self.opts.make_HTML:
+ self.makeHtml()
+ return retval
+
+
+def main():
+ u = """
+ This is a Galaxy wrapper. It expects to be called by a special purpose tool.xml as:
+ <command interpreter="python">rgBaseScriptWrapper.py --script_path "$scriptPath" --tool_name "foo" --interpreter "Rscript"
+ </command>
+ """
+ op = optparse.OptionParser()
+ a = op.add_option
+ a('--script_path',default=None)
+ a('--tool_name',default=None)
+ a('--interpreter',default=None)
+ a('--output_dir',default=None)
+ a('--output_html',default=None)
+ a('--input_tab',default="None")
+ a('--output_tab',default="None")
+ a('--user_email',default='Unknown')
+ a('--bad_user',default=None)
+ a('--make_Tool',default=None)
+ a('--make_HTML',default=None)
+ a('--help_text',default=None)
+ a('--tool_desc',default=None)
+ a('--new_tool',default=None)
+ a('--tool_version',default=None)
+ opts, args = op.parse_args()
+ assert not opts.bad_user,'UNAUTHORISED: %s is NOT authorized to use this tool until Galaxy admin adds %s to admin_users in universe_wsgi.ini' % (opts.bad_user,opts.bad_user)
+ assert opts.tool_name,'## Tool Factory expects a tool name - eg --tool_name=DESeq'
+ assert opts.interpreter,'## Tool Factory wrapper expects an interpreter - eg --interpreter=Rscript'
+ assert os.path.isfile(opts.script_path),'## Tool Factory wrapper expects a script path - eg --script_path=foo.R'
+ if opts.output_dir:
+ try:
+ os.makedirs(opts.output_dir)
+ except:
+ pass
+ r = ScriptRunner(opts)
+ if opts.make_Tool:
+ retcode = r.makeTooltar()
+ else:
+ retcode = r.run()
+ os.unlink(r.sfile)
+ if retcode:
+ sys.exit(retcode) # indicate failure to job runner
+
+
+if __name__ == "__main__":
+ main()
+
+
diff -r 1092b0d54bf690b9d148276bcb14baae80f429e2 -r f0cdc6bebfaa8757bb093d74508332515d5eec38 tools/rgedgeR/rgedgeRpaired.xml
--- /dev/null
+++ b/tools/rgedgeR/rgedgeRpaired.xml
@@ -0,0 +1,1079 @@
+<tool id="rgDifferentialCount" name="Differential_Count" version="0.20">
+ <description>models using BioConductor packages</description>
+ <requirements>
+ <requirement type="package" version="2.12">biocbasics</requirement>
+ <requirement type="package" version="3.0.1">r3</requirement>
+ <requirement type="package" version="1.3.18">graphicsmagick</requirement>
+ <requirement type="package" version="9.07">ghostscript</requirement>
+ </requirements>
+
+ <command interpreter="python">
+ rgToolFactory.py --script_path "$runme" --interpreter "Rscript" --tool_name "DifferentialCounts"
+ --output_dir "$html_file.files_path" --output_html "$html_file" --make_HTML "yes"
+ </command>
+ <inputs>
+ <param name="input1" type="data" format="tabular" label="Select an input matrix - rows are contigs, columns are counts for each sample"
+ help="Use the HTSeq based count matrix preparation tool to create these matrices from BAM/SAM files and a GTF file of genomic features"/>
+ <param name="title" type="text" value="Differential Counts" size="80" label="Title for job outputs"
+ help="Supply a meaningful name here to remind you what the outputs contain">
+ <sanitizer invalid_char="">
+ <valid initial="string.letters,string.digits"><add value="_" /></valid>
+ </sanitizer>
+ </param>
+ <param name="treatment_name" type="text" value="Treatment" size="50" label="Treatment Name"/>
+ <param name="Treat_cols" label="Select columns containing treatment." type="data_column" data_ref="input1" numerical="True"
+ multiple="true" use_header_names="true" size="120" display="checkboxes">
+ <validator type="no_options" message="Please select at least one column."/>
+ </param>
+ <param name="control_name" type="text" value="Control" size="50" label="Control Name"/>
+ <param name="Control_cols" label="Select columns containing control." type="data_column" data_ref="input1" numerical="True"
+ multiple="true" use_header_names="true" size="120" display="checkboxes" optional="true">
+ </param>
+ <param name="subjectids" type="text" optional="true" size="120" value = ""
+ label="IF SUBJECTS NOT ALL INDEPENDENT! Enter comma separated strings to indicate sample labels for (eg) pairing - must be one for every column in input"
+ help="Leave blank if no pairing, but eg if data from sample id A99 is in columns 2,4 and id C21 is in 3,5 then enter 'A99,C21,A99,C21'">
+ <sanitizer>
+ <valid initial="string.letters,string.digits"><add value="," /></valid>
+ </sanitizer>
+ </param>
+ <param name="fQ" type="float" value="0.3" size="5" label="Non-differential contig count quantile threshold - zero to analyze all non-zero read count contigs"
+ help="May be a good or a bad idea depending on the biology and the question. EG 0.3 = sparsest 30% of contigs with at least one read are removed before analysis"/>
+ <param name="useNDF" type="boolean" truevalue="T" falsevalue="F" checked="false" size="1"
+ label="Non differential filter - remove contigs below a threshold (1 per million) for half or more samples"
+ help="May be a good or a bad idea depending on the biology and the question. This was the old default. Quantile based is available as an alternative"/>
+
+ <conditional name="edgeR">
+ <param name="doedgeR" type="select"
+ label="Run this model using edgeR"
+ help="edgeR uses a negative binomial model and seems to be powerful, even with few replicates">
+ <option value="F">Do not run edgeR</option>
+ <option value="T" selected="true">Run edgeR</option>
+ </param>
+ <when value="T">
+ <param name="edgeR_priordf" type="integer" value="20" size="3"
+ label="prior.df for tagwise dispersion - lower value = more emphasis on each tag's variance. Replaces prior.n and prior.df = prior.n * residual.df"
+ help="0 = Use edgeR default. Use a small value to 'smooth' small samples. See edgeR docs and note below"/>
+ </when>
+ <when value="F"></when>
+ </conditional>
+ <conditional name="DESeq2">
+ <param name="doDESeq2" type="select"
+ label="Run the same model with DESeq2 and compare findings"
+ help="DESeq2 is an update to the DESeq package. It uses different assumptions and methods to edgeR">
+ <option value="F" selected="true">Do not run DESeq2</option>
+ <option value="T">Run DESeq2</option>
+ </param>
+ <when value="T">
+ <param name="DESeq_fitType" type="select">
+ <option value="parametric" selected="true">Parametric (default) fit for dispersions</option>
+ <option value="local">Local fit - this will automagically be used if parametric fit fails</option>
+ <option value="mean">Mean dispersion fit- use this if you really understand what you're doing - read the fine manual linked below in the documentation</option>
+ </param>
+ </when>
+ <when value="F"></when>
+ </conditional>
+ <param name="doVoom" type="select"
+ label="Run the same model with Voom/limma and compare findings"
+ help="Voom uses counts per million and a precise transformation of variance so count data can be analysed using limma">
+ <option value="F" selected="true">Do not run VOOM</option>
+ <option value="T">Run VOOM</option>
+ </param>
+ <conditional name="camera">
+ <param name="doCamera" type="select" label="Run the edgeR implementation of Camera GSEA for up/down gene sets"
+ help="If yes, you can choose a set of genesets to test and/or supply a gmt format geneset collection from your history">
+ <option value="F" selected="true">Do not run GSEA tests with the Camera algorithm</option>
+ <option value="T">Run GSEA tests with the Camera algorithm</option>
+ </param>
+ <when value="T">
+ <conditional name="gmtSource">
+ <param name="refgmtSource" type="select"
+ label="Use a gene set (.gmt) from your history and/or use a built-in (MSigDB etc) gene set">
+ <option value="indexed" selected="true">Use a built-in gene set</option>
+ <option value="history">Use a gene set from my history</option>
+ <option value="both">Add a gene set from my history to a built in gene set</option>
+ </param>
+ <when value="indexed">
+ <param name="builtinGMT" type="select" label="Select a gene set matrix (.gmt) file to use for the analysis">
+ <options from_data_table="gseaGMT_3.1">
+ <filter type="sort_by" column="2" />
+ <validator type="no_options" message="No GMT v3.1 files are available - please install them"/>
+ </options>
+ </param>
+ </when>
+ <when value="history">
+ <param name="ownGMT" type="data" format="gmt" label="Select a Gene Set from your history" />
+ </when>
+ <when value="both">
+ <param name="ownGMT" type="data" format="gseagmt" label="Select a Gene Set from your history" />
+ <param name="builtinGMT" type="select" label="Select a gene set matrix (.gmt) file to use for the analysis">
+ <options from_data_table="gseaGMT_4">
+ <filter type="sort_by" column="2" />
+ <validator type="no_options" message="No GMT v4 files are available - please fix tool_data_table and loc files"/>
+ </options>
+ </param>
+ </when>
+ </conditional>
+ </when>
+ <when value="F">
+ </when>
+ </conditional>
+ <param name="fdrthresh" type="float" value="0.05" size="5" label="P value threshold for FDR filtering for amily wise error rate control"
+ help="Conventional default value of 0.05 recommended"/>
+ <param name="fdrtype" type="select" label="FDR (Type II error) control method"
+ help="Use fdr or bh typically to control for the number of tests in a reliable way">
+ <option value="fdr" selected="true">fdr</option>
+ <option value="BH">Benjamini Hochberg</option>
+ <option value="BY">Benjamini Yukateli</option>
+ <option value="bonferroni">Bonferroni</option>
+ <option value="hochberg">Hochberg</option>
+ <option value="holm">Holm</option>
+ <option value="hommel">Hommel</option>
+ <option value="none">no control for multiple tests</option>
+ </param>
+ </inputs>
+ <outputs>
+ <data format="tabular" name="out_edgeR" label="${title}_topTable_edgeR.xls">
+ <filter>edgeR['doedgeR'] == "T"</filter>
+ </data>
+ <data format="tabular" name="out_DESeq2" label="${title}_topTable_DESeq2.xls">
+ <filter>DESeq2['doDESeq2'] == "T"</filter>
+ </data>
+ <data format="tabular" name="out_VOOM" label="${title}_topTable_VOOM.xls">
+ <filter>doVoom == "T"</filter>
+ </data>
+ <data format="html" name="html_file" label="${title}.html"/>
+ </outputs>
+ <stdio>
+ <exit_code range="4" level="fatal" description="Number of subject ids must match total number of samples in the input matrix" />
+ </stdio>
+ <tests>
+<test>
+<param name='input1' value='test_bams2mx.xls' ftype='tabular' />
+ <param name='treatment_name' value='case' />
+ <param name='title' value='edgeRtest' />
+ <param name='useNDF' value='' />
+ <param name='doedgeR' value='T' />
+ <param name='doVoom' value='T' />
+ <param name='doDESeq2' value='T' />
+ <param name='fdrtype' value='fdr' />
+ <param name='edgeR_priordf' value="8" />
+ <param name='fdrthresh' value="0.05" />
+ <param name='control_name' value='control' />
+ <param name='subjectids' value='' />
+ <param name='Treat_cols' value='3,4,5,9' />
+ <param name='Control_cols' value='2,6,7,8' />
+ <output name='out_edgeR' file='edgeRtest1out.xls' compare='diff' />
+ <output name='html_file' file='edgeRtest1out.html' compare='diff' lines_diff='20' />
+</test>
+</tests>
+
+<configfiles>
+<configfile name="runme">
+<![CDATA[
+#
+# edgeR.Rscript
+# updated npv 2011 for R 2.14.0 and edgeR 2.4.0 by ross
+# Performs DGE on a count table containing n replicates of two conditions
+#
+# Parameters
+#
+# 1 - Output Dir
+
+# Original edgeR code by: S.Lunke and A.Kaspi
+reallybig = log10(.Machine\$double.xmax)
+reallysmall = log10(.Machine\$double.xmin)
+library('stringr')
+library('gplots')
+library('edgeR')
+hmap2 = function(cmat,nsamp=100,outpdfname='heatmap2.pdf', TName='Treatment',group=NA,myTitle='title goes here')
+{
+# Perform clustering for significant pvalues after controlling FWER
+ samples = colnames(cmat)
+ gu = unique(group)
+ gn = rownames(cmat)
+ if (length(gu) == 2) {
+ col.map = function(g) {if (g==gu[1]) "#FF0000" else "#0000FF"}
+ pcols = unlist(lapply(group,col.map))
+ } else {
+ colours = rainbow(length(gu),start=0,end=4/6)
+ pcols = colours[match(group,gu)] }
+ dm = cmat[(! is.na(gn)),]
+ # remove unlabelled hm rows
+ nprobes = nrow(dm)
+ # sub = paste('Showing',nprobes,'contigs ranked for evidence of differential abundance')
+ if (nprobes > nsamp) {
+ dm =dm[1:nsamp,]
+ #sub = paste('Showing',nsamp,'contigs ranked for evidence for differential abundance out of',nprobes,'total')
+ }
+ newcolnames = substr(colnames(dm),1,20)
+ colnames(dm) = newcolnames
+ pdf(outpdfname)
+ heatmap.2(dm,main=myTitle,ColSideColors=pcols,col=topo.colors(100),dendrogram="col",key=T,density.info='none',
+ Rowv=F,scale='row',trace='none',margins=c(8,8),cexRow=0.4,cexCol=0.5)
+ dev.off()
+}
+
+hmap = function(cmat,nmeans=4,outpdfname="heatMap.pdf",nsamp=250,TName='Treatment',group=NA,myTitle="Title goes here")
+{
+ # for 2 groups only was
+ #col.map = function(g) {if (g==TName) "#FF0000" else "#0000FF"}
+ #pcols = unlist(lapply(group,col.map))
+ gu = unique(group)
+ colours = rainbow(length(gu),start=0.3,end=0.6)
+ pcols = colours[match(group,gu)]
+ nrows = nrow(cmat)
+ mtitle = paste(myTitle,'Heatmap: n contigs =',nrows)
+ if (nrows > nsamp) {
+ cmat = cmat[c(1:nsamp),]
+ mtitle = paste('Heatmap: Top ',nsamp,' DE contigs (of ',nrows,')',sep='')
+ }
+ newcolnames = substr(colnames(cmat),1,20)
+ colnames(cmat) = newcolnames
+ pdf(outpdfname)
+ heatmap(cmat,scale='row',main=mtitle,cexRow=0.3,cexCol=0.4,Rowv=NA,ColSideColors=pcols)
+ dev.off()
+}
+
+qqPlot = function(descr='qqplot',pvector, outpdf='qqplot.pdf',...)
+# stolen from https://gist.github.com/703512
+{
+ o = -log10(sort(pvector,decreasing=F))
+ e = -log10( 1:length(o)/length(o) )
+ o[o==-Inf] = reallysmall
+ o[o==Inf] = reallybig
+ maint = descr
+ pdf(outpdf)
+ plot(e,o,pch=19,cex=1, main=maint, ...,
+ xlab=expression(Expected~~-log[10](italic(p))),
+ ylab=expression(Observed~~-log[10](italic(p))),
+ xlim=c(0,max(e)), ylim=c(0,max(o)))
+ lines(e,e,col="red")
+ grid(col = "lightgray", lty = "dotted")
+ dev.off()
+}
+
+smearPlot = function(DGEList,deTags, outSmear, outMain)
+ {
+ pdf(outSmear)
+ plotSmear(DGEList,de.tags=deTags,main=outMain)
+ grid(col="lightgray", lty="dotted")
+ dev.off()
+ }
+
+boxPlot = function(rawrs,cleanrs,maint,myTitle,pdfname)
+{ #
+ nc = ncol(rawrs)
+ for (i in c(1:nc)) {rawrs[(rawrs[,i] < 0),i] = NA}
+ fullnames = colnames(rawrs)
+ newcolnames = substr(colnames(rawrs),1,20)
+ colnames(rawrs) = newcolnames
+ newcolnames = substr(colnames(cleanrs),1,20)
+ colnames(cleanrs) = newcolnames
+ defpar = par(no.readonly=T)
+ print.noquote('raw contig counts by sample:')
+ print.noquote(summary(rawrs))
+ print.noquote('normalised contig counts by sample:')
+ print.noquote(summary(cleanrs))
+ pdf(pdfname)
+ par(mfrow=c(1,2))
+ boxplot(rawrs,varwidth=T,notch=T,ylab='log contig count',col="maroon",las=3,cex.axis=0.35,main=paste('Raw:',maint))
+ grid(col="lightgray",lty="dotted")
+ boxplot(cleanrs,varwidth=T,notch=T,ylab='log contig count',col="maroon",las=3,cex.axis=0.35,main=paste('After ',maint))
+ grid(col="lightgray",lty="dotted")
+ dev.off()
+ pdfname = "sample_counts_histogram.pdf"
+ nc = ncol(rawrs)
+ print.noquote(paste('Using ncol rawrs=',nc))
+ ncroot = round(sqrt(nc))
+ if (ncroot*ncroot < nc) { ncroot = ncroot + 1 }
+ m = c()
+ for (i in c(1:nc)) {
+ rhist = hist(rawrs[,i],breaks=100,plot=F)
+ m = append(m,max(rhist\$counts))
+ }
+ ymax = max(m)
+ ncols = length(fullnames)
+ if (ncols > 20)
+ {
+ scale = 7*ncols/20
+ pdf(pdfname,width=scale,height=scale)
+ } else {
+ pdf(pdfname)
+ }
+ par(mfrow=c(ncroot,ncroot))
+ for (i in c(1:nc)) {
+ hist(rawrs[,i], main=paste("Contig logcount",i), xlab='log raw count', col="maroon",
+ breaks=100,sub=fullnames[i],cex=0.8,ylim=c(0,ymax))
+ }
+ dev.off()
+ par(defpar)
+
+}
+
+cumPlot = function(rawrs,cleanrs,maint,myTitle)
+{ # updated to use ecdf
+ pdfname = "Filtering_rowsum_bar_charts.pdf"
+ defpar = par(no.readonly=T)
+ lrs = log(rawrs,10)
+ lim = max(lrs)
+ pdf(pdfname)
+ par(mfrow=c(2,1))
+ hist(lrs,breaks=100,main=paste('Before:',maint),xlab="# Reads (log)",
+ ylab="Count",col="maroon",sub=myTitle, xlim=c(0,lim),las=1)
+ grid(col="lightgray", lty="dotted")
+ lrs = log(cleanrs,10)
+ hist(lrs,breaks=100,main=paste('After:',maint),xlab="# Reads (log)",
+ ylab="Count",col="maroon",sub=myTitle,xlim=c(0,lim),las=1)
+ grid(col="lightgray", lty="dotted")
+ dev.off()
+ par(defpar)
+}
+
+cumPlot1 = function(rawrs,cleanrs,maint,myTitle)
+{ # updated to use ecdf
+ pdfname = paste(gsub(" ","", myTitle , fixed=TRUE),"RowsumCum.pdf",sep='_')
+ pdf(pdfname)
+ par(mfrow=c(2,1))
+ lastx = max(rawrs)
+ rawe = knots(ecdf(rawrs))
+ cleane = knots(ecdf(cleanrs))
+ cy = 1:length(cleane)/length(cleane)
+ ry = 1:length(rawe)/length(rawe)
+ plot(rawe,ry,type='l',main=paste('Before',maint),xlab="Log Contig Total Reads",
+ ylab="Cumulative proportion",col="maroon",log='x',xlim=c(1,lastx),sub=myTitle)
+ grid(col="blue")
+ plot(cleane,cy,type='l',main=paste('After',maint),xlab="Log Contig Total Reads",
+ ylab="Cumulative proportion",col="maroon",log='x',xlim=c(1,lastx),sub=myTitle)
+ grid(col="blue")
+ dev.off()
+}
+
+
+
+doGSEAold = function(y=NULL,design=NULL,histgmt="",
+ bigmt="/data/genomes/gsea/3.1/Abetterchoice_nocgp_c2_c3_c5_symbols_all.gmt",
+ ntest=0, myTitle="myTitle", outfname="GSEA.xls", minnin=5, maxnin=2000,fdrthresh=0.05,fdrtype="BH")
+{
+ sink('Camera.log')
+ genesets = c()
+ if (bigmt > "")
+ {
+ bigenesets = readLines(bigmt)
+ genesets = bigenesets
+ }
+ if (histgmt > "")
+ {
+ hgenesets = readLines(histgmt)
+ if (bigmt > "") {
+ genesets = rbind(genesets,hgenesets)
+ } else {
+ genesets = hgenesets
+ } # use only history if no bi
+ }
+ print.noquote(paste("@@@read",length(genesets), 'genesets from',histgmt,bigmt))
+ genesets = strsplit(genesets,'\t') # tabular. genesetid\tURLorwhatever\tgene_1\t..\tgene_n
+ outf = outfname
+ head=paste(myTitle,'edgeR GSEA')
+ write(head,file=outfname,append=F)
+ ntest=length(genesets)
+ urownames = toupper(rownames(y))
+ upcam = c()
+ downcam = c()
+ for (i in 1:ntest) {
+ gs = unlist(genesets[i])
+ g = gs[1] # geneset_id
+ u = gs[2]
+ if (u > "") { u = paste("<a href=\'",u,"\'>",u,"</a>",sep="") }
+ glist = gs[3:length(gs)] # member gene symbols
+ glist = toupper(glist)
+ inglist = urownames %in% glist
+ nin = sum(inglist)
+ if ((nin > minnin) && (nin < maxnin)) {
+ ### print(paste('@@found',sum(inglist),'genes in glist'))
+ camres = camera(y=y,index=inglist,design=design)
+ if (! is.null(camres)) {
+ rownames(camres) = g # gene set name
+ camres = cbind(GeneSet=g,URL=u,camres)
+ if (camres\$Direction == "Up")
+ {
+ upcam = rbind(upcam,camres) } else {
+ downcam = rbind(downcam,camres)
+ }
+ }
+ }
+ }
+ uscam = upcam[order(upcam\$PValue),]
+ unadjp = uscam\$PValue
+ uscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
+ nup = max(10,sum((uscam\$adjPValue < fdrthresh)))
+ dscam = downcam[order(downcam\$PValue),]
+ unadjp = dscam\$PValue
+ dscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
+ ndown = max(10,sum((dscam\$adjPValue < fdrthresh)))
+ write.table(uscam,file=paste('camera_up',outfname,sep='_'),quote=F,sep='\t',row.names=F)
+ write.table(dscam,file=paste('camera_down',outfname,sep='_'),quote=F,sep='\t',row.names=F)
+ print.noquote(paste('@@@@@ Camera up top',nup,'gene sets:'))
+ write.table(head(uscam,nup),file="",quote=F,sep='\t',row.names=F)
+ print.noquote(paste('@@@@@ Camera down top',ndown,'gene sets:'))
+ write.table(head(dscam,ndown),file="",quote=F,sep='\t',row.names=F)
+ sink()
+}
+
+
+
+
+doGSEA = function(y=NULL,design=NULL,histgmt="",
+ bigmt="/data/genomes/gsea/3.1/Abetterchoice_nocgp_c2_c3_c5_symbols_all.gmt",
+ ntest=0, myTitle="myTitle", outfname="GSEA.xls", minnin=5, maxnin=2000,fdrthresh=0.05,fdrtype="BH")
+{
+ sink('Camera.log')
+ genesets = c()
+ if (bigmt > "")
+ {
+ bigenesets = readLines(bigmt)
+ genesets = bigenesets
+ }
+ if (histgmt > "")
+ {
+ hgenesets = readLines(histgmt)
+ if (bigmt > "") {
+ genesets = rbind(genesets,hgenesets)
+ } else {
+ genesets = hgenesets
+ } # use only history if no bi
+ }
+ print.noquote(paste("@@@read",length(genesets), 'genesets from',histgmt,bigmt))
+ genesets = strsplit(genesets,'\t') # tabular. genesetid\tURLorwhatever\tgene_1\t..\tgene_n
+ outf = outfname
+ head=paste(myTitle,'edgeR GSEA')
+ write(head,file=outfname,append=F)
+ ntest=length(genesets)
+ urownames = toupper(rownames(y))
+ upcam = c()
+ downcam = c()
+ incam = c()
+ urls = c()
+ gsids = c()
+ for (i in 1:ntest) {
+ gs = unlist(genesets[i])
+ gsid = gs[1] # geneset_id
+ url = gs[2]
+ if (url > "") { url = paste("<a href=\'",url,"\'>",url,"</a>",sep="") }
+ glist = gs[3:length(gs)] # member gene symbols
+ glist = toupper(glist)
+ inglist = urownames %in% glist
+ nin = sum(inglist)
+ if ((nin > minnin) && (nin < maxnin)) {
+ incam = c(incam,inglist)
+ gsids = c(gsids,gsid)
+ urls = c(urls,url)
+ }
+ }
+ incam = as.list(incam)
+ names(incam) = gsids
+ allcam = camera(y=y,index=incam,design=design)
+ allcamres = cbind(geneset=gsids,allcam,URL=urls)
+ for (i in 1:ntest) {
+ camres = allcamres[i]
+ res = try(test = (camres\$Direction == "Up"))
+ if ("try-error" %in% class(res)) {
+ cat("test failed, camres = :")
+ print.noquote(camres)
+ } else { if (camres\$Direction == "Up")
+ { upcam = rbind(upcam,camres)
+ } else { downcam = rbind(downcam,camres)
+ }
+
+ }
+ }
+ uscam = upcam[order(upcam\$PValue),]
+ unadjp = uscam\$PValue
+ uscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
+ nup = max(10,sum((uscam\$adjPValue < fdrthresh)))
+ dscam = downcam[order(downcam\$PValue),]
+ unadjp = dscam\$PValue
+ dscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
+ ndown = max(10,sum((dscam\$adjPValue < fdrthresh)))
+ write.table(uscam,file=paste('camera_up',outfname,sep='_'),quote=F,sep='\t',row.names=F)
+ write.table(dscam,file=paste('camera_down',outfname,sep='_'),quote=F,sep='\t',row.names=F)
+ print.noquote(paste('@@@@@ Camera up top',nup,'gene sets:'))
+ write.table(head(uscam,nup),file="",quote=F,sep='\t',row.names=F)
+ print.noquote(paste('@@@@@ Camera down top',ndown,'gene sets:'))
+ write.table(head(dscam,ndown),file="",quote=F,sep='\t',row.names=F)
+ sink()
+ }
+
+
+edgeIt = function (Count_Matrix=c(),group=c(),out_edgeR=F,out_VOOM=F,out_DESeq2=F,fdrtype='fdr',priordf=5,
+ fdrthresh=0.05,outputdir='.', myTitle='Differential Counts',libSize=c(),useNDF=F,
+ filterquantile=0.2, subjects=c(),mydesign=NULL,
+ doDESeq2=T,doVoom=T,doCamera=T,doedgeR=T,org='hg19',
+ histgmt="", bigmt="/data/genomes/gsea/3.1/Abetterchoice_nocgp_c2_c3_c5_symbols_all.gmt",
+ doCook=F,DESeq_fitType="parameteric")
+{
+ # Error handling
+ if (length(unique(group))!=2){
+ print("Number of conditions identified in experiment does not equal 2")
+ q()
+ }
+ require(edgeR)
+ options(width = 512)
+ mt = paste(unlist(strsplit(myTitle,'_')),collapse=" ")
+ allN = nrow(Count_Matrix)
+ nscut = round(ncol(Count_Matrix)/2)
+ colTotmillionreads = colSums(Count_Matrix)/1e6
+ counts.dataframe = as.data.frame(c())
+ rawrs = rowSums(Count_Matrix)
+ nonzerod = Count_Matrix[(rawrs > 0),] # remove all zero count genes
+ nzN = nrow(nonzerod)
+ nzrs = rowSums(nonzerod)
+ zN = allN - nzN
+ print('# Quantiles for non-zero row counts:',quote=F)
+ print(quantile(nzrs,probs=seq(0,1,0.1)),quote=F)
+ if (useNDF == T)
+ {
+ gt1rpin3 = rowSums(Count_Matrix/expandAsMatrix(colTotmillionreads,dim(Count_Matrix)) >= 1) >= nscut
+ lo = colSums(Count_Matrix[!gt1rpin3,])
+ workCM = Count_Matrix[gt1rpin3,]
+ cleanrs = rowSums(workCM)
+ cleanN = length(cleanrs)
+ meth = paste( "After removing",length(lo),"contigs with fewer than ",nscut," sample read counts >= 1 per million, there are",sep="")
+ print(paste("Read",allN,"contigs. Removed",zN,"contigs with no reads.",meth,cleanN,"contigs"),quote=F)
+ maint = paste('Filter >=1/million reads in >=',nscut,'samples')
+ } else {
+ useme = (nzrs > quantile(nzrs,filterquantile))
+ workCM = nonzerod[useme,]
+ lo = colSums(nonzerod[!useme,])
+ cleanrs = rowSums(workCM)
+ cleanN = length(cleanrs)
+ meth = paste("After filtering at count quantile =",filterquantile,", there are",sep="")
+ print(paste('Read',allN,"contigs. Removed",zN,"with no reads.",meth,cleanN,"contigs"),quote=F)
+ maint = paste('Filter below',filterquantile,'quantile')
+ }
+ cumPlot(rawrs=rawrs,cleanrs=cleanrs,maint=maint,myTitle=myTitle)
+ allgenes = rownames(workCM)
+ reg = "^chr([0-9]+):([0-9]+)-([0-9]+)"
+ genecards="<a href=\'http://www.genecards.org/index.php?path=/Search/keyword/"
+ ucsc = paste("<a href=\'http://genome.ucsc.edu/cgi-bin/hgTracks?db=",org,sep='')
+ testreg = str_match(allgenes,reg)
+ if (sum(!is.na(testreg[,1]))/length(testreg[,1]) > 0.8) # is ucsc style string
+ {
+ print("@@ using ucsc substitution for urls")
+ contigurls = paste0(ucsc,"&position=chr",testreg[,2],":",testreg[,3],"-",testreg[,4],"\'>",allgenes,"</a>")
+ } else {
+ print("@@ using genecards substitution for urls")
+ contigurls = paste0(genecards,allgenes,"\'>",allgenes,"</a>")
+ }
+ print.noquote("# urls")
+ print.noquote(head(contigurls))
+ print(paste("# Total low count contigs per sample = ",paste(lo,collapse=',')),quote=F)
+ cmrowsums = rowSums(workCM)
+ TName=unique(group)[1]
+ CName=unique(group)[2]
+ if (is.null(mydesign)) {
+ if (length(subjects) == 0)
+ {
+ mydesign = model.matrix(~group)
+ }
+ else {
+ subjf = factor(subjects)
+ mydesign = model.matrix(~subjf+group) # we block on subject so make group last to simplify finding it
+ }
+ }
+ print.noquote(paste('Using samples:',paste(colnames(workCM),collapse=',')))
+ print.noquote('Using design matrix:')
+ print.noquote(mydesign)
+ if (doedgeR) {
+ sink('edgeR.log')
+ #### Setup DGEList object
+ DGEList = DGEList(counts=workCM, group = group)
+ DGEList = calcNormFactors(DGEList)
+
+ DGEList = estimateGLMCommonDisp(DGEList,mydesign)
+ comdisp = DGEList\$common.dispersion
+ DGEList = estimateGLMTrendedDisp(DGEList,mydesign)
+ if (edgeR_priordf > 0) {
+ print.noquote(paste("prior.df =",edgeR_priordf))
+ DGEList = estimateGLMTagwiseDisp(DGEList,mydesign,prior.df = edgeR_priordf)
+ } else {
+ DGEList = estimateGLMTagwiseDisp(DGEList,mydesign)
+ }
+ DGLM = glmFit(DGEList,design=mydesign)
+ DE = glmLRT(DGLM,coef=ncol(DGLM\$design)) # always last one - subject is first if needed
+ efflib = DGEList\$samples\$lib.size*DGEList\$samples\$norm.factors
+ normData = (1e+06*DGEList\$counts/efflib)
+ uoutput = cbind(
+ Name=as.character(rownames(DGEList\$counts)),
+ DE\$table,
+ adj.p.value=p.adjust(DE\$table\$PValue, method=fdrtype),
+ Dispersion=DGEList\$tagwise.dispersion,totreads=cmrowsums,normData,
+ DGEList\$counts
+ )
+ soutput = uoutput[order(DE\$table\$PValue),] # sorted into p value order - for quick toptable
+ goodness = gof(DGLM, pcutoff=fdrthresh)
+ if (sum(goodness\$outlier) > 0) {
+ print.noquote('GLM outliers:')
+ print(paste(rownames(DGLM)[(goodness\$outlier)],collapse=','),quote=F)
+ } else {
+ print('No GLM fit outlier genes found\n')
+ }
+ z = limma::zscoreGamma(goodness\$gof.statistic, shape=goodness\$df/2, scale=2)
+ pdf("edgeR_GoodnessofFit.pdf")
+ qq = qqnorm(z, panel.first=grid(), main="tagwise dispersion")
+ abline(0,1,lwd=3)
+ points(qq\$x[goodness\$outlier],qq\$y[goodness\$outlier], pch=16, col="maroon")
+ dev.off()
+ estpriorn = getPriorN(DGEList)
+ print(paste("Common Dispersion =",comdisp,"CV = ",sqrt(comdisp),"getPriorN = ",estpriorn),quote=F)
+ efflib = DGEList\$samples\$lib.size*DGEList\$samples\$norm.factors
+ normData = (1e+06*DGEList\$counts/efflib)
+ uniqueg = unique(group)
+ #### Plot MDS
+ sample_colors = match(group,levels(group))
+ sampleTypes = levels(factor(group))
+ print.noquote(sampleTypes)
+ pdf("edgeR_MDSplot.pdf")
+ plotMDS.DGEList(DGEList,main=paste("edgeR MDS for",myTitle),cex=0.5,col=sample_colors,pch=sample_colors)
+ legend(x="topleft", legend = sampleTypes,col=c(1:length(sampleTypes)), pch=19)
+ grid(col="blue")
+ dev.off()
+ colnames(normData) = paste( colnames(normData),'N',sep="_")
+ print(paste('Raw sample read totals',paste(colSums(nonzerod,na.rm=T),collapse=',')))
+ nzd = data.frame(log(nonzerod + 1e-2,10))
+ try( boxPlot(rawrs=nzd,cleanrs=log(normData,10),maint='TMM Normalisation',myTitle=myTitle,pdfname="edgeR_raw_norm_counts_box.pdf") )
+ write.table(soutput,file=out_edgeR, quote=FALSE, sep="\t",row.names=F)
+ tt = cbind(
+ Name=as.character(rownames(DGEList\$counts)),
+ DE\$table,
+ adj.p.value=p.adjust(DE\$table\$PValue, method=fdrtype),
+ Dispersion=DGEList\$tagwise.dispersion,totreads=cmrowsums
+ )
+ print.noquote("# edgeR Top tags\n")
+ tt = cbind(tt,URL=contigurls) # add to end so table isn't laid out strangely
+ tt = tt[order(DE\$table\$PValue),]
+ print.noquote(tt[1:50,])
+ deTags = rownames(uoutput[uoutput\$adj.p.value < fdrthresh,])
+ nsig = length(deTags)
+ print(paste('#',nsig,'tags significant at adj p=',fdrthresh),quote=F)
+ deColours = ifelse(deTags,'red','black')
+ pdf("edgeR_BCV_vs_abundance.pdf")
+ plotBCV(DGEList, cex=0.3, main="Biological CV vs abundance")
+ dev.off()
+ dg = DGEList[order(DE\$table\$PValue),]
+ #normData = (1e+06 * dg\$counts/expandAsMatrix(dg\$samples\$lib.size, dim(dg)))
+ efflib = dg\$samples\$lib.size*dg\$samples\$norm.factors
+ normData = (1e+06*dg\$counts/efflib)
+ outpdfname="edgeR_top_100_heatmap.pdf"
+ hmap2(normData,nsamp=100,TName=TName,group=group,outpdfname=outpdfname,myTitle=paste('edgeR Heatmap',myTitle))
+ outSmear = "edgeR_smearplot.pdf"
+ outMain = paste("Smear Plot for ",TName,' Vs ',CName,' (FDR@',fdrthresh,' N = ',nsig,')',sep='')
+ smearPlot(DGEList=DGEList,deTags=deTags, outSmear=outSmear, outMain = outMain)
+ qqPlot(descr=paste(myTitle,'edgeR adj p QQ plot'),pvector=tt\$adj.p.value,outpdf='edgeR_qqplot.pdf')
+ norm.factor = DGEList\$samples\$norm.factors
+ topresults.edgeR = soutput[which(soutput\$adj.p.value < fdrthresh), ]
+ edgeRcountsindex = which(allgenes %in% rownames(topresults.edgeR))
+ edgeRcounts = rep(0, length(allgenes))
+ edgeRcounts[edgeRcountsindex] = 1 # Create venn diagram of hits
+ sink()
+ } ### doedgeR
+ if (doDESeq2 == T)
+ {
+ sink("DESeq2.log")
+ # DESeq2
+ require('DESeq2')
+ library('RColorBrewer')
+ pdata = data.frame(Name=colnames(workCM),Rx=group,subjects=subjects,row.names=colnames(workCM))
+ if (length(subjects) == 0)
+ {
+ deSEQds = DESeqDataSetFromMatrix(countData = workCM, colData = pdata, design = formula(~ Rx))
+ } else {
+ deSEQds = DESeqDataSetFromMatrix(countData = workCM, colData = pdata, design = formula(~ subjects + Rx))
+ }
+ #DESeq2 = DESeq(deSEQds,fitType='local',pAdjustMethod=fdrtype)
+ #rDESeq = results(DESeq2)
+ #newCountDataSet(workCM, group)
+ deSeqDatsizefac = estimateSizeFactors(deSEQds)
+ deSeqDatdisp = estimateDispersions(deSeqDatsizefac,fitType=DESeq_fitType)
+ resDESeq = nbinomWaldTest(deSeqDatdisp, pAdjustMethod=fdrtype)
+ rDESeq = as.data.frame(results(resDESeq))
+ rDESeq = cbind(Contig=rownames(workCM),rDESeq,NReads=cmrowsums,URL=contigurls)
+ srDESeq = rDESeq[order(rDESeq\$pvalue),]
+ qqPlot(descr=paste(myTitle,'DESeq2 adj p qq plot'),pvector=rDESeq\$padj,outpdf='DESeq2_qqplot.pdf')
+ cat("# DESeq top 50\n")
+ print.noquote(srDESeq[1:50,])
+ write.table(srDESeq,file=out_DESeq2, quote=FALSE, sep="\t",row.names=F)
+ topresults.DESeq = rDESeq[which(rDESeq\$padj < fdrthresh), ]
+ DESeqcountsindex = which(allgenes %in% rownames(topresults.DESeq))
+ DESeqcounts = rep(0, length(allgenes))
+ DESeqcounts[DESeqcountsindex] = 1
+ pdf("DESeq2_dispersion_estimates.pdf")
+ plotDispEsts(resDESeq)
+ dev.off()
+ ysmall = abs(min(rDESeq\$log2FoldChange))
+ ybig = abs(max(rDESeq\$log2FoldChange))
+ ylimit = min(4,ysmall,ybig)
+ pdf("DESeq2_MA_plot.pdf")
+ plotMA(resDESeq,main=paste(myTitle,"DESeq2 MA plot"),ylim=c(-ylimit,ylimit))
+ dev.off()
+ rlogres = rlogTransformation(resDESeq)
+ sampledists = dist( t( assay(rlogres) ) )
+ sdmat = as.matrix(sampledists)
+ pdf("DESeq2_sample_distance_plot.pdf")
+ heatmap.2(sdmat,trace="none",main=paste(myTitle,"DESeq2 sample distances"),
+ col = colorRampPalette( rev(brewer.pal(9, "RdBu")) )(255))
+ dev.off()
+ ###outpdfname="DESeq2_top50_heatmap.pdf"
+ ###hmap2(sresDESeq,nsamp=50,TName=TName,group=group,outpdfname=outpdfname,myTitle=paste('DESeq2 vst rlog Heatmap',myTitle))
+ sink()
+ result = try( (ppca = plotPCA( varianceStabilizingTransformation(deSeqDatdisp,blind=T), intgroup=c("Rx","Name")) ) )
+ if ("try-error" %in% class(result)) {
+ print.noquote('DESeq2 plotPCA failed.')
+ } else {
+ pdf("DESeq2_PCA_plot.pdf")
+ #### wtf - print? Seems needed to get this to work
+ print(ppca)
+ dev.off()
+ }
+ }
+
+ if (doVoom == T) {
+ sink('VOOM.log')
+ if (doedgeR == F) {
+ #### Setup DGEList object
+ DGEList = DGEList(counts=workCM, group = group)
+ DGEList = calcNormFactors(DGEList)
+ DGEList = estimateGLMCommonDisp(DGEList,mydesign)
+ DGEList = estimateGLMTrendedDisp(DGEList,mydesign)
+ DGEList = estimateGLMTagwiseDisp(DGEList,mydesign)
+ DGEList = estimateGLMTagwiseDisp(DGEList,mydesign)
+ norm.factor = DGEList\$samples\$norm.factors
+ }
+ pdf("VOOM_mean_variance_plot.pdf")
+ dat.voomed = voom(DGEList, mydesign, plot = TRUE, lib.size = colSums(workCM) * norm.factor)
+ dev.off()
+ # Use limma to fit data
+ fit = lmFit(dat.voomed, mydesign)
+ fit = eBayes(fit)
+ rvoom = topTable(fit, coef = length(colnames(mydesign)), adj = fdrtype, n = Inf, sort="none")
+ qqPlot(descr=paste(myTitle,'VOOM-limma adj p QQ plot'),pvector=rvoom\$adj.P.Val,outpdf='VOOM_qqplot.pdf')
+ rownames(rvoom) = rownames(workCM)
+ rvoom = cbind(rvoom,NReads=cmrowsums,URL=contigurls)
+ srvoom = rvoom[order(rvoom\$P.Value),]
+ cat("# VOOM top 50\n")
+ print(srvoom[1:50,])
+ write.table(srvoom,file=out_VOOM, quote=FALSE, sep="\t",row.names=F)
+ # Use an FDR cutoff to find interesting samples for edgeR, DESeq and voom/limma
+ topresults.voom = rvoom[which(rvoom\$adj.P.Val < fdrthresh), ]
+ voomcountsindex = which(allgenes %in% topresults.voom\$ID)
+ voomcounts = rep(0, length(allgenes))
+ voomcounts[voomcountsindex] = 1
+ sink()
+ }
+
+ if (doCamera) {
+ doGSEA(y=DGEList,design=mydesign,histgmt=histgmt,bigmt=bigmt,ntest=20,myTitle=myTitle,
+ outfname=paste(mt,"GSEA.xls",sep="_"),fdrthresh=fdrthresh,fdrtype=fdrtype)
+ }
+
+ if ((doDESeq2==T) || (doVoom==T) || (doedgeR==T)) {
+ if ((doVoom==T) && (doDESeq2==T) && (doedgeR==T)) {
+ vennmain = paste(mt,'Voom,edgeR and DESeq2 overlap at FDR=',fdrthresh)
+ counts.dataframe = data.frame(edgeR = edgeRcounts, DESeq2 = DESeqcounts,
+ VOOM_limma = voomcounts, row.names = allgenes)
+ } else if ((doDESeq2==T) && (doedgeR==T)) {
+ vennmain = paste(mt,'DESeq2 and edgeR overlap at FDR=',fdrthresh)
+ counts.dataframe = data.frame(edgeR = edgeRcounts, DESeq2 = DESeqcounts, row.names = allgenes)
+ } else if ((doVoom==T) && (doedgeR==T)) {
+ vennmain = paste(mt,'Voom and edgeR overlap at FDR=',fdrthresh)
+ counts.dataframe = data.frame(edgeR = edgeRcounts, VOOM_limma = voomcounts, row.names = allgenes)
+ }
+
+ if (nrow(counts.dataframe > 1)) {
+ counts.venn = vennCounts(counts.dataframe)
+ vennf = "Venn_significant_genes_overlap.pdf"
+ pdf(vennf)
+ vennDiagram(counts.venn,main=vennmain,col="maroon")
+ dev.off()
+ }
+ } #### doDESeq2 or doVoom
+
+}
+#### Done
+
+###sink(stdout(),append=T,type="message")
+builtin_gmt = ""
+history_gmt = ""
+history_gmt_name = ""
+out_edgeR = F
+out_DESeq2 = F
+out_VOOM = "$out_VOOM"
+doDESeq2 = $DESeq2.doDESeq2 # make these T or F
+doVoom = $doVoom
+doCamera = $camera.doCamera
+doedgeR = $edgeR.doedgeR
+edgeR_priordf = 0
+
+
+#if $doVoom == "T":
+ out_VOOM = "$out_VOOM"
+#end if
+
+#if $DESeq2.doDESeq2 == "T":
+ out_DESeq2 = "$out_DESeq2"
+ DESeq_fitType = "$DESeq2.DESeq_fitType"
+#end if
+
+#if $edgeR.doedgeR == "T":
+ out_edgeR = "$out_edgeR"
+ edgeR_priordf = $edgeR.edgeR_priordf
+#end if
+
+#if $camera.doCamera == 'T'
+ #if $camera.gmtSource.refgmtSource == "indexed" or $camera.gmtSource.refgmtSource == "both":
+ builtin_gmt = "${camera.gmtSource.builtinGMT.fields.path}"
+ #end if
+ #if $camera.gmtSource.refgmtSource == "history" or $camera.gmtSource.refgmtSource == "both":
+ history_gmt = "${camera.gmtSource.ownGMT}"
+ history_gmt_name = "${camera.gmtSource.ownGMT.name}"
+ #end if
+#end if
+
+
+if (sum(c(doedgeR,doVoom,doDESeq2)) == 0)
+{
+write("No methods chosen - nothing to do! Please try again after choosing one or more methods", stderr())
+quit(save="no",status=2)
+}
+
+Out_Dir = "$html_file.files_path"
+Input = "$input1"
+TreatmentName = "$treatment_name"
+TreatmentCols = "$Treat_cols"
+ControlName = "$control_name"
+ControlCols= "$Control_cols"
+org = "$input1.dbkey"
+if (org == "") { org = "hg19"}
+fdrtype = "$fdrtype"
+fdrthresh = $fdrthresh
+useNDF = $useNDF
+fQ = $fQ # non-differential centile cutoff
+myTitle = "$title"
+sids = strsplit("$subjectids",',')
+subjects = unlist(sids)
+nsubj = length(subjects)
+TCols = as.numeric(strsplit(TreatmentCols,",")[[1]])-1
+CCols = as.numeric(strsplit(ControlCols,",")[[1]])-1
+cat('Got TCols=')
+cat(TCols)
+cat('; CCols=')
+cat(CCols)
+cat('\n')
+useCols = c(TCols,CCols)
+if (file.exists(Out_Dir) == F) dir.create(Out_Dir)
+Count_Matrix = read.table(Input,header=T,row.names=1,sep='\t') #Load tab file assume header
+snames = colnames(Count_Matrix)
+nsamples = length(snames)
+if (nsubj > 0 & nsubj != nsamples) {
+options("show.error.messages"=T)
+mess = paste('Fatal error: Supplied subject id list',paste(subjects,collapse=','),
+ 'has length',nsubj,'but there are',nsamples,'samples',paste(snames,collapse=','))
+write(mess, stderr())
+quit(save="no",status=4)
+}
+if (length(subjects) != 0) {subjects = subjects[useCols]}
+Count_Matrix = Count_Matrix[,useCols] ### reorder columns
+rn = rownames(Count_Matrix)
+islib = rn %in% c('librarySize','NotInBedRegions')
+LibSizes = Count_Matrix[subset(rn,islib),][1] # take first
+Count_Matrix = Count_Matrix[subset(rn,! islib),]
+group = c(rep(TreatmentName,length(TCols)), rep(ControlName,length(CCols)) ) #Build a group descriptor
+group = factor(group, levels=c(ControlName,TreatmentName))
+colnames(Count_Matrix) = paste(group,colnames(Count_Matrix),sep="_") #Relable columns
+results = edgeIt(Count_Matrix=Count_Matrix,group=group, out_edgeR=out_edgeR, out_VOOM=out_VOOM, out_DESeq2=out_DESeq2,
+ fdrtype='BH',mydesign=NULL,priordf=edgeR_priordf,fdrthresh=fdrthresh,outputdir='.',
+ myTitle=myTitle,useNDF=F,libSize=c(),filterquantile=fQ,subjects=subjects,
+ doDESeq2=doDESeq2,doVoom=doVoom,doCamera=doCamera,doedgeR=doedgeR,org=org,
+ histgmt=history_gmt,bigmt=builtin_gmt,DESeq_fitType=DESeq_fitType)
+sessionInfo()
+]]>
+</configfile>
+</configfiles>
+<help>
+
+**What it does**
+
+Allows short read sequence counts from controlled experiments to be analysed for differentially expressed genes.
+Optionally adds a term for subject if not all samples are independent or if some other factor needs to be blocked in the design.
+
+**Input**
+
+Requires a count matrix as a tabular file. These are best made using the companion HTSeq_ based counter Galaxy wrapper
+and your fave gene model to generate inputs. Each row is a genomic feature (gene or exon eg) and each column the
+non-negative integer count of reads from one sample overlapping the feature.
+The matrix must have a header row uniquely identifying the source samples, and unique row names in
+the first column. Typically the row names are gene symbols or probe ids for downstream use in GSEA and other methods.
+
+**Specifying comparisons**
+
+This is basically dumbed down for two factors - case vs control.
+
+More complex interfaces are possible but painful at present.
+Probably need to specify a phenotype file to do this better.
+Work in progress. Send code.
+
+If you have (eg) paired samples and wish to include a term in the GLM to account for some other factor (subject in the case of paired samples),
+put a comma separated list of indicators for every sample (whether modelled or not!) indicating (eg) the subject number or
+A list of integers, one for each subject or an empty string if samples are all independent.
+If not empty, there must be exactly as many integers in the supplied integer list as there are columns (samples) in the count matrix.
+Integers for samples that are not in the analysis *must* be present in the string as filler even if not used.
+
+So if you have 2 pairs out of 6 samples, you need to put in unique integers for the unpaired ones
+eg if you had 6 samples with the first two independent but the second and third pairs each being from independent subjects. you might use
+8,9,1,1,2,2
+as subject IDs to indicate two paired samples from the same subject in columns 3/4 and 5/6
+
+**Methods available**
+
+You can run 3 popular Bioconductor packages available for count data.
+
+edgeR - see edgeR_ for details
+
+VOOM/limma - see limma_VOOM_ for details
+
+DESeq2 - see DESeq2_ for details
+
+and optionally camera in edgeR which works better if MSigDB is installed.
+
+**Outputs**
+
+Some helpful plots and analysis results. Note that most of these are produced using R code
+suggested by the excellent documentation and vignettes for the Bioconductor
+packages invoked. The Tool Factory is used to automatically lay these out for you to enjoy.
+
+**Note on Voom**
+
+The voom from limma version 3.16.6 help in R includes this from the authors - but you should read the paper to interpret this method.
+
+This function is intended to process RNA-Seq or ChIP-Seq data prior to linear modelling in limma.
+
+voom is an acronym for mean-variance modelling at the observational level.
+The key concern is to estimate the mean-variance relationship in the data, then use this to compute appropriate weights for each observation.
+Count data almost show non-trivial mean-variance relationships. Raw counts show increasing variance with increasing count size, while log-counts typically show a decreasing mean-variance trend.
+This function estimates the mean-variance trend for log-counts, then assigns a weight to each observation based on its predicted variance.
+The weights are then used in the linear modelling process to adjust for heteroscedasticity.
+
+In an experiment, a count value is observed for each tag in each sample. A tag-wise mean-variance trend is computed using lowess.
+The tag-wise mean is the mean log2 count with an offset of 0.5, across samples for a given tag.
+The tag-wise variance is the quarter-root-variance of normalized log2 counts per million values with an offset of 0.5, across samples for a given tag.
+Tags with zero counts across all samples are not included in the lowess fit. Optional normalization is performed using normalizeBetweenArrays.
+Using fitted values of log2 counts from a linear model fit by lmFit, variances from the mean-variance trend were interpolated for each observation.
+This was carried out by approxfun. Inverse variance weights can be used to correct for mean-variance trend in the count data.
+
+
+Author(s)
+
+Charity Law and Gordon Smyth
+
+References
+
+Law, CW (2013). Precision weights for gene expression analysis. PhD Thesis. University of Melbourne, Australia.
+
+Law, CW, Chen, Y, Shi, W, Smyth, GK (2013). Voom! Precision weights unlock linear model analysis tools for RNA-seq read counts.
+Technical Report 1 May 2013, Bioinformatics Division, Walter and Eliza Hall Institute of Medical Reseach, Melbourne, Australia.
+http://www.statsci.org/smyth/pubs/VoomPreprint.pdf
+
+See Also
+
+A voom case study is given in the edgeR User's Guide.
+
+vooma is a similar function but for microarrays instead of RNA-seq.
+
+
+***old rant on changes to Bioconductor package variable names between versions***
+
+The edgeR authors made a small cosmetic change in the name of one important variable (from p.value to PValue)
+breaking this and all other code that assumed the old name for this variable,
+between edgeR2.4.4 and 2.4.6 (the version for R 2.14 as at the time of writing).
+This means that all code using edgeR is sensitive to the version. I think this was a very unwise thing
+to do because it wasted hours of my time to track down and will similarly cost other edgeR users dearly
+when their old scripts break. This tool currently now works with 2.4.6.
+
+**Note on prior.N**
+
+http://seqanswers.com/forums/showthread.php?t=5591 says:
+
+*prior.n*
+
+The value for prior.n determines the amount of smoothing of tagwise dispersions towards the common dispersion.
+You can think of it as like a "weight" for the common value. (It is actually the weight for the common likelihood
+in the weighted likelihood equation). The larger the value for prior.n, the more smoothing, i.e. the closer your
+tagwise dispersion estimates will be to the common dispersion. If you use a prior.n of 1, then that gives the
+common likelihood the weight of one observation.
+
+In answer to your question, it is a good thing to squeeze the tagwise dispersions towards a common value,
+or else you will be using very unreliable estimates of the dispersion. I would not recommend using the value that
+you obtained from estimateSmoothing()---this is far too small and would result in virtually no moderation
+(squeezing) of the tagwise dispersions. How many samples do you have in your experiment?
+What is the experimental design? If you have few samples (less than 6) then I would suggest a prior.n of at least 10.
+If you have more samples, then the tagwise dispersion estimates will be more reliable,
+so you could consider using a smaller prior.n, although I would hesitate to use a prior.n less than 5.
+
+
+From Bioconductor Digest, Vol 118, Issue 5, Gordon writes:
+
+Dear Dorota,
+
+The important settings are prior.df and trend.
+
+prior.n and prior.df are related through prior.df = prior.n * residual.df,
+and your experiment has residual.df = 36 - 12 = 24. So the old setting of
+prior.n=10 is equivalent for your data to prior.df = 240, a very large
+value. Going the other way, the new setting of prior.df=10 is equivalent
+to prior.n=10/24.
+
+To recover old results with the current software you would use
+
+ estimateTagwiseDisp(object, prior.df=240, trend="none")
+
+To get the new default from old software you would use
+
+ estimateTagwiseDisp(object, prior.n=10/24, trend=TRUE)
+
+Actually the old trend method is equivalent to trend="loess" in the new
+software. You should use plotBCV(object) to see whether a trend is
+required.
+
+Note you could also use
+
+ prior.n = getPriorN(object, prior.df=10)
+
+to map between prior.df and prior.n.
+
+----
+
+**Attributions**
+
+edgeR - edgeR_
+
+VOOM/limma - limma_VOOM_
+
+DESeq2 - DESeq2_ for details
+
+See above for Bioconductor package documentation for packages exposed in Galaxy by this tool and app store package.
+
+Galaxy_ (that's what you are using right now!) for gluing everything together
+
+Otherwise, all code and documentation comprising this tool was written by Ross Lazarus and is
+licensed to you under the LGPL_ like other rgenetics artefacts
+
+.. _LGPL: http://www.gnu.org/copyleft/lesser.html
+.. _HTSeq: http://www-huber.embl.de/users/anders/HTSeq/doc/index.html
+.. _edgeR: http://www.bioconductor.org/packages/release/bioc/html/edgeR.html
+.. _DESeq2: http://www.bioconductor.org/packages/release/bioc/html/DESeq2.html
+.. _limma_VOOM: http://www.bioconductor.org/packages/release/bioc/html/limma.html
+.. _Galaxy: http://getgalaxy.org
+</help>
+
+</tool>
+
+
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/95b2c165412d/
Changeset: 95b2c165412d
User: ross...(a)gmail.com
Date: 2013-08-05 02:27:13
Summary: fixing on_string again. Add job title to tophat2
Affected #: 4 files
diff -r f0cdc6bebfaa8757bb093d74508332515d5eec38 -r 95b2c165412d1a7c55c3f6173d6c0c0285dd788e lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -106,6 +106,7 @@
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
self.allow_user_deletion = string_as_bool( kwargs.get( "allow_user_deletion", "False" ) )
self.allow_user_dataset_purge = string_as_bool( kwargs.get( "allow_user_dataset_purge", "False" ) )
+ self.use_data_id_on_string = string_as_bool( kwargs.get( "use_data_id_on_string", "False" ) )
self.allow_user_impersonation = string_as_bool( kwargs.get( "allow_user_impersonation", "False" ) )
self.new_user_dataset_access_role_default_private = string_as_bool( kwargs.get( "new_user_dataset_access_role_default_private", "False" ) )
self.collect_outputs_from = [ x.strip() for x in kwargs.get( 'collect_outputs_from', 'new_file_path,job_working_directory' ).lower().split(',') ]
diff -r f0cdc6bebfaa8757bb093d74508332515d5eec38 -r 95b2c165412d1a7c55c3f6173d6c0c0285dd788e lib/galaxy/tools/actions/__init__.py
--- a/lib/galaxy/tools/actions/__init__.py
+++ b/lib/galaxy/tools/actions/__init__.py
@@ -191,14 +191,20 @@
data = data.to_history_dataset_association( None )
inp_data[name] = data
- else: # HDA
- if data.hid:
- input_names.append( 'data %s' % data.hid )
+# else: # HDA
+# if data.hid:
+# input_names.append( 'data %s' % data.hid )
input_ext = data.ext
if data.dbkey not in [None, '?']:
input_dbkey = data.dbkey
-
+ data_name_sane = re.sub('[^a-zA-Z0-9_]+', '', data.name)
+ if trans.app.config.use_data_id_on_string:
+ # we want names in our on_strings not numbers
+ input_names.append(data_name_sane)
+ else:
+ if data.hid:
+ input_names.append('data %s' % data.hid)
# Collect chromInfo dataset and add as parameters to incoming
db_datasets = {}
db_dataset = trans.db_dataset_for( input_dbkey )
@@ -232,11 +238,14 @@
if len( input_names ) == 1:
on_text = input_names[0]
elif len( input_names ) == 2:
- on_text = '%s and %s' % tuple(input_names[0:2])
+ #on_text = '%s and %s' % tuple(input_names[0:2])
+ on_text = '%s_%s' % tuple(input_names[0:2])
elif len( input_names ) == 3:
- on_text = '%s, %s, and %s' % tuple(input_names[0:3])
+ #on_text = '%s, %s, and %s' % tuple(input_names[0:3])
+ on_text = '%s_%s_%s' % tuple(input_names[0:3])
elif len( input_names ) > 3:
- on_text = '%s, %s, and others' % tuple(input_names[0:2])
+ #on_text = '%s, %s, and others' % tuple(input_names[0:2])
+ on_text = '%s_%s_and_others' % tuple(input_names[0:2])
else:
on_text = ""
# Add the dbkey to the incoming parameters
diff -r f0cdc6bebfaa8757bb093d74508332515d5eec38 -r 95b2c165412d1a7c55c3f6173d6c0c0285dd788e tools/ngs_rna/tophat2_wrapper.xml
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -126,6 +126,14 @@
</command><inputs>
+ <param name="jobname" type="text" value="Tophat2" size="80" label="Job title for outputs"
+ help="Output name to remind you what this was for">
+ <sanitizer invalid_char="">
+ <valid initial="string.letters,string.digits">
+ <add value="_" />
+ </valid>
+ </sanitizer>
+ </param><conditional name="singlePaired"><param name="sPaired" type="select" label="Is this library mate-paired?"><option value="single">Single-end</option>
@@ -273,19 +281,19 @@
</stdio><outputs>
- <data format="tabular" name="fusions" label="${on_string}_tophat2fus.xls" from_work_dir="tophat_out/fusions.out">
+ <data format="tabular" name="fusions" label="${on_string}_${jobname}_fusions.xls" from_work_dir="tophat_out/fusions.out"><filter>(params['settingsType'] == 'full' and params['fusion_search']['do_search'] == 'Yes')</filter></data>
- <data format="bed" name="insertions" label="${on_string}_tophat2ins.bed" from_work_dir="tophat_out/insertions.bed">
+ <data format="bed" name="insertions" label="${on_string}_${jobname}_ins.bed" from_work_dir="tophat_out/insertions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="deletions" label="${on_string}_tophhat2del.bed" from_work_dir="tophat_out/deletions.bed">
+ <data format="bed" name="deletions" label="${on_string}_${jobname}_del.bed" from_work_dir="tophat_out/deletions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="junctions" label="${on_string}tophat2sj.bed" from_work_dir="tophat_out/junctions.bed">
+ <data format="bed" name="junctions" label="${on_string}_${jobname}_splicejunc.bed" from_work_dir="tophat_out/junctions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bam" name="accepted_hits" label="${on_string}tophat2hits.bam" from_work_dir="tophat_out/accepted_hits.bam">
+ <data format="bam" name="accepted_hits" label="${on_string}_${jobname}_hits.bam" from_work_dir="tophat_out/accepted_hits.bam"><expand macro="dbKeyActions" /></data></outputs>
diff -r f0cdc6bebfaa8757bb093d74508332515d5eec38 -r 95b2c165412d1a7c55c3f6173d6c0c0285dd788e tools/sr_mapping/bowtie2_wrapper.xml
--- a/tools/sr_mapping/bowtie2_wrapper.xml
+++ b/tools/sr_mapping/bowtie2_wrapper.xml
@@ -95,15 +95,14 @@
#end if
## view/sort and output file
- | samtools view -Su - | samtools sort -o - - > $output;
+ | samtools view -Su - | samtools sort -o - - > $output
## rename unaligned sequence files
#if $library.type == "paired" and $output_unaligned_reads_l and $output_unaligned_reads_r:
#set left = str($output_unaligned_reads_l).replace( '.dat', '.1.dat' )
#set right = str($output_unaligned_reads_l).replace( '.dat', '.2.dat' )
-
- mv $left $output_unaligned_reads_l;
- mv $right $output_unaligned_reads_r;
+ ;mv $left $output_unaligned_reads_l;
+ mv $right $output_unaligned_reads_r
#end if
</command>
@@ -218,7 +217,7 @@
<!-- define outputs --><outputs>
- <data format="fastqsanger" name="output_unaligned_reads_l" label="${on_string}_{jobtitle}_unaligL.fastq" >
+ <data format="fastqsanger" name="output_unaligned_reads_l" label="${on_string}_${jobtitle}_unaligL.fastq" ><filter>unaligned_file is True</filter><actions><action type="format">
@@ -226,7 +225,7 @@
</action></actions></data>
- <data format="fastqsanger" name="output_unaligned_reads_r" label="${on_string}_{jobtitle}_unaligR.fastq)">
+ <data format="fastqsanger" name="output_unaligned_reads_r" label="${on_string}_${jobtitle}_unaligR.fastq)"><filter>library['type'] == "paired" and unaligned_file is True</filter><actions><action type="format">
@@ -234,7 +233,7 @@
</action></actions></data>
- <data format="bam" name="output" label="${tool.name} on ${on_string}_{jobtitle}_aligned.bam">
+ <data format="bam" name="output" label="${tool.name} on ${on_string}_${jobtitle}_aligned.bam"><actions><conditional name="reference_genome.source"><when value="indexed">
https://bitbucket.org/galaxy/galaxy-central/commits/fcfd3af76225/
Changeset: fcfd3af76225
User: ross...(a)gmail.com
Date: 2013-08-07 01:45:35
Summary: monkeypatches to output.py
Affected #: 3 files
diff -r 95b2c165412d1a7c55c3f6173d6c0c0285dd788e -r fcfd3af762255910c5959c8c8147af90ab4038bc lib/galaxy/tools/actions/__init__.py
--- a/lib/galaxy/tools/actions/__init__.py
+++ b/lib/galaxy/tools/actions/__init__.py
@@ -1,5 +1,6 @@
import os
import galaxy.tools
+import re
from galaxy.exceptions import ObjectInvalid
from galaxy.model import LibraryDatasetDatasetAssociation
diff -r 95b2c165412d1a7c55c3f6173d6c0c0285dd788e -r fcfd3af762255910c5959c8c8147af90ab4038bc lib/galaxy/tools/parameters/output.py
--- a/lib/galaxy/tools/parameters/output.py
+++ b/lib/galaxy/tools/parameters/output.py
@@ -215,9 +215,13 @@
self.offset = elem.get( 'offset', -1 )
self.offset = int( self.offset )
else:
+ self.options = []
self.missing_tool_data_table_name = self.name
def get_value( self, other_values ):
- options = self.options
+ if self.options:
+ options = self.options
+ else:
+ options = []
for filter in self.filters:
options = filter.filter_options( options, other_values )
try:
diff -r 95b2c165412d1a7c55c3f6173d6c0c0285dd788e -r fcfd3af762255910c5959c8c8147af90ab4038bc tool-data/bowtie2_indices.loc.sample
--- a/tool-data/bowtie2_indices.loc.sample
+++ b/tool-data/bowtie2_indices.loc.sample
@@ -1,37 +1,37 @@
-# bowtie2_indices.loc.sample
-# This is a *.loc.sample file distributed with Galaxy that enables tools
-# to use a directory of indexed data files. This one is for Bowtie2 and Tophat2.
-# See the wiki: http://wiki.galaxyproject.org/Admin/NGS%20Local%20Setup
-# First create these data files and save them in your own data directory structure.
-# Then, create a bowtie_indices.loc file to use those indexes with tools.
-# Copy this file, save it with the same name (minus the .sample),
-# follow the format examples, and store the result in this directory.
-# The file should include an one line entry for each index set.
-# The path points to the "basename" for the set, not a specific file.
-# It has four text columns seperated by TABS.
+#This is a sample file distributed with Galaxy that enables tools
+#to use a directory of Bowtie2 indexed sequences data files. You will
+#need to create these data files and then create a bowtie_indices.loc
+#file similar to this one (store it in this directory) that points to
+#the directories in which those files are stored. The bowtie2_indices.loc
+#file has this format (longer white space characters are TAB characters):
#
-# <unique_build_id><dbkey><display_name><file_base_path>
+#<unique_build_id><dbkey><display_name><file_base_path>
#
-# So, for example, if you had hg18 indexes stored in:
+#So, for example, if you had hg18 indexed stored in
+#/depot/data2/galaxy/bowtie2/hg18/,
+#then the bowtie2_indices.loc entry would look like this:
#
-# /depot/data2/galaxy/hg19/bowtie2/
+#hg18 hg18 hg18 /depot/data2/galaxy/bowtie2/hg18/hg18
#
-# containing hg19 genome and hg19.*.bt2 files, such as:
-# -rw-rw-r-- 1 james james 914M Feb 10 18:56 hg19canon.fa
-# -rw-rw-r-- 1 james james 914M Feb 10 18:56 hg19canon.1.bt2
-# -rw-rw-r-- 1 james james 683M Feb 10 18:56 hg19canon.2.bt2
-# -rw-rw-r-- 1 james james 3.3K Feb 10 16:54 hg19canon.3.bt2
-# -rw-rw-r-- 1 james james 683M Feb 10 16:54 hg19canon.4.bt2
-# -rw-rw-r-- 1 james james 914M Feb 10 20:45 hg19canon.rev.1.bt2
-# -rw-rw-r-- 1 james james 683M Feb 10 20:45 hg19canon.rev.2.bt2
+#and your /depot/data2/galaxy/bowtie2/hg18/ directory
+#would contain hg18.*.ebwt files:
#
-# then the bowtie2_indices.loc entry could look like this:
+#-rw-r--r-- 1 james universe 830134 2005-09-13 10:12 hg18.1.ebwt
+#-rw-r--r-- 1 james universe 527388 2005-09-13 10:12 hg18.2.ebwt
+#-rw-r--r-- 1 james universe 269808 2005-09-13 10:12 hg18.3.ebwt
+#...etc...
#
-#hg19 hg19 Human (hg19) /depot/data2/galaxy/hg19/bowtie2/hg19canon
+#Your bowtie2_indices.loc file should include an entry per line for each
+#index set you have stored. The "file" in the path does not actually
+#exist, but it is the prefix for the actual index files. For example:
#
-#More examples:
+#hg18canon hg18 hg18 Canonical /depot/data2/galaxy/bowtie2/hg18/hg18canon
+#hg18full hg18 hg18 Full /depot/data2/galaxy/bowtie2/hg18/hg18full
+#/orig/path/hg19 hg19 hg19 /depot/data2/galaxy/bowtie2/hg19/hg19
+#...etc...
#
-#mm10 mm10 Mouse (mm10) /depot/data2/galaxy/mm10/bowtie2/mm10
-#dm3 dm3 D. melanogaster (dm3) /depot/data2/galaxy/mm10/bowtie2/dm3
+#Note that for backwards compatibility with workflows, the unique ID of
+#an entry must be the path that was in the original loc file, because that
+#is the value stored in the workflow for that parameter. That is why the
+#hg19 entry above looks odd. New genomes can be better-looking.
#
-#
https://bitbucket.org/galaxy/galaxy-central/commits/da94b2539602/
Changeset: da94b2539602
User: ross...(a)gmail.com
Date: 2013-08-07 01:53:35
Summary: branch merge
Affected #: 79 files
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -60,7 +60,7 @@
job_conf.xml
data_manager_conf.xml
shed_data_manager_conf.xml
-config/visualizations/*.xml
+config/*
static/welcome.html.*
static/welcome.html
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b config/plugins/visualizations/README.txt
--- /dev/null
+++ b/config/plugins/visualizations/README.txt
@@ -0,0 +1,34 @@
+Custom visualization plugins
+----------------------------
+
+Visualizations can be added to your Galaxy instance by creating
+sub-directories, templates, and static files here.
+
+Properly configured and written visualizations will be accessible to
+the user when they click the 'visualizations' icon for a dataset
+in their history panel.
+
+The framework must be enabled in your 'universe_wsgi.ini' file by
+uncommenting (and having a valid path for) the
+'visualizations_plugin_directory' entry.
+
+For more information, see http://wiki.galaxyproject.org/VisualizationsRegistry
+
+
+Sub-directory structure
+-----------------------
+
+In general, sub-directories should follow the pattern:
+
+ my_visualization/
+ config/
+ my_visualization.xml
+ static/
+ ... any static files the visualization needs (if any)
+ templates/
+ ... any Mako templates the visualization needs
+
+The XML config file for a visualization plugin can be validated on the command
+line using (from your plugin directory):
+
+ xmllint my_visualization/config/my_visualization.xml --valid --noout
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b config/plugins/visualizations/visualization.dtd
--- /dev/null
+++ b/config/plugins/visualizations/visualization.dtd
@@ -0,0 +1,130 @@
+<!-- each visualization must have a template (all other elements are optional) -->
+<!ELEMENT visualization (data_sources*,params*,template_root*,template,link_text*,render_location*)>
+<!-- visualization name (e.g. 'trackster', 'scatterplot', etc.) is required -->
+<!ATTLIST visualization
+ name CDATA #REQUIRED
+>
+
+<!ELEMENT data_sources (data_source*)>
+<!-- data sources are elements that describe what objects (HDAs, LDDAs, Job, User, etc.)
+ are applicable to a visualization. Often these are used to fetch applicable links
+ to the visualizations that use them.
+-->
+ <!ELEMENT data_source (model_class,(test|to_param)*)>
+ <!ELEMENT model_class (#PCDATA)>
+ <!-- model_class is currently the class name of the object you want to make a visualization
+ applicable to (e.g. HistoryDatasetAssociation). Currently only classes in galaxy.model
+ can be used.
+ REQUIRED and currently limited to: 'HistoryDatasetAssociation', 'LibraryDatasetDatasetAssociation'
+ -->
+ <!ELEMENT test (#PCDATA)>
+ <!-- tests help define what conditions the visualization can be applied to the model_class/target.
+ Currently, all tests are OR'd and there is no logical grouping. Tests are run in order.
+ (text): the text of this element is what the given target will be compared to (REQUIRED)
+ type: what type of test to run (e.g. when the target is an HDA the test will often be of type 'isinstance'
+ and test whether the HDA's datatype isinstace of a class)
+ DEFAULT: string comparison.
+ test_attr: what attribute of the target object should be used in the test. For instance, 'datatype'
+ will attempt to get the HDA.datatype from a target HDA. If the given object doesn't have
+ that attribute the test will fail (with no error). test_attr can be dot separated attributes,
+ looking up each in turn. For example, if the target was a history, one could access the
+ history.user.email by setting test_attr to 'user.email' (why you would want that, I don't know)
+ DEFAULT: to comparing the object itself (and not any of it's attributes)
+ result_type: if the result (the text of the element mentioned above) needs to be parsed into
+ something other than a string, result_type will tell the registry how to do this. E.g.
+ if result_type is 'datatype' the registry will assume the text is a datatype class name
+ and parse it into the proper class before the test (often 'isinstance') is run.
+ DEFAULT: no parsing (result should be a string)
+ -->
+ <!ATTLIST test
+ type CDATA #IMPLIED
+ test_attr CDATA #IMPLIED
+ result_type CDATA #IMPLIED
+ >
+
+ <!ELEMENT to_param (#PCDATA)>
+ <!-- to_param tells the registry how to parse the data_source into a query string param.
+ For example, HDA data_sources can set param_to text to 'dataset_id' and param_attr to 'id' and the
+ the target HDA (if it passes the tests) will be passed as "dataset_id=HDA.id"
+ (text): the query string param key this source will be parsed into (e.g. dataset_id)
+ REQUIRED
+ param_attr: the attribute of the data_source object to use as the value in the query string param.
+ E.g. param_attr='id' for an HDA data_source would use the (encoded) id.
+ NOTE: a to_param MUST have either a param_attr or assign
+ assign: you can use this to directly assign a value to a query string's param. E.g. if the
+ data_source is a LDDA we can set 'hda_or_ldda=ldda' using assign='ldda'.
+ NOTE: a to_param MUST have either a param_attr or assign
+ -->
+ <!ATTLIST to_param
+ param_attr CDATA #IMPLIED
+ assign CDATA #IMPLIED
+ >
+
+<!ELEMENT params ((param|param_modifier)*)>
+<!-- params describe what data will be sent to a visualization template and
+ how to convert them from a query string in a URL into variables usable in a template.
+ For example,
+ param_modifiers are a special class of parameters that modify other params
+ (e.g. hda_ldda can be 'hda' or 'ldda' and modifies/informs dataset_id to fetch an HDA or LDDA)
+-->
+ <!ELEMENT param (#PCDATA)>
+ <!-- param tells the registry how to parse the query string param back into a resource/data_source.
+ For example, if a query string has "dataset_id=NNN" and the type is 'dataset', the registry
+ will attempt to fetch the hda with id of NNN from the database and pass it to the template.
+ (text): the query string param key this source will be parsed from (e.g. dataset_id)
+ REQUIRED
+ type: the type of the resource.
+ Can be: str (DEFAULT), bool, int, float, json, visualization, dbkey, dataset, or hda_ldda.
+ default: if a param is not passed on the query string (and is not required) OR the given param
+ fails to parse, this value is used instead.
+ DEFAULT: None
+ required: set this to true if the param is required for the template. Rendering will with an error
+ if the param hasn't been sent.
+ DEFAULT: false
+ csv: set this to true if the param is a comma separated list. The registry will attempt to
+ parse each value as the given type and send the result as a list to the template.
+ DEFAULT: false
+ constrain_to: (currently unused) constain a param to a set of values, error if not valid.
+ DEFAULT: don't constrain
+ var_name_in_template: a new name for the resource/variable to use in the template. E.g. an initial
+ query string param key might be 'dataset_id' in the URL, the registry parses it into an HDA,
+ and if var_name_in_template is set to 'hda', the template will be able to access the HDA
+ with the variable name 'hda' (as in hda.title).
+ DEFAULT: keep the original query string name
+ -->
+ <!ATTLIST param
+ type CDATA #IMPLIED
+ default CDATA #IMPLIED
+ required CDATA #IMPLIED
+ csv CDATA #IMPLIED
+ constrain_to CDATA #IMPLIED
+ var_name_in_template CDATA #IMPLIED
+ >
+ <!-- param_modifiers are the same as param but have a REQUIRED 'modifies' attribute.
+ 'modifies' must point to the param name (the text part of param element) that it will modify.
+ E.g. <param_modifier modifies="dataset_id">hda_ldda</param_modifier>
+ -->
+ <!ELEMENT param_modifier (#PCDATA)>
+ <!ATTLIST param_modifier
+ modifies CDATA #REQUIRED
+ type CDATA #IMPLIED
+ default CDATA #IMPLIED
+ required CDATA #IMPLIED
+ csv CDATA #IMPLIED
+ constrain_to CDATA #IMPLIED
+ 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)>
+<!-- link_text: the text component of an html anchor displayed when the registry builds the link information -->
+<!ELEMENT link_text (#PCDATA)>
+<!-- render_location: used as the target attribute of the link to the visualization.
+ Can be 'galaxy_main', '_top', '_blank'. DEFAULT: 'galaxy_main'
+-->
+<!-- TODO: rename -> render_target -->
+<!ELEMENT render_location (#PCDATA)>
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b config/plugins/visualizations/visualization_base.mako
--- /dev/null
+++ b/config/plugins/visualizations/visualization_base.mako
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+<% _=n_ %>
+
+%if embedded:
+ ${self.as_embedded()}
+%else:
+ ${self.as_page()}
+%endif
+
+## render this inside another page or via ajax
+<%def name="as_embedded()">
+ ${self.stylesheets()}
+ ${self.javascripts()}
+ ${self.get_body()}
+</%def>
+
+## render this as it's own page
+<%def name="as_page()">
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>${self.title()}</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ ${self.metas()}
+ ${self.stylesheets()}
+ ${self.javascripts()}
+ </head>
+ <body>
+ ${self.get_body()}
+ </body>
+</html>
+</%def>
+##TODO: late_javascripts
+
+## Default body
+<%def name="get_body()"></%def>
+
+## Default title
+<%def name="title()">${visualization_name}</%def>
+
+## Additional metas can be defined by templates inheriting from this one.
+<%def name="metas()"></%def>
+
+## Default stylesheets
+<%def name="stylesheets()">
+${h.css('base')}
+</%def>
+
+## Default javascripts
+<%def name="javascripts()">
+${h.js(
+ "libs/jquery/jquery",
+ "libs/jquery/jquery.migrate"
+)}
+
+<script type="text/javascript">
+ // console protection
+ window.console = window.console || {
+ log : function(){},
+ debug : function(){},
+ info : function(){},
+ warn : function(){},
+ error : function(){},
+ assert : function(){}
+ };
+</script>
+</%def>
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b config/visualizations/circster.xml.sample
--- a/config/visualizations/circster.xml.sample
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE visualization SYSTEM "visualization.dtd">
-<visualization name="circster">
- <data_sources>
- <data_source>
- <model_class>HistoryDatasetAssociation</model_class>
- <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
- <to_param param_attr="id">dataset_id</to_param>
- <to_param assign="hda">hda_ldda</to_param>
- </data_source>
- <data_source>
- <model_class>LibraryDatasetDatasetAssociation</model_class>
- <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
- <to_param param_attr="id">dataset_id</to_param>
- <to_param assign="ldda">hda_ldda</to_param>
- </data_source>
- </data_sources>
- <params>
- <param type="visualization">id</param>
- <param type="hda_or_ldda">dataset_id</param>
- <param_modifier type="string" modifies="dataset_id">hda_ldda</param_modifier>
- <param type="dbkey">dbkey</param>
- </params>
- <template>circster.mako</template>
- <render_location>_top</render_location>
-</visualization>
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b config/visualizations/phyloviz.xml.sample
--- a/config/visualizations/phyloviz.xml.sample
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE visualization SYSTEM "visualization.dtd">
-<visualization name="phyloviz">
- <data_sources>
- <data_source>
- <model_class>HistoryDatasetAssociation</model_class>
- <test type="isinstance" test_attr="datatype" result_type="datatype">data.Newick</test>
- <test type="isinstance" test_attr="datatype" result_type="datatype">data.Nexus</test>
- <to_param param_attr="id">dataset_id</to_param>
- </data_source>
- </data_sources>
- <params>
- <param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
- <param type="integer" default="0">tree_index</param>
- </params>
- <template>phyloviz.mako</template>
- <render_location>_top</render_location>
-</visualization>
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b config/visualizations/scatterplot.xml.sample
--- a/config/visualizations/scatterplot.xml.sample
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE visualization SYSTEM "visualization.dtd">
-<visualization name="scatterplot">
- <data_sources>
- <data_source>
- <model_class>HistoryDatasetAssociation</model_class>
- <test type="isinstance" test_attr="datatype" result_type="datatype">tabular.Tabular</test>
- <to_param param_attr="id">dataset_id</to_param>
- </data_source>
- </data_sources>
- <params>
- <param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
- </params>
- <template>scatterplot.mako</template>
-</visualization>
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b config/visualizations/sweepster.xml.sample
--- a/config/visualizations/sweepster.xml.sample
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE visualization SYSTEM "visualization.dtd">
-<visualization name="sweepster">
- <data_sources>
- <data_source>
- <model_class>HistoryDatasetAssociation</model_class>
- <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
- <to_param param_attr="id">dataset_id</to_param>
- <to_param assign="hda">hda_ldda</to_param>
- </data_source>
- <data_source>
- <model_class>LibraryDatasetDatasetAssociation</model_class>
- <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
- <to_param param_attr="id">dataset_id</to_param>
- <to_param assign="ldda">hda_ldda</to_param>
- </data_source>
- </data_sources>
- <params>
- <param type="visualization" var_name_in_template="viz">visualization</param>
- <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>sweepster.mako</template>
- <render_location>_top</render_location>
-</visualization>
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b config/visualizations/trackster.xml.sample
--- a/config/visualizations/trackster.xml.sample
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE visualization SYSTEM "visualization.dtd">
-<visualization name="trackster">
- <!--not tested yet -->
- <data_sources>
- <data_source>
- <model_class>HistoryDatasetAssociation</model_class>
- <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
- <to_param param_attr="id">dataset_id</to_param>
- <to_param assign="hda">hda_ldda</to_param>
- <to_param param_attr="dbkey">dbkey</to_param>
- </data_source>
- <data_source>
- <model_class>LibraryDatasetDatasetAssociation</model_class>
- <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
- <to_param param_attr="id">dataset_id</to_param>
- <to_param assign="ldda">hda_ldda</to_param>
- </data_source>
- </data_sources>
- <params>
- <param type="visualization">id</param>
- <param type="dataset">dataset_id</param>
- <param type="genome_region">genome_region</param>
- <param type="dbkey">dbkey</param>
- </params>
- <template_root>tracks</template_root>
- <template>browser.mako</template>
- <render_location>_top</render_location>
-</visualization>
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b config/visualizations/visualization.dtd
--- a/config/visualizations/visualization.dtd
+++ /dev/null
@@ -1,132 +0,0 @@
-<!-- runnable on NIX with xmllint -->
-
-<!-- each visualization must have a template (all other elements are optional) -->
-<!ELEMENT visualization (data_sources*,params*,template_root*,template,link_text*,render_location*)>
-<!-- visualization name (e.g. 'trackster', 'scatterplot', etc.) is required -->
-<!ATTLIST visualization
- name CDATA #REQUIRED
->
-
-<!ELEMENT data_sources (data_source*)>
-<!-- data sources are elements that describe what objects (HDAs, LDDAs, Job, User, etc.)
- are applicable to a visualization. Often these are used to fetch applicable links
- to the visualizations that use them.
--->
- <!ELEMENT data_source (model_class,(test|to_param)*)>
- <!ELEMENT model_class (#PCDATA)>
- <!-- model_class is currently the class name of the object you want to make a visualization
- applicable to (e.g. HistoryDatasetAssociation). Currently only classes in galaxy.model
- can be used.
- REQUIRED and currently limited to: 'HistoryDatasetAssociation', 'LibraryDatasetDatasetAssociation'
- -->
- <!ELEMENT test (#PCDATA)>
- <!-- tests help define what conditions the visualization can be applied to the model_class/target.
- Currently, all tests are OR'd and there is no logical grouping. Tests are run in order.
- (text): the text of this element is what the given target will be compared to (REQUIRED)
- type: what type of test to run (e.g. when the target is an HDA the test will often be of type 'isinstance'
- and test whether the HDA's datatype isinstace of a class)
- DEFAULT: string comparison.
- test_attr: what attribute of the target object should be used in the test. For instance, 'datatype'
- will attempt to get the HDA.datatype from a target HDA. If the given object doesn't have
- that attribute the test will fail (with no error). test_attr can be dot separated attributes,
- looking up each in turn. For example, if the target was a history, one could access the
- history.user.email by setting test_attr to 'user.email' (why you would want that, I don't know)
- DEFAULT: to comparing the object itself (and not any of it's attributes)
- result_type: if the result (the text of the element mentioned above) needs to be parsed into
- something other than a string, result_type will tell the registry how to do this. E.g.
- if result_type is 'datatype' the registry will assume the text is a datatype class name
- and parse it into the proper class before the test (often 'isinstance') is run.
- DEFAULT: no parsing (result should be a string)
- -->
- <!ATTLIST test
- type CDATA #IMPLIED
- test_attr CDATA #IMPLIED
- result_type CDATA #IMPLIED
- >
-
- <!ELEMENT to_param (#PCDATA)>
- <!-- to_param tells the registry how to parse the data_source into a query string param.
- For example, HDA data_sources can set param_to text to 'dataset_id' and param_attr to 'id' and the
- the target HDA (if it passes the tests) will be passed as "dataset_id=HDA.id"
- (text): the query string param key this source will be parsed into (e.g. dataset_id)
- REQUIRED
- param_attr: the attribute of the data_source object to use as the value in the query string param.
- E.g. param_attr='id' for an HDA data_source would use the (encoded) id.
- NOTE: a to_param MUST have either a param_attr or assign
- assign: you can use this to directly assign a value to a query string's param. E.g. if the
- data_source is a LDDA we can set 'hda_or_ldda=ldda' using assign='ldda'.
- NOTE: a to_param MUST have either a param_attr or assign
- -->
- <!ATTLIST to_param
- param_attr CDATA #IMPLIED
- assign CDATA #IMPLIED
- >
-
-<!ELEMENT params ((param|param_modifier)*)>
-<!-- params describe what data will be sent to a visualization template and
- how to convert them from a query string in a URL into variables usable in a template.
- For example,
- param_modifiers are a special class of parameters that modify other params
- (e.g. hda_ldda can be 'hda' or 'ldda' and modifies/informs dataset_id to fetch an HDA or LDDA)
--->
- <!ELEMENT param (#PCDATA)>
- <!-- param tells the registry how to parse the query string param back into a resource/data_source.
- For example, if a query string has "dataset_id=NNN" and the type is 'dataset', the registry
- will attempt to fetch the hda with id of NNN from the database and pass it to the template.
- (text): the query string param key this source will be parsed from (e.g. dataset_id)
- REQUIRED
- type: the type of the resource.
- Can be: str (DEFAULT), bool, int, float, json, visualization, dbkey, dataset, or hda_ldda.
- default: if a param is not passed on the query string (and is not required) OR the given param
- fails to parse, this value is used instead.
- DEFAULT: None
- required: set this to true if the param is required for the template. Rendering will with an error
- if the param hasn't been sent.
- DEFAULT: false
- csv: set this to true if the param is a comma separated list. The registry will attempt to
- parse each value as the given type and send the result as a list to the template.
- DEFAULT: false
- constrain_to: (currently unused) constain a param to a set of values, error if not valid.
- DEFAULT: don't constrain
- var_name_in_template: a new name for the resource/variable to use in the template. E.g. an initial
- query string param key might be 'dataset_id' in the URL, the registry parses it into an HDA,
- and if var_name_in_template is set to 'hda', the template will be able to access the HDA
- with the variable name 'hda' (as in hda.title).
- DEFAULT: keep the original query string name
- -->
- <!ATTLIST param
- type CDATA #IMPLIED
- default CDATA #IMPLIED
- required CDATA #IMPLIED
- csv CDATA #IMPLIED
- constrain_to CDATA #IMPLIED
- var_name_in_template CDATA #IMPLIED
- >
- <!-- param_modifiers are the same as param but have a REQUIRED 'modifies' attribute.
- 'modifies' must point to the param name (the text part of param element) that it will modify.
- E.g. <param_modifier modifies="dataset_id">hda_ldda</param_modifier>
- -->
- <!ELEMENT param_modifier (#PCDATA)>
- <!ATTLIST param_modifier
- modifies CDATA #REQUIRED
- type CDATA #IMPLIED
- default CDATA #IMPLIED
- required CDATA #IMPLIED
- csv CDATA #IMPLIED
- constrain_to CDATA #IMPLIED
- 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)>
-<!-- link_text: the text component of an html anchor displayed when the registry builds the link information -->
-<!ELEMENT link_text (#PCDATA)>
-<!-- render_location: used as the target attribute of the link to the visualization.
- Can be 'galaxy_main', '_top', '_blank'. DEFAULT: 'galaxy_main'
--->
-<!-- TODO: rename -> render_target -->
-<!ELEMENT render_location (#PCDATA)>
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/app.py
--- a/lib/galaxy/app.py
+++ b/lib/galaxy/app.py
@@ -54,7 +54,7 @@
from tool_shed.galaxy_install.migrate.check import verify_tools
verify_tools( self, db_url, kwargs.get( 'global_conf', {} ).get( '__file__', None ), self.config.database_engine_options )
# Object store manager
- self.object_store = build_object_store_from_config(self.config)
+ self.object_store = build_object_store_from_config(self.config, fsmon=True)
# Setup the database engine and ORM
from galaxy.model import mapping
self.model = mapping.init( self.config.file_path,
@@ -123,10 +123,8 @@
# Load genome indexer tool.
load_genome_index_tools( self.toolbox )
# visualizations registry: associates resources with visualizations, controls how to render
- self.visualizations_registry = None
- if self.config.visualizations_config_directory:
- self.visualizations_registry = VisualizationsRegistry( self.config.root,
- self.config.visualizations_config_directory )
+ self.visualizations_registry = VisualizationsRegistry.from_config(
+ self.config.visualizations_plugins_directory, self.config )
# Load security policy.
self.security_agent = self.model.security_agent
self.host_security_agent = galaxy.security.HostAgent( model=self.security_agent.model, permitted_actions=self.security_agent.permitted_actions )
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -76,21 +76,24 @@
self.tool_data_table_config_path = resolve_path( kwargs.get( 'tool_data_table_config_path', 'tool_data_table_conf.xml' ), self.root )
self.shed_tool_data_table_config = resolve_path( kwargs.get( 'shed_tool_data_table_config', 'shed_tool_data_table_conf.xml' ), self.root )
self.enable_tool_shed_check = string_as_bool( kwargs.get( 'enable_tool_shed_check', False ) )
+ self.running_functional_tests = string_as_bool( kwargs.get( 'running_functional_tests', False ) )
self.hours_between_check = kwargs.get( 'hours_between_check', 12 )
try:
- hbc_test = int( self.hours_between_check )
- self.hours_between_check = hbc_test
- if self.hours_between_check < 1 or self.hours_between_check > 24:
+ if isinstance( self.hours_between_check, int ):
+ if self.hours_between_check < 1 or self.hours_between_check > 24:
+ self.hours_between_check = 12
+ elif isinstance( self.hours_between_check, float ):
+ # If we're running functional tests, the minimum hours between check should be reduced to 0.001, or 3.6 seconds.
+ if self.running_functional_tests:
+ if self.hours_between_check < 0.001 or self.hours_between_check > 24.0:
+ self.hours_between_check = 12.0
+ else:
+ if self.hours_between_check < 1.0 or self.hours_between_check > 24.0:
+ self.hours_between_check = 12.0
+ else:
self.hours_between_check = 12
except:
- try:
- # Float values are supported for functional tests.
- hbc_test = float( self.hours_between_check )
- self.hours_between_check = hbc_test
- if self.hours_between_check < 0.001 or self.hours_between_check > 24.0:
- self.hours_between_check = 12.0
- except:
- self.hours_between_check = 12
+ self.hours_between_check = 12
self.update_integrated_tool_panel = kwargs.get( "update_integrated_tool_panel", True )
self.enable_data_manager_user_view = string_as_bool( kwargs.get( "enable_data_manager_user_view", "False" ) )
self.data_manager_config_file = resolve_path( kwargs.get('data_manager_config_file', 'data_manager_conf.xml' ), self.root )
@@ -277,7 +280,6 @@
self.biostar_url = kwargs.get( 'biostar_url', None )
self.biostar_key_name = kwargs.get( 'biostar_key_name', None )
self.biostar_key = kwargs.get( 'biostar_key', None )
- self.running_functional_tests = string_as_bool( kwargs.get( 'running_functional_tests', False ) )
# Experimental: This will not be enabled by default and will hide
# nonproduction code.
# The api_folders refers to whether the API exposes the /folders section.
@@ -290,8 +292,10 @@
self.fluent_log = string_as_bool( kwargs.get( 'fluent_log', False ) )
self.fluent_host = kwargs.get( 'fluent_host', 'localhost' )
self.fluent_port = int( kwargs.get( 'fluent_port', 24224 ) )
- # visualization registries config directory
- self.visualizations_config_directory = kwargs.get( 'visualizations_config_directory', None )
+ # PLUGINS:
+ self.plugin_frameworks = []
+ # visualization framework
+ self.visualizations_plugins_directory = kwargs.get( 'visualizations_plugins_directory', None )
@property
def sentry_dsn_public( self ):
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/jobs/runners/condor.py
--- a/lib/galaxy/jobs/runners/condor.py
+++ b/lib/galaxy/jobs/runners/condor.py
@@ -246,12 +246,12 @@
if job_id is None:
self.put( job_wrapper )
return
- cjs = CondorJobState()
+ cjs = CondorJobState( job_wrapper=job_wrapper, files_dir=self.app.config.cluster_files_directory )
cjs.job_id = str( job_id )
cjs.command_line = job.get_command_line()
cjs.job_wrapper = job_wrapper
cjs.job_destination = job_wrapper.job_destination
- cjs.user_log = os.path.join( self.app.config.cluster_files_directory, '%s.condor.log' % galaxy_id_tag )
+ cjs.user_log = os.path.join( self.app.config.cluster_files_directory, 'galaxy_%s.condor.log' % galaxy_id_tag )
cjs.register_cleanup_file_attribute( 'user_log' )
self.__old_state_paths( cjs )
if job.state == model.Job.states.RUNNING:
@@ -268,9 +268,9 @@
files in the AsychronousJobState object
"""
if cjs.job_wrapper is not None:
- user_log = "%s/%s.condor.log" % (self.app.config.cluster_files_directory, job_wrapper.job_id)
- if not os.path.exists( cjs.job_file ) and os.path.exists( job_file ):
- cjs.output_file = "%s/%s.o" % (self.app.config.cluster_files_directory, job_wrapper.job_id)
- cjs.error_file = "%s/%s.e" % (self.app.config.cluster_files_directory, job_wrapper.job_id)
- cjs.job_file = "%s/galaxy_%s.sh" % (self.app.config.cluster_files_directory, job_wrapper.job_id)
+ user_log = "%s/%s.condor.log" % (self.app.config.cluster_files_directory, cjs.job_wrapper.job_id)
+ if not os.path.exists( cjs.user_log ) and os.path.exists( user_log ):
+ cjs.output_file = "%s/%s.o" % (self.app.config.cluster_files_directory, cjs.job_wrapper.job_id)
+ cjs.error_file = "%s/%s.e" % (self.app.config.cluster_files_directory, cjs.job_wrapper.job_id)
+ cjs.job_file = "%s/galaxy_%s.sh" % (self.app.config.cluster_files_directory, cjs.job_wrapper.job_id)
cjs.user_log = user_log
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/jobs/runners/pbs.py
--- a/lib/galaxy/jobs/runners/pbs.py
+++ b/lib/galaxy/jobs/runners/pbs.py
@@ -588,8 +588,8 @@
def stop_job( self, job ):
"""Attempts to delete a job from the PBS queue"""
- job_tag = ( "(%s/%s)"
- % ( job.get_id_tag(), job.get_job_runner_external_id() ) )
+ job_id = job.get_job_runner_external_id().encode('utf-8')
+ job_tag = "(%s/%s)" % ( job.get_id_tag(), job_id )
log.debug( "%s Stopping PBS job" % job_tag )
# Declare the connection handle c so that it can be cleaned up:
@@ -606,7 +606,7 @@
log.debug("(%s) Connection to PBS server for job delete failed"
% job_tag )
return
- pbs.pbs_deljob( c, job.get_job_runner_external_id(), '' )
+ pbs.pbs_deljob( c, job_id, '' )
log.debug( "%s Removed from PBS queue before job completion"
% job_tag )
except:
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -666,10 +666,11 @@
api_collection_visible_keys = ( 'id', 'name', 'published', 'deleted' )
api_element_visible_keys = ( 'id', 'name', 'published', 'deleted', 'genome_build', 'purged' )
+ default_name = 'Unnamed history'
def __init__( self, id=None, name=None, user=None ):
self.id = id
- self.name = name or "Unnamed history"
+ self.name = name or History.default_name
self.deleted = False
self.purged = False
self.importing = False
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/objectstore/__init__.py
--- a/lib/galaxy/objectstore/__init__.py
+++ b/lib/galaxy/objectstore/__init__.py
@@ -864,7 +864,7 @@
store selected randomly, but with weighting.
"""
- def __init__(self, config):
+ def __init__(self, config, fsmon=False):
super(DistributedObjectStore, self).__init__()
self.distributed_config = config.distributed_object_store_config_file
assert self.distributed_config is not None, "distributed object store ('object_store = distributed') " \
@@ -881,9 +881,10 @@
self.__parse_distributed_config(config)
self.sleeper = None
- if self.global_max_percent_full or filter(lambda x: x != 0.0, self.max_percent_full.values()):
+ if fsmon and ( self.global_max_percent_full or filter( lambda x: x != 0.0, self.max_percent_full.values() ) ):
self.sleeper = Sleeper()
self.filesystem_monitor_thread = threading.Thread(target=self.__filesystem_monitor)
+ self.filesystem_monitor_thread.setDaemon( True )
self.filesystem_monitor_thread.start()
log.info("Filesystem space monitor started")
@@ -1020,7 +1021,7 @@
def __init__(self, backends=[]):
super(HierarchicalObjectStore, self).__init__()
-def build_object_store_from_config(config):
+def build_object_store_from_config(config, fsmon=False):
""" Depending on the configuration setting, invoke the appropriate object store
"""
store = config.object_store
@@ -1029,7 +1030,7 @@
elif store == 's3' or store == 'swift':
return S3ObjectStore(config=config)
elif store == 'distributed':
- return DistributedObjectStore(config=config)
+ return DistributedObjectStore(config=config, fsmon=fsmon)
elif store == 'hierarchical':
return HierarchicalObjectStore()
else:
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/tools/filters/__init__.py
--- a/lib/galaxy/tools/filters/__init__.py
+++ b/lib/galaxy/tools/filters/__init__.py
@@ -12,7 +12,7 @@
# Prepopulate dict containing filters that are always checked,
# other filters that get checked depending on context (e.g. coming from
# trackster or no user found are added in build filters).
- self.default_filters = dict( tool=[ _not_hidden ], section=[], label=[] )
+ self.default_filters = dict( tool=[ _not_hidden, _handle_requires_login ], section=[], label=[] )
# Add dynamic filters to these default filters.
config = toolbox.app.config
self.__init_filters( "tool", config.tool_filters )
@@ -25,9 +25,6 @@
"""
filters = self.default_filters.copy()
- if not trans.user:
- filters[ "tool" ].append( _requires_login )
-
if kwds.get( "trackster", False ):
filters[ "tool" ].append( _has_trackster_conf )
@@ -59,8 +56,8 @@
return not tool.hidden
-def _requires_login( context, tool ):
- return not tool.require_login
+def _handle_requires_login( context, tool ):
+ return not tool.require_login or context.trans.user
def _has_trackster_conf( context, tool ):
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -38,6 +38,9 @@
from inflection import Inflector, English
inflector = Inflector(English)
+pkg_resources.require( "simplejson" )
+import simplejson
+
def is_multi_byte( chars ):
for char in chars:
try:
@@ -282,6 +285,11 @@
value = "%s%s%s" % ( value[:left_index], join_by, value[-right_index:] )
return value
+def pretty_print_json(json_data, is_json_string=False):
+ if is_json_string:
+ json_data = simplejson.loads(json_data)
+ return simplejson.dumps(json_data, sort_keys=True, indent=4 * ' ')
+
# characters that are valid
valid_chars = set(string.letters + string.digits + " -=_.()/+*^,:?!")
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/visualization/registry.py
--- a/lib/galaxy/visualization/registry.py
+++ b/lib/galaxy/visualization/registry.py
@@ -12,6 +12,8 @@
import galaxy.model
from galaxy.web import url_for
+from galaxy.web.base import pluginframework
+
import logging
log = logging.getLogger( __name__ )
@@ -28,17 +30,16 @@
tests:
anding, grouping, not
has_dataprovider
+ user is admin
data_sources:
lists of
add description element to visualization.
-TESTS to add:
- has dataprovider
- user is admin
+user_pref for ordering/ex/inclusion of particular visualizations
"""
# ------------------------------------------------------------------- the registry
-class VisualizationsRegistry( object ):
+class VisualizationsRegistry( pluginframework.PluginFramework ):
"""
Main responsibilities are:
- testing if an object has a visualization that can be applied to it
@@ -47,6 +48,8 @@
- validating and parsing params into resources (based on a context)
used in the visualization template
"""
+ #: name of this plugin
+ #: any built in visualizations that have their own render method in ctrls/visualization
# these should be handled somewhat differently - and be passed onto their resp. methods in ctrl.visualization
#TODO: change/remove if/when they can be updated to use this system
BUILT_IN_VISUALIZATIONS = [
@@ -55,57 +58,34 @@
'sweepster',
'phyloviz'
]
- # where to search for visualiztion templates (relative to templates/webapps/galaxy)
- # this can be overridden individually in the config entries
- TEMPLATE_ROOT = 'visualization'
+ #: directories under plugin_directory that aren't plugins
+ non_plugin_directories = []
def __str__( self ):
- listings_keys_str = ','.join( self.listings.keys() ) if self.listings else ''
- return 'VisualizationsRegistry(%s)' %( listings_keys_str )
+ return 'VisualizationsRegistry(%s)' %( self.plugin_directory )
- def __init__( self, galaxy_root, configuration_filepath ):
- # load the registry from the xml files located in configuration_filepath using the given parser
- configuration_filepath = os.path.join( galaxy_root, configuration_filepath )
- self.configuration_filepath = self.check_conf_filepath( configuration_filepath )
- self.move_sample_conf_files()
- self.load()
+ def __init__( self, registry_filepath, template_cache_dir ):
+ super( VisualizationsRegistry, self ).__init__( registry_filepath, 'visualizations', template_cache_dir )
# what to use to parse query strings into resources/vars for the template
self.resource_parser = ResourceParser()
+ log.debug( '%s loaded', str( self ) )
- def check_conf_filepath( self, configuration_filepath ):
+ def load_configuration( self ):
"""
- Checks for the existence of the given filepath.
- :param configurarion_filepath: full filepath to the visualization config directory
- :raises IOError: if the given directory doesn't exist
+ Builds the registry by parsing the `config/*.xml` files for every plugin
+ in ``get_plugin_directories`` and stores the results in ``self.listings``.
+
+ ..note::
+ This could be used to re-load a new configuration without restarting
+ the instance.
"""
- if not os.path.exists( configuration_filepath ):
- raise IOError( 'visualization configuration directory (%s) not found' %( configuration_filepath ) )
- return configuration_filepath
+ try:
+ self.listings = VisualizationsConfigParser.parse( self.get_plugin_directories() )
- def move_sample_conf_files( self ):
- """
- Copies any `*.xml.sample` files in `configuration_filepath` to
- `.xml` files of the same names if no file with that name already exists.
-
- :returns: a list of the files moved
- """
- files_moved = []
- for sample_file in glob.glob( os.path.join( self.configuration_filepath, '*.sample' ) ):
- new_name = os.path.splitext( sample_file )[0]
- if not os.path.exists( new_name ):
- shutil.copy2( sample_file, new_name )
- files_moved.append( new_name )
-
- def load( self ):
- """
- Builds the registry by parsing the xml in `self.configuration_filepath`
- and stores the results in `self.listings`.
-
- Provided as separate method from `__init__` in order to re-load a
- new configuration without restarting the instance.
- """
- self.listings = VisualizationsConfigParser.parse( self.configuration_filepath )
+ except Exception, exc:
+ log.exception( 'Error parsing visualizations plugins %s', self.plugin_directory )
+ raise
def get_visualization( self, trans, visualization_name, target_object ):
"""
@@ -283,11 +263,11 @@
VALID_RENDER_LOCATIONS = [ 'galaxy_main', '_top', '_blank' ]
@classmethod
- def parse( cls, config_dir, debug=True ):
+ def parse( cls, plugin_directories, debug=False ):
"""
Static class interface.
"""
- return cls( debug ).parse_files( config_dir )
+ return cls( debug ).parse_plugins( plugin_directories )
def __init__( self, debug=False ):
self.debug = debug
@@ -297,33 +277,45 @@
self.param_parser = ParamParser()
self.param_modifier_parser = ParamModifierParser()
- def parse_files( self, config_dir ):
+ def parse_plugins( self, plugin_directories ):
"""
- Parse each XML file in `config_dir` for visualizations config data.
+ Parses the config files for each plugin sub-dir in `base_path`.
+
+ :param plugin_directories: a list of paths to enabled plugins.
+
+ :returns: registry data in dictionary form
+ """
+ returned = {}
+ for plugin_path in plugin_directories:
+ returned.update( self.parse_plugin( plugin_path ) )
+ return returned
+
+ def parse_plugin( self, plugin_path ):
+ """
+ Parses any XML files in ``<plugin_path>/config``.
If an error occurs while parsing a visualizations entry, it is skipped.
:returns: registry data in dictionary form
+ ..note::
+ assumes config files are in a 'config' sub-dir of each plugin
"""
returned = {}
- try:
- for xml_filepath in glob.glob( os.path.join( config_dir, '*.xml' ) ):
- try:
- visualization_name, visualization = self.parse_file( xml_filepath )
- # skip vis' with parsing errors - don't shutdown the startup
- except ParsingException, parse_exc:
- log.error( 'Skipped visualization config "%s" due to parsing errors: %s',
- xml_filepath, str( parse_exc ), exc_info=self.debug )
- if visualization:
- returned[ visualization_name ] = visualization
- log.debug( 'Visualization config loaded for: %s', visualization_name )
+ plugin_config_path = os.path.join( plugin_path, 'config' )
+ if not os.path.isdir( plugin_config_path ):
+ return returned
- except Exception, exc:
- log.error( 'Error parsing visualizations configuration directory %s: %s',
- config_dir, str( exc ), exc_info=( not self.debug ) )
- #TODO: change when this framework is on by default
- if self.debug:
- raise
+ for xml_filepath in glob.glob( os.path.join( plugin_config_path, '*.xml' ) ):
+ try:
+ visualization_name, visualization = self.parse_file( xml_filepath )
+ # skip vis' with parsing errors - don't shutdown the startup
+ except ParsingException, parse_exc:
+ log.error( 'Skipped visualization config "%s" due to parsing errors: %s',
+ xml_filepath, str( parse_exc ), exc_info=self.debug )
+
+ if visualization:
+ returned[ visualization_name ] = visualization
+ log.debug( 'Visualization config loaded for: %s', visualization_name )
return returned
@@ -457,6 +449,7 @@
returned[ 'tests' ] = tests
# to_params (optional, 0 or more) - tells the registry to set certain params based on the model_clas, tests
+ returned[ 'to_params' ] = {}
to_params = self.parse_to_params( xml_tree.findall( 'to_param' ) )
if to_params:
returned[ 'to_params' ] = to_params
@@ -522,6 +515,7 @@
# test_attr can be a dot separated chain of object attributes (e.g. dataset.datatype) - convert to list
#TODO: too dangerous - constrain these to some allowed list
+ #TODO: does this err if no test_attr - it should...
test_attr = test_elem.get( 'test_attr' )
test_attr = test_attr.split( self.ATTRIBUTE_SPLIT_CHAR ) if isinstance( test_attr, str ) else []
# build a lambda function that gets the desired attribute to test
@@ -531,11 +525,17 @@
test_result_type = test_elem.get( 'result_type' ) or 'string'
# test functions should be sent an object to test, and the parsed result expected from the test
- #TODO: currently, isinstance and string equivalance are the only test types supported
- if test_type == 'isinstance':
+ # is test_attr attribute an instance of result
+ if test_type == 'isinstance':
#TODO: wish we could take this further but it would mean passing in the datatypes_registry
test_fn = lambda o, result: isinstance( getter( o ), result )
+ #TODO: needs cleanup - robustiosity-nessness
+ # does the object itself have a datatype attr and does that datatype have the given dataprovider
+ elif test_type == 'has_dataprovider':
+ test_fn = lambda o, result: ( hasattr( o, 'datatype' )
+ and o.datatype.has_dataprovider( result ) )
+
# default to simple (string) equilavance (coercing the test_attr to a string)
else:
test_fn = lambda o, result: str( getter( o ) ) == result
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -36,6 +36,7 @@
from galaxy.datatypes.interval import ChromatinInteractions
from galaxy.datatypes.data import Text
+from galaxy.model import ExtendedMetadata, ExtendedMetadataIndex, LibraryDatasetDatasetAssociation
from galaxy.visualization.genome.visual_analytics import get_tool_def
from galaxy.datatypes.display_applications import util as da_util
@@ -536,7 +537,9 @@
hda = None
try:
hda = self.get_dataset( trans, id,
- check_ownership=check_ownership, check_accesible=check_accesible, check_state=check_state )
+ check_ownership=check_ownership,
+ check_accessible=check_accessible,
+ check_state=check_state )
except Exception, exception:
pass
hdas.append( hda )
@@ -2281,6 +2284,80 @@
log.debug( "In get_item_tag_assoc with tagged_item %s" % tagged_item )
return self.get_tag_handler( trans )._get_item_tag_assoc( user, tagged_item, tag_name )
+
+
+class UsesExtendedMetadataMixin( SharableItemSecurityMixin ):
+ """ Mixin for getting and setting item extended metadata. """
+
+ def get_item_extended_metadata_obj( self, trans, item ):
+ """
+ Given an item object (such as a LibraryDatasetDatasetAssociation), find the object
+ of the associated extended metadata
+ """
+ if item.extended_metadata:
+ return item.extended_metadata
+ return None
+
+ def set_item_extended_metadata_obj( self, trans, item, extmeta_obj, check_writable=False):
+ print "setting", extmeta_obj.data
+ if item.__class__ == LibraryDatasetDatasetAssociation:
+ if not check_writable or trans.app.security_agent.can_modify_library_item( trans.get_current_user_roles(), item, trans.user ):
+ item.extended_metadata = extmeta_obj
+ trans.sa_session.flush()
+
+ def unset_item_extended_metadata_obj( self, trans, item, check_writable=False):
+ if item.__class__ == LibraryDatasetDatasetAssociation:
+ if not check_writable or trans.app.security_agent.can_modify_library_item( trans.get_current_user_roles(), item, trans.user ):
+ item.extended_metadata = None
+ trans.sa_session.flush()
+
+ def create_extended_metadata(self, trans, extmeta):
+ """
+ Create/index an extended metadata object. The returned object is
+ not associated with any items
+ """
+ ex_meta = ExtendedMetadata(extmeta)
+ trans.sa_session.add( ex_meta )
+ trans.sa_session.flush()
+ for path, value in self._scan_json_block(extmeta):
+ meta_i = ExtendedMetadataIndex(ex_meta, path, value)
+ trans.sa_session.add(meta_i)
+ trans.sa_session.flush()
+ return ex_meta
+
+ def delete_extended_metadata( self, trans, item):
+ if item.__class__ == ExtendedMetadata:
+ trans.sa_session.delete( item )
+ trans.sa_session.flush()
+
+ def _scan_json_block(self, meta, prefix=""):
+ """
+ Scan a json style data structure, and emit all fields and their values.
+ Example paths
+
+ Data
+ { "data" : [ 1, 2, 3 ] }
+
+ Path:
+ /data == [1,2,3]
+
+ /data/[0] == 1
+
+ """
+ if isinstance(meta, dict):
+ for a in meta:
+ for path, value in self._scan_json_block(meta[a], prefix + "/" + a):
+ yield path, value
+ elif isinstance(meta, list):
+ for i, a in enumerate(meta):
+ for path, value in self._scan_json_block(a, prefix + "[%d]" % (i)):
+ yield path, value
+ else:
+ #BUG: Everything is cast to string, which can lead to false positives
+ #for cross type comparisions, ie "True" == True
+ yield prefix, ("%s" % (meta)).encode("utf8", errors='replace')
+
+
"""
Deprecated: `BaseController` used to be available under the name `Root`
"""
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/web/base/pluginframework.py
--- /dev/null
+++ b/lib/galaxy/web/base/pluginframework.py
@@ -0,0 +1,233 @@
+"""
+Base class for plugins - frameworks or systems that may:
+ * serve static content
+ * serve templated html
+ * have some configuration at startup
+"""
+
+import os.path
+import glob
+import sys
+
+import pkg_resources
+pkg_resources.require( 'MarkupSafe' )
+pkg_resources.require( 'Mako' )
+import mako
+
+import logging
+log = logging.getLogger( __name__ )
+
+# ============================================================================= exceptions
+class PluginFrameworkException( Exception ):
+ """Base exception for plugin frameworks.
+ """
+ pass
+class PluginFrameworkConfigException( PluginFrameworkException ):
+ """Exception for plugin framework configuration errors.
+ """
+ pass
+class PluginFrameworkStaticException( PluginFrameworkException ):
+ """Exception for plugin framework static directory set up errors.
+ """
+ pass
+class PluginFrameworkTemplateException( PluginFrameworkException ):
+ """Exception for plugin framework template directory
+ and template rendering errors.
+ """
+ pass
+
+
+# ============================================================================= base
+class PluginFramework( object ):
+ """
+ Plugins are files/directories living outside the Galaxy ``lib`` directory
+ that serve static files (css, js, images, etc.), use and serve mako templates,
+ and have some configuration to control the rendering.
+
+ A plugin framework sets up all the above components.
+ """
+ #: does the class need a config file(s) to be parsed?
+ has_config = True
+ #: does the class need static files served?
+ serves_static = True
+ #: does the class need template files served?
+ serves_templates = True
+ #TODO: allow plugin mako inheritance from existing ``/templates`` files
+ #uses_galaxy_templates = True
+ #TODO: possibly better as instance var (or a combo)
+ #: the directories in ``plugin_directory`` with basenames listed here will
+ #: be ignored for config, static, and templates
+ non_plugin_directories = []
+
+ # ------------------------------------------------------------------------- setup
+ @classmethod
+ def from_config( cls, config_plugin_directory, config ):
+ """
+ Set up the framework based on data from some config object by:
+ * constructing it's absolute plugin_directory filepath
+ * getting a template_cache
+ * and appending itself to the config object's ``plugin_frameworks`` list
+
+ .. note::
+ precondition: config obj should have attributes:
+ root, template_cache, and (list) plugin_frameworks
+ """
+ # currently called from (base) app.py - defined here to allow override if needed
+ if not config_plugin_directory:
+ return None
+ try:
+ # create the plugin path and if plugin dir begins with '/' assume absolute path
+ full_plugin_filepath = os.path.join( config.root, config_plugin_directory )
+ if config_plugin_directory.startswith( os.path.sep ):
+ full_plugin_filepath = config_plugin_directory
+ if not os.path.exists( full_plugin_filepath ):
+ raise PluginFrameworkException( 'Plugin path not found: %s' %( full_plugin_filepath ) )
+
+ template_cache = config.template_cache if cls.serves_static else None
+ plugin = cls( full_plugin_filepath, template_cache )
+
+ config.plugin_frameworks.append( plugin )
+ return plugin
+
+ except PluginFrameworkException, plugin_exc:
+ log.exception( "Error loading framework %s. Skipping...", cls.__class__.__name__ )
+ return None
+
+ def __str__( self ):
+ return '%s(%s)' %( self.__class__.__name__, self.plugin_directory )
+
+ def __init__( self, plugin_directory, name=None, template_cache_dir=None, debug=False ):
+ """
+ :type plugin_directory: string
+ :param plugin_directory: the base directory where plugin code is kept
+ :type name: (optional) string (default: None)
+ :param name: the name of this plugin
+ (that will appear in url pathing, etc.)
+ :type template_cache_dir: (optional) string (default: None)
+ :param template_cache_dir: the cache directory to store compiled mako
+ """
+ if not os.path.isdir( plugin_directory ):
+ raise PluginFrameworkException( 'Framework plugin directory not found: %s, %s'
+ %( self.__class__.__name__, plugin_directory ) )
+ self.plugin_directory = plugin_directory
+ #TODO: or pass in from config
+ self.name = name or os.path.basename( self.plugin_directory )
+
+ if self.has_config:
+ self.load_configuration()
+ # set_up_static_urls will be called during the static middleware creation (if serves_static)
+ if self.serves_templates:
+ self.set_up_templates( template_cache_dir )
+
+ def get_plugin_directories( self ):
+ """
+ Return the plugin directory paths for this plugin.
+
+ Gets any directories within ``plugin_directory`` that are directories
+ themselves and whose ``basename`` is not in ``plugin_directory``.
+ """
+ # could instead explicitly list on/off in master config file
+ for plugin_path in glob.glob( os.path.join( self.plugin_directory, '*' ) ):
+ if not os.path.isdir( plugin_path ):
+ continue
+
+ if os.path.basename( plugin_path ) in self.non_plugin_directories:
+ continue
+
+ yield plugin_path
+
+ # ------------------------------------------------------------------------- config
+ def load_configuration( self ):
+ """
+ Override to load some framework/plugin specifc configuration.
+ """
+ # Abstract method
+ return True
+
+ # ------------------------------------------------------------------------- serving static files
+ def get_static_urls_and_paths( self ):
+ """
+ For each plugin, return a 2-tuple where the first element is a url path
+ to the plugin's static files and the second is a filesystem path to those
+ same files.
+
+ Meant to be passed to a Static url map.
+ """
+ url_and_paths = []
+ # called during the static middleware creation (buildapp.py, wrap_in_static)
+
+ # NOTE: this only searches for static dirs two levels deep (i.e. <plugin_directory>/<plugin-name>/static)
+ for plugin_path in self.get_plugin_directories():
+ # that path is a plugin, search for subdirs named static in THAT dir
+ plugin_static_path = os.path.join( plugin_path, 'static' )
+ if not os.path.isdir( plugin_static_path ):
+ continue
+
+ # build a url for that static subdir and create a Static urlmap entry for it
+ plugin_name = os.path.splitext( os.path.basename( plugin_path ) )[0]
+ plugin_url = self.name + '/' + plugin_name + '/static'
+ url_and_paths.append( ( plugin_url, plugin_static_path ) )
+
+ return url_and_paths
+
+ # ------------------------------------------------------------------------- templates
+ def set_up_templates( self, template_cache_dir ):
+ """
+ Add a ``template_lookup`` attribute to the framework that can be passed
+ to the mako renderer to find templates.
+ """
+ if not template_cache_dir:
+ raise PluginFrameworkTemplateException( 'Plugins that serve templates require a template_cache_dir' )
+ self.template_lookup = self._create_mako_template_lookup( template_cache_dir, self._get_template_paths() )
+ return self.template_lookup
+
+ def _get_template_paths( self ):
+ """
+ Get the paths that will be searched for templates.
+ """
+ return [ self.plugin_directory ]
+
+ def _create_mako_template_lookup( self, cache_dir, paths, collection_size=500, output_encoding='utf-8' ):
+ """
+ Create a ``TemplateLookup`` with defaults.
+ """
+ return mako.lookup.TemplateLookup(
+ directories = paths,
+ module_directory = cache_dir,
+ collection_size = collection_size,
+ output_encoding = output_encoding )
+
+ #TODO: do we want to remove trans and app from the plugin template context?
+ def fill_template( self, trans, template_filename, **kwargs ):
+ """
+ Pass control over to trans and render the ``template_filename``.
+ """
+ # defined here to be overridden
+ return trans.fill_template( template_filename, template_lookup=self.template_lookup, **kwargs )
+
+ def fill_template_with_plugin_imports( self, trans, template_filename, **kwargs ):
+ """
+ Returns a rendered plugin template but allows importing modules from inside
+ the plugin directory within the template.
+
+ ..example:: I.e. given this layout for a plugin:
+ bler/
+ template/
+ bler.mako
+ static/
+ conifg/
+ my_script.py
+ this version of `fill_template` allows `bler.mako` to call `import my_script`.
+ """
+ try:
+ plugin_base_path = os.path.split( os.path.dirname( template_filename ) )[0]
+ plugin_path = os.path.join( self.plugin_directory, plugin_base_path )
+ sys.path.append( plugin_path )
+ filled_template = self.fill_template( trans, template_filename, **kwargs )
+
+ finally:
+ sys.path.remove( plugin_path )
+
+ return filled_template
+
+ #TODO: could add plugin template helpers here
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -832,6 +832,39 @@
history = property( get_history, set_history )
+ def get_or_create_default_history( self ):
+ """
+ Gets or creates a default history and associates it with the current
+ session.
+ """
+
+ # There must be a user to fetch a default history.
+ if not self.galaxy_session.user:
+ return self.new_history()
+
+ # Look for default history that (a) has default name + is not deleted and
+ # (b) has no datasets. If suitable history found, use it; otherwise, create
+ # new history.
+ unnamed_histories = self.sa_session.query( self.app.model.History ).filter_by(
+ user=self.galaxy_session.user,
+ name=self.app.model.History.default_name,
+ deleted=False )
+ default_history = None
+ for history in unnamed_histories:
+ if len( history.datasets ) == 0:
+ # Found suitable default history.
+ default_history = history
+ break
+
+ # Set or create hsitory.
+ if default_history:
+ history = default_history
+ self.set_history( history )
+ else:
+ history = self.new_history()
+
+ return history
+
def new_history( self, name=None ):
"""
Create a new history and associate it with the current session and
@@ -960,10 +993,13 @@
searchList=[kwargs, self.template_context, dict(caller=self, t=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app)] )
return str( template )
- def fill_template_mako( self, filename, **kwargs ):
- template = self.webapp.mako_template_lookup.get_template( filename )
+ def fill_template_mako( self, filename, template_lookup=None, **kwargs ):
+ template_lookup = template_lookup or self.webapp.mako_template_lookup
+ template = template_lookup.get_template( filename )
template.output_encoding = 'utf-8'
- data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app )
+
+ data = dict( caller=self, t=self, trans=self, h=helpers, util=util,
+ request=self.request, response=self.response, app=self.app )
data.update( self.template_context )
data.update( kwargs )
return template.render( **data )
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/web/framework/middleware/static.py
--- a/lib/galaxy/web/framework/middleware/static.py
+++ b/lib/galaxy/web/framework/middleware/static.py
@@ -12,9 +12,11 @@
from paste.urlparser import StaticURLParser
class CacheableStaticURLParser( StaticURLParser ):
+
def __init__( self, directory, cache_seconds=None ):
StaticURLParser.__init__( self, directory )
self.cache_seconds = cache_seconds
+
def __call__( self, environ, start_response ):
path_info = environ.get('PATH_INFO', '')
if not path_info:
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/webapps/galaxy/api/extended_metadata.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/extended_metadata.py
@@ -0,0 +1,59 @@
+"""
+API operations on annotations.
+"""
+import logging
+from galaxy import web
+from galaxy.web.base.controller import BaseAPIController, UsesHistoryMixin, UsesLibraryMixinItems, UsesHistoryDatasetAssociationMixin, UsesStoredWorkflowMixin, UsesExtendedMetadataMixin, HTTPNotImplemented
+
+log = logging.getLogger( __name__ )
+
+class BaseExtendedMetadataController( BaseAPIController, UsesExtendedMetadataMixin, UsesHistoryMixin, UsesLibraryMixinItems, UsesHistoryDatasetAssociationMixin, UsesStoredWorkflowMixin ):
+
+ @web.expose_api
+ def index( self, trans, **kwd ):
+ idnum = kwd[self.exmeta_item_id]
+ item = self._get_item_from_id(trans, idnum, check_writable=False)
+ if item is not None:
+ ex_meta = self.get_item_extended_metadata_obj( trans, item )
+ if ex_meta is not None:
+ return ex_meta.data
+
+ @web.expose_api
+ def create( self, trans, payload, **kwd ):
+ idnum = kwd[self.exmeta_item_id]
+ item = self._get_item_from_id(trans, idnum, check_writable=True)
+ if item is not None:
+ ex_obj = self.get_item_extended_metadata_obj(trans, item)
+ if ex_obj is not None:
+ self.unset_item_extended_metadata_obj(trans, item)
+ self.delete_extended_metadata(trans, ex_obj)
+ ex_obj = self.create_extended_metadata(trans, payload)
+ self.set_item_extended_metadata_obj(trans, item, ex_obj)
+
+ @web.expose_api
+ def delete( self, trans, **kwd ):
+ idnum = kwd[self.tagged_item_id]
+ item = self._get_item_from_id(trans, idnum, check_writable=True)
+ if item is not None:
+ ex_obj = self.get_item_extended_metadata_obj(trans, item)
+ if ex_obj is not None:
+ self.unset_item_extended_metadata_obj(trans, item)
+ self.delete_extended_metadata(trans, ex_obj)
+
+ @web.expose_api
+ def undelete( self, trans, **kwd ):
+ raise HTTPNotImplemented()
+
+class LibraryDatasetExtendMetadataController(BaseExtendedMetadataController):
+ controller_name = "library_dataset_extended_metadata"
+ exmeta_item_id = "library_content_id"
+ def _get_item_from_id(self, trans, idstr, check_writable=True):
+ if check_writable:
+ item = self.get_library_dataset_dataset_association( trans, idstr)
+ if trans.app.security_agent.can_modify_library_item( trans.get_current_user_roles(), item ):
+ return item
+ else:
+ item = self.get_library_dataset_dataset_association( trans, idstr)
+ if trans.app.security_agent.can_access_library_item( trans.get_current_user_roles(), item, trans.user ):
+ return item
+ return None
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/webapps/galaxy/api/permissions.py
--- a/lib/galaxy/webapps/galaxy/api/permissions.py
+++ b/lib/galaxy/webapps/galaxy/api/permissions.py
@@ -3,7 +3,8 @@
"""
import logging, os, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
-from galaxy import util, datatypes, jobs, web, util
+import galaxy.util
+from galaxy import util, datatypes, jobs, web
from galaxy.web.base.controller import *
from galaxy.util.sanitize_html import sanitize_html
from galaxy.model.orm import *
@@ -23,7 +24,7 @@
trans.response.status = 403
return "You are not authorized to update library permissions."
- params = util.Params( payload )
+ params = galaxy.util.Params( payload )
try:
decoded_library_id = trans.security.decode_id( library_id )
except TypeError:
@@ -38,7 +39,7 @@
permissions = {}
for k, v in trans.app.model.Library.permitted_actions.items():
role_params = params.get( k + '_in', [] )
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( trans.security.decode_id( x ) ) for x in util.listify( role_params ) ]
+ in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( trans.security.decode_id( x ) ) for x in galaxy.util.listify( role_params ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.set_all_library_permissions( trans, library, permissions )
trans.sa_session.refresh( library )
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -2,22 +2,26 @@
Provides factory methods to assemble the Galaxy web application
"""
-import logging, atexit
-import os, os.path
-import sys, warnings
-
-from galaxy.util import asbool
+import sys
+import os
+import os.path
+import atexit
+import warnings
+import glob
from paste import httpexceptions
import pkg_resources
-log = logging.getLogger( __name__ )
-
-from galaxy import util
import galaxy.model
import galaxy.model.mapping
import galaxy.datatypes.registry
import galaxy.web.framework
+from galaxy import util
+from galaxy.util import asbool
+
+import logging
+log = logging.getLogger( __name__ )
+
class GalaxyWebApplication( galaxy.web.framework.WebApplication ):
pass
@@ -118,6 +122,10 @@
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
+
_add_item_annotation_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -181,7 +189,7 @@
if kwargs.get( 'middleware', True ):
webapp = wrap_in_middleware( webapp, global_conf, **kwargs )
if asbool( kwargs.get( 'static_enabled', True ) ):
- webapp = wrap_in_static( webapp, global_conf, **kwargs )
+ webapp = wrap_in_static( webapp, global_conf, plugin_frameworks=app.config.plugin_frameworks, **kwargs )
if asbool(kwargs.get('pack_scripts', False)):
pack_scripts()
# Close any pooled database connections before forking
@@ -233,6 +241,10 @@
conditions=dict(method=["GET"]))
+def _add_item_extended_metadata_controller( webapp, name_prefix, path_prefix, **kwd ):
+ controller = "%sextended_metadata" % name_prefix
+ name = "%sextended_metadata" % name_prefix
+ webapp.mapper.resource(name, "extended_metadata", path_prefix=path_prefix, controller=controller)
def _add_item_annotation_controller( webapp, name_prefix, path_prefix, **kwd ):
controller = "%sannotations" % name_prefix
@@ -323,7 +335,7 @@
log.debug( "Enabling 'Request ID' middleware" )
return app
-def wrap_in_static( app, global_conf, **local_conf ):
+def wrap_in_static( app, global_conf, plugin_frameworks=None, **local_conf ):
from paste.urlmap import URLMap
from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static
urlmap = URLMap()
@@ -343,6 +355,16 @@
urlmap["/static/style"] = Static( conf.get( "static_style_dir" ), cache_time )
urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir" ), cache_time )
urlmap["/robots.txt"] = Static( conf.get( "static_robots_txt", 'static/robots.txt'), cache_time )
+
+ # wrap any static dirs for plugins
+ plugin_frameworks = plugin_frameworks or []
+ for static_serving_framework in ( framework for framework in plugin_frameworks if framework.serves_static ):
+ # invert control to each plugin for finding their own static dirs
+ for plugin_url, plugin_static_path in static_serving_framework.get_static_urls_and_paths():
+ plugin_url = '/plugins/' + plugin_url
+ urlmap[( plugin_url )] = Static( plugin_static_path, cache_time )
+ log.debug( 'added url, path to static middleware: %s, %s', plugin_url, plugin_static_path )
+
# URL mapper becomes the root webapp
return urlmap
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -1054,18 +1054,19 @@
no_changes_checked=no_changes_checked,
tool_panel_section=tool_panel_section,
new_tool_panel_section=new_tool_panel_section )
- # The repository's status must be updated from 'Uninstall' to 'New' when initiating reinstall so the repository_installation_updater will function.
- tool_shed_repository = suc.create_or_update_tool_shed_repository( trans.app,
- tool_shed_repository.name,
- tool_shed_repository.description,
- tool_shed_repository.installed_changeset_revision,
- tool_shed_repository.ctx_rev,
- repository_clone_url,
- metadata,
- trans.model.ToolShedRepository.installation_status.NEW,
- tool_shed_repository.installed_changeset_revision,
- tool_shed_repository.owner,
- tool_shed_repository.dist_to_shed )
+ if tool_shed_repository.status == trans.model.ToolShedRepository.installation_status.UNINSTALLED:
+ # The repository's status must be updated from 'Uninstalled' to 'New' when initiating reinstall so the repository_installation_updater will function.
+ tool_shed_repository = suc.create_or_update_tool_shed_repository( trans.app,
+ tool_shed_repository.name,
+ tool_shed_repository.description,
+ tool_shed_repository.installed_changeset_revision,
+ tool_shed_repository.ctx_rev,
+ repository_clone_url,
+ metadata,
+ trans.model.ToolShedRepository.installation_status.NEW,
+ tool_shed_repository.installed_changeset_revision,
+ tool_shed_repository.owner,
+ tool_shed_repository.dist_to_shed )
ctx_rev = suc.get_ctx_rev( trans.app,
tool_shed_url,
tool_shed_repository.name,
@@ -1096,7 +1097,8 @@
repository_metadata=None,
tool_dependencies=tool_dependencies,
repository_dependencies=repository_dependencies )
- repo_info_dicts.append( repo_info_dict )
+ if repo_info_dict not in repo_info_dicts:
+ repo_info_dicts.append( repo_info_dict )
# Make sure all tool_shed_repository records exist.
created_or_updated_tool_shed_repositories, tool_panel_section_keys, repo_info_dicts, filtered_repo_info_dicts = \
repository_dependency_util.create_repository_dependency_objects( trans=trans,
@@ -1107,7 +1109,7 @@
install_repository_dependencies=install_repository_dependencies,
no_changes_checked=no_changes_checked,
tool_panel_section=tool_panel_section,
- new_tool_panel_section=new_tool_panel_section )
+ new_tool_panel_section=new_tool_panel_section )
# Default the selected tool panel location for loading tools included in each newly installed required tool shed repository to the location
# selected for the repository selected for reinstallation.
for index, tps_key in enumerate( tool_panel_section_keys ):
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -309,7 +309,7 @@
# If deleting the current history, make a new current.
if history == trans.get_history():
deleted_current = True
- trans.new_history()
+ trans.get_or_create_default_history()
trans.log_event( "History (%s) marked as deleted" % history.name )
n_deleted += 1
if purge and trans.app.config.allow_user_dataset_purge:
@@ -571,8 +571,8 @@
# No need to check other outputs since the job's parent history is this history
job.mark_deleted( trans.app.config.track_jobs_in_database )
trans.app.job_manager.job_stop_queue.put( job.id )
- # Regardless of whether it was previously deleted, we make a new history active
- trans.new_history()
+ # Regardless of whether it was previously deleted, get or create default history.
+ trans.get_or_create_default_history()
return trans.show_ok_message( "History deleted, a new history is active", refresh_frames=['history'] )
@web.expose
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/webapps/galaxy/controllers/library_common.py
--- a/lib/galaxy/webapps/galaxy/controllers/library_common.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library_common.py
@@ -16,9 +16,9 @@
from galaxy.security import Action
from galaxy.tools.actions import upload_common
from galaxy.util import inflector
-from galaxy.util.json import to_json_string
+from galaxy.util.json import to_json_string, from_json_string
from galaxy.util.streamball import StreamBall
-from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin
+from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin, UsesExtendedMetadataMixin
from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, build_select_field
from galaxy.model.orm import and_, eagerload_all
@@ -73,7 +73,7 @@
pass
os.rmdir( tmpd )
-class LibraryCommon( BaseUIController, UsesFormDefinitionsMixin ):
+class LibraryCommon( BaseUIController, UsesFormDefinitionsMixin, UsesExtendedMetadataMixin ):
@web.json
def library_item_updates( self, trans, ids=None, states=None ):
# Avoid caching
@@ -536,6 +536,39 @@
trans.sa_session.flush()
message = "Information updated for library dataset '%s'." % ldda.name
status = 'done'
+ elif params.get( 'change_extended_metadata', False):
+ em_string = util.restore_text( params.get("extended_metadata", "") )
+ if len(em_string):
+ payload = None
+ try:
+ payload = from_json_string(em_string)
+ except Exception, e:
+ message = 'Invalid JSON input'
+ status = 'error'
+ if payload is not None:
+ if ldda is not None:
+ ex_obj = self.get_item_extended_metadata_obj(trans, ldda)
+ if ex_obj is not None:
+ self.unset_item_extended_metadata_obj(trans, ldda)
+ self.delete_extended_metadata(trans, ex_obj)
+ ex_obj = self.create_extended_metadata(trans, payload)
+ self.set_item_extended_metadata_obj(trans, ldda, ex_obj)
+ message = "Updated Extended metadata '%s'." % ldda.name
+ status = 'done'
+ else:
+ message = "LDDA not found"
+ status = 'error'
+ else:
+ if ldda is not None:
+ ex_obj = self.get_item_extended_metadata_obj(trans, ldda)
+ if ex_obj is not None:
+ self.unset_item_extended_metadata_obj(trans, ldda)
+ self.delete_extended_metadata(trans, ex_obj)
+ message = "Deleted Extended metadata '%s'." % ldda.name
+ status = 'done'
+
+
+
if "dbkey" in ldda.datatype.metadata_spec and not ldda.metadata.dbkey:
# Copy dbkey into metadata, for backwards compatability
# This looks like it does nothing, but getting the dbkey
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/webapps/galaxy/controllers/visualization.py
--- a/lib/galaxy/webapps/galaxy/controllers/visualization.py
+++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py
@@ -708,7 +708,7 @@
# validate name vs. registry
registry = trans.app.visualizations_registry
if not registry:
- raise HTTPNotFound( 'No visualization registry (possibly disabled in universe_wsgi.ini)')
+ raise HTTPNotFound( 'No visualization registry (possibly disabled in universe_wsgi.ini)' )
if visualization_name not in registry.listings:
raise HTTPNotFound( 'Unknown or invalid visualization: ' + visualization_name )
# or redirect to list?
@@ -722,16 +722,15 @@
resources = registry.query_dict_to_resources( trans, self, visualization_name, kwargs )
# look up template and render
- template_root = registry_listing.get( 'template_root', registry.TEMPLATE_ROOT )
- template = registry_listing[ 'template' ]
- template_path = os.path.join( template_root, template )
+ template_path = registry_listing[ 'template' ]
+ returned = registry.fill_template( trans, template_path,
+ visualization_name=visualization_name, query_args=kwargs,
+ embedded=embedded, shared_vars={}, **resources )
#NOTE: passing *unparsed* kwargs as query_args
#NOTE: shared_vars is a dictionary for shared data in the template
# this feels hacky to me but it's what mako recommends:
# http://docs.makotemplates.org/en/latest/runtime.html
#TODO: embedded
- returned = trans.fill_template( template_path, visualization_name=visualization_name,
- embedded=embedded, query_args=kwargs, shared_vars={}, **resources )
except Exception, exception:
log.exception( 'error rendering visualization (%s): %s', visualization_name, str( exception ) )
@@ -745,47 +744,58 @@
@web.expose
@web.require_login()
- def trackster(self, trans, id=None, **kwargs):
+ def trackster(self, trans, **kwargs):
"""
Display browser for the visualization denoted by id and add the datasets listed in `dataset_ids`.
"""
- # Get dataset to add.
+ # define app configuration
+ app = { 'jscript' : "viz/trackster" }
+
+ # get dataset to add
+ id = kwargs.get( "id", None )
+
+ # get dataset to add
new_dataset_id = kwargs.get( "dataset_id", None )
- # Check for gene region
- gene_region = GenomeRegion.from_str(kwargs.get("gene_region", ""))
-
- # Set up new browser if no id provided.
+ # set up new browser if no id provided
if not id:
- # Use dbkey from dataset to be added or from incoming parameter.
+ # use dbkey from dataset to be added or from incoming parameter
dbkey = None
if new_dataset_id:
dbkey = self.get_dataset( trans, new_dataset_id ).dbkey
if dbkey == '?':
dbkey = kwargs.get( "dbkey", None )
-
- # fill template
- return trans.fill_template( "tracks/browser.mako", viewport_config=gene_region.__dict__, add_dataset=new_dataset_id, default_dbkey=dbkey )
+
+ # save database key
+ app['default_dbkey'] = dbkey
+
+ # add url
+ app['new_browser'] = web.url_for( controller='visualization', action='new_browser', default_dbkey=dbkey )
+ else:
+ # load saved visualization
+ vis = self.get_visualization( trans, id, check_ownership=False, check_accessible=True )
+ app['viz_config'] = self.get_visualization_config( trans, vis )
+
+ # backup id
+ app['id'] = id;
- # Display saved visualization.
- vis = self.get_visualization( trans, id, check_ownership=False, check_accessible=True )
- viz_config = self.get_visualization_config( trans, vis )
-
- # Update gene region of saved visualization if user parses a new gene region in the url
+ # add dataset id
+ app['add_dataset'] = new_dataset_id
+
+ # check for gene region
+ gene_region = GenomeRegion.from_str(kwargs.get("gene_region", ""))
+
+ # update gene region of saved visualization if user parses a new gene region in the url
if gene_region.chrom is not None:
- viz_config['viewport']['chrom'] = gene_region.chrom
- viz_config['viewport']['start'] = gene_region.start
- viz_config['viewport']['end'] = gene_region.end
-
- '''
- FIXME:
- if new_dataset is not None:
- if trans.security.decode_id(new_dataset) in [ d["dataset_id"] for d in viz_config.get("tracks") ]:
- new_dataset = None # Already in browser, so don't add
- '''
+ app['gene_region'] = {
+ 'chrom' : gene_region.chrom,
+ 'start' : gene_region.start,
+ 'end' : gene_region.end
+ }
+
# fill template
- return trans.fill_template( 'tracks/browser.mako', config=viz_config, add_dataset=new_dataset_id )
+ return trans.fill_template('galaxy.panels.mako', config = {'right_panel' : True, 'app' : app})
@web.expose
def circster( self, trans, id=None, hda_ldda=None, dataset_id=None, dbkey=None ):
@@ -839,7 +849,15 @@
if not isinstance( genome_data, str ):
track[ 'preloaded_data' ] = genome_data
- return trans.fill_template( 'visualization/circster.mako', viz_config=viz_config, genome=genome )
+ # define app configuration for generic mako template
+ app = {
+ 'jscript' : "viz/circster",
+ 'viz_config' : viz_config,
+ 'genome' : genome
+ }
+
+ # fill template
+ return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
@web.expose
def sweepster( self, trans, id=None, hda_ldda=None, dataset_id=None, regions=None ):
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/webapps/tool_shed/api/repository_revisions.py
--- a/lib/galaxy/webapps/tool_shed/api/repository_revisions.py
+++ b/lib/galaxy/webapps/tool_shed/api/repository_revisions.py
@@ -54,7 +54,6 @@
try:
# We'll currently support only gzip-compressed tar archives.
file_type = 'gz'
- file_type_str = suc.get_file_type_str( changeset_revision, file_type )
export_repository_dependencies = util.string_as_bool( export_repository_dependencies )
# Get the repository information.
repository = suc.get_repository_by_name_and_owner( trans.app, name, owner )
diff -r fcfd3af762255910c5959c8c8147af90ab4038bc -r da94b25396024b6af382242f446d2daf117b514b lib/galaxy/webapps/tool_shed/util/container_util.py
--- a/lib/galaxy/webapps/tool_shed/util/container_util.py
+++ b/lib/galaxy/webapps/tool_shed/util/container_util.py
@@ -257,13 +257,12 @@
class ToolDependency( object ):
"""Tool dependency object"""
- def __init__( self, id=None, name=None, version=None, type=None, install_dir=None, readme=None, installation_status=None, repository_id=None,
+ def __init__( self, id=None, name=None, version=None, type=None, readme=None, installation_status=None, repository_id=None,
tool_dependency_id=None, is_orphan=None ):
self.id = id
self.name = name
self.version = version
self.type = type
- self.install_dir = install_dir
self.readme = readme
self.installation_status = installation_status
self.repository_id = repository_id
@@ -948,12 +947,10 @@
# Insert a header row.
tool_dependency_id += 1
if trans.webapp.name == 'galaxy':
- # Include the installation directory.
tool_dependency = ToolDependency( id=tool_dependency_id,
name='Name',
version='Version',
type='Type',
- install_dir='Install directory',
readme=None,
installation_status='Installation status',
repository_id=None,
@@ -964,7 +961,6 @@
name='Name',
version='Version',
type='Type',
- install_dir=None,
readme=None,
installation_status=None,
repository_id=None,
@@ -995,7 +991,6 @@
name=name,
version=None,
type=type,
- install_dir=None,
readme=None,
installation_status=installation_status,
repository_id=repository_id,
@@ -1013,7 +1008,6 @@
name = requirements_dict[ 'name' ]
version = requirements_dict[ 'version' ]
type = requirements_dict[ 'type' ]
- install_dir = requirements_dict.get( 'install_dir', None )
repository_id = requirements_dict.get( 'repository_id', None )
td_id = requirements_dict.get( 'tool_dependency_id', None )
if trans.webapp.name == 'galaxy':
@@ -1024,7 +1018,6 @@
name=name,
version=version,
type=type,
- install_dir=install_dir,
readme=None,
installation_status=installation_status,
repository_id=repository_id,
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/b076688903e1/
Changeset: b076688903e1
User: ross...(a)gmail.com
Date: 2013-08-08 07:21:58
Summary: adding rgenetics tools missing from baker
Affected #: 2 files
diff -r da94b25396024b6af382242f446d2daf117b514b -r b076688903e1f96fb0453c61d0ea38b5d92cd89c tools/rgenetics/htseqsams2mx.py
--- a/tools/rgenetics/htseqsams2mx.py
+++ b/tools/rgenetics/htseqsams2mx.py
@@ -60,7 +60,7 @@
self.msg = msg
-def htseqMX(gff_filename,sam_filenames,colnames,sam_exts,opts):
+def htseqMX(gff_filename,sam_filenames,colnames,sam_exts,sam_bais,opts):
"""
Code taken from count.py in Simon Anders HTSeq distribution
Wrapped in a loop to accept multiple bam/sam files and their names from galaxy to
@@ -90,6 +90,7 @@
features = HTSeq.GenomicArrayOfSets( "auto", opts.stranded != "no" )
mapqMin = int(opts.mapqMin)
counts = {}
+ nreads = 0
empty = 0
ambiguous = 0
notaligned = 0
@@ -123,9 +124,19 @@
for sami,sam_filename in enumerate(sam_filenames):
colname = colnames[sami]
isbam = sam_exts[sami] == 'bam'
+ hasbai = sam_bais[sami] > ''
+ if hasbai:
+ tempname = os.path.splitext(os.path.basename(sam_filename))[0]
+ tempbam = '%s.bam' % tempname
+ tempbai = '%s.bai' % tempname
+ os.link(sam_filename,tempbam)
+ os.link(sam_bais[sami],tempbai)
try:
if isbam:
- read_seq = HTSeq.BAM_Reader( sam_filename )
+ if hasbai:
+ read_seq = HTSeq.BAM_Reader ( tempbam )
+ else:
+ read_seq = HTSeq.BAM_Reader( sam_filename )
else:
read_seq = HTSeq.SAM_Reader( sam_filename )
first_read = iter(read_seq).next()
@@ -142,6 +153,7 @@
read_seq_pe_file = read_seq
read_seq = HTSeq.pair_SAM_alignments( read_seq )
for seqi,r in enumerate(read_seq):
+ nreads += 1
if not pe_mode:
if not r.aligned:
notaligned += 1
@@ -240,13 +252,13 @@
raise
if not opts.quiet:
- sys.stdout.write( "%d sam %s processed.\n" % ( seqi, "lines " if not pe_mode else "line pairs" ) )
- return counts,empty,ambiguous,lowqual,notaligned,nonunique,filtered
+ sys.stdout.write( "%d sam %s processed for %s.\n" % ( seqi, "lines " if not pe_mode else "line pairs", colname ) )
+ return counts,empty,ambiguous,lowqual,notaligned,nonunique,filtered,nreads
warnings.showwarning = my_showwarning
assert os.path.isfile(gff_filename),'## unable to open supplied gff file %s' % gff_filename
try:
- counts,empty,ambiguous,lowqual,notaligned,nonunique,filtered = count_reads_in_features( sam_filenames, colnames, gff_filename,opts)
+ counts,empty,ambiguous,lowqual,notaligned,nonunique,filtered,nreads = count_reads_in_features( sam_filenames, colnames, gff_filename,opts)
except:
sys.stderr.write( "Error: %s\n" % str( sys.exc_info()[1] ) )
sys.stderr.write( "[Exception type: %s, raised in %s:%d]\n" %
@@ -254,7 +266,7 @@
os.path.basename(traceback.extract_tb( sys.exc_info()[2] )[-1][0]),
traceback.extract_tb( sys.exc_info()[2] )[-1][1] ) )
sys.exit( 1 )
- return counts,empty,ambiguous,lowqual,notaligned,nonunique,filtered
+ return counts,empty,ambiguous,lowqual,notaligned,nonunique,filtered,nreads
def usage():
@@ -330,6 +342,7 @@
assert len(samfnames) == len(scolnames), '##ERROR sams2mx: Count of sam/cname not consistent - %d/%d' % (len(samfnames),len(scolnames))
sam_exts = [x[2] for x in samsets]
assert len(samfnames) == len(sam_exts), '##ERROR sams2mx: Count of extensions not consistent - %d/%d' % (len(samfnames),len(sam_exts))
+ sam_bais = [x[3] for x in samsets] # these only exist for bams and need to be finessed with a symlink so pysam will just work
for i,b in enumerate(samfnames):
assert os.path.isfile(b),'## Supplied input sam file "%s" not found' % b
sam_filenames.append(b)
@@ -339,7 +352,7 @@
sampName = sampName.replace(')','') # for R
sampName = sampName.replace(' ','_') # for R
colnames.append(sampName)
- counts,empty,ambiguous,lowqual,notaligned,nonunique,filtered = htseqMX(gff_file, sam_filenames,colnames,sam_exts,opts)
+ counts,empty,ambiguous,lowqual,notaligned,nonunique,filtered,nreads = htseqMX(gff_file, sam_filenames,colnames,sam_exts,sam_bais,opts)
heads = '\t'.join(['Contig',] + colnames)
res = [heads,]
contigs = counts.keys()
@@ -359,9 +372,10 @@
outf.write('\n')
outf.close()
walltime = int(time.time() - starttime)
- accumulatornames = ('walltimeseconds','totreadscounted','ncontigs','emptyreads','ambiguousreads','lowqualreads',
- 'notalignedreads','nonuniquereads','extra_filtered_reads','emptycontigs')
- accums = (walltime,totalc,len(contigs),empty,ambiguous,lowqual,notaligned,nonunique,filtered,emptycontigs)
- notes = ['%s=%d' % (accumulatornames[i],x) for i,x in enumerate(accums)]
- print >> sys.stdout, ','.join(notes)
+ accumulatornames = ('walltime (seconds)','total reads read','total reads counted','number of contigs','total empty reads','total ambiguous reads','total low quality reads',
+ 'total not aligned reads','total not unique mapping reads','extra filtered reads','empty contigs')
+ accums = (walltime,nreads,totalc,len(contigs),empty,ambiguous,lowqual,notaligned,nonunique,filtered,emptycontigs)
+ fracs = (1.0,1.0,float(totalc)/nreads,1.0,float(empty)/nreads,float(ambiguous)/nreads,float(lowqual)/nreads,float(notaligned)/nreads,float(nonunique)/nreads,float(filtered)/nreads,float(emptycontigs)/len(contigs))
+ notes = ['%s = %d (%2.3f)' % (accumulatornames[i],x,100.0*fracs[i]) for i,x in enumerate(accums)]
+ print >> sys.stdout, '\n'.join(notes)
sys.exit(0)
diff -r da94b25396024b6af382242f446d2daf117b514b -r b076688903e1f96fb0453c61d0ea38b5d92cd89c tools/rgenetics/htseqsams2mx.xml
--- a/tools/rgenetics/htseqsams2mx.xml
+++ b/tools/rgenetics/htseqsams2mx.xml
@@ -1,24 +1,25 @@
-<tool id="htseqsams2mxlocal" name="SAM/BAM to count matrix" version="0.4">
- <description>local using HTSeq code</description>
+<tool id="htseqsams2mx" name="SAM/BAM to count matrix" version="0.4">
+ <description>using HTSeq code</description><stdio><exit_code range="666" level="warning"
description="Exit code 666 encountered" /></stdio><requirements><requirement type="package" version="1.7.1">numpy</requirement>
+ <requirement type="package" version="0.7.5">pysam</requirement><requirement type="package" version="2.4.11">freetype</requirement><requirement type="package" version="1.2.1">matplotliblite</requirement><requirement type="package" version="0.5.4p3">htseq</requirement></requirements><command interpreter="python">
htseqsams2mx.py -g "$gfffile" -o "$outfile" -m "$model" --id_attribute "$id_attr" --feature_type "$feature_type"
- --mapqMin $mapqMin --samf "'${firstsamf}','${firstsamf.name}','${firstsamf.ext}'"
+ --mapqMin $mapqMin --samf "'${firstsamf}','${firstsamf.name}','${firstsamf.ext}','${firstsamf.metadata.bam_index}'"
#if $secondsamf.ext != 'data':
- --samf "'${secondsamf}','${secondsamf.name}','${secondsamf.ext}'"
+ --samf "'${secondsamf}','${secondsamf.name}','${secondsamf.ext}','${secondsamf.metadata.bam_index}'"
#end if
#for $s in $samfiles:
#if $s.samf.ext != 'data':
- --samf "'${s.samf}','${s.samf.name}','${s.samf.ext}'"
+ --samf "'${s.samf}','${s.samf.name}','${s.samf.ext}','${s.samf.metadata.bam_index}'"
#end if
#end for
#if $filter_extras:
https://bitbucket.org/galaxy/galaxy-central/commits/c7d0b32602fb/
Changeset: c7d0b32602fb
User: ross...(a)gmail.com
Date: 2013-08-08 07:38:10
Summary: branch merge with central
Affected #: 111 files
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/circster/config/circster.xml
--- /dev/null
+++ b/config/plugins/visualizations/circster/config/circster.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
+<visualization name="circster">
+ <data_sources>
+ <data_source>
+ <model_class>HistoryDatasetAssociation</model_class>
+ <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
+ <to_param param_attr="id">dataset_id</to_param>
+ <to_param assign="hda">hda_ldda</to_param>
+ </data_source>
+ <data_source>
+ <model_class>LibraryDatasetDatasetAssociation</model_class>
+ <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
+ <to_param param_attr="id">dataset_id</to_param>
+ <to_param assign="ldda">hda_ldda</to_param>
+ </data_source>
+ </data_sources>
+ <params>
+ <param type="visualization">id</param>
+ <param type="hda_or_ldda">dataset_id</param>
+ <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_location>_top</render_location>
+</visualization>
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/phyloviz/config/phyloviz.xml
--- /dev/null
+++ b/config/plugins/visualizations/phyloviz/config/phyloviz.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
+<visualization name="phyloviz">
+ <data_sources>
+ <data_source>
+ <model_class>HistoryDatasetAssociation</model_class>
+ <test type="isinstance" test_attr="datatype" result_type="datatype">data.Newick</test>
+ <test type="isinstance" test_attr="datatype" result_type="datatype">data.Nexus</test>
+ <to_param param_attr="id">dataset_id</to_param>
+ </data_source>
+ </data_sources>
+ <params>
+ <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_location>_top</render_location>
+</visualization>
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/Gruntfile.js
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/Gruntfile.js
@@ -0,0 +1,56 @@
+// NOTE: use 'sudo npm install .', then 'grunt' to use this file
+
+module.exports = function(grunt) {
+
+ grunt.initConfig({
+ pkg: grunt.file.readJSON( 'package.json' ),
+
+ handlebars: {
+ compile: {
+ options: {
+ namespace: 'Templates',
+ processName : function( filepath ){
+ return filepath.match( /\w*\.handlebars/ )[0].replace( '.handlebars', '' );
+ }
+ },
+ files: {
+ "build/compiled-templates.js" : "src/handlebars/*.handlebars"
+ }
+ }
+ },
+
+ concat: {
+ options: {
+ separator: ';\n'
+ },
+ dist: {
+ //NOTE: mvc references templates - templates must be cat'd first
+ src : [ 'build/compiled-templates.js', 'src/**/*.js' ],
+ dest: 'build/scatterplot-concat.js'
+ }
+ },
+
+ uglify: {
+ options: {
+ },
+ dist: {
+ src : 'build/scatterplot-concat.js',
+ // uglify directly into static dir
+ dest: 'static/scatterplot.js'
+ }
+ },
+
+ watch: {
+ files: [ 'src/**.js', 'src/handlebars/*.handlebars' ],
+ tasks: [ 'default' ]
+ }
+ });
+
+ grunt.loadNpmTasks( 'grunt-contrib-handlebars' );
+ grunt.loadNpmTasks( 'grunt-contrib-concat' );
+ grunt.loadNpmTasks( 'grunt-contrib-uglify' );
+ grunt.loadNpmTasks( 'grunt-contrib-watch' );
+
+ grunt.registerTask( 'default', [ 'handlebars', 'concat', 'uglify' ]);
+ grunt.registerTask( 'watch', [ 'handlebars', 'concat', 'uglify', 'watch' ]);
+};
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/config/scatterplot.xml
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/config/scatterplot.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
+<visualization name="scatterplot">
+ <data_sources>
+ <data_source>
+ <model_class>HistoryDatasetAssociation</model_class>
+ <test type="isinstance" test_attr="datatype" result_type="datatype">tabular.Tabular</test>
+ <to_param param_attr="id">dataset_id</to_param>
+ </data_source>
+ </data_sources>
+ <params>
+ <param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
+ </params>
+ <template>scatterplot/templates/scatterplot.mako</template>
+</visualization>
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/package.json
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "galaxy-scatterplot",
+ "version": "0.0.0",
+ "description": "Scatterplot visualization plugin for the Galaxy informatics framework",
+ "main": " ",
+ "scripts": {
+ "test": "test"
+ },
+ "keywords": [
+ "galaxy",
+ "visualization",
+ "d3"
+ ],
+ "author": "Carl Eberhard",
+ "license": "BSD",
+ "devDependencies": {
+ "grunt": "~0.4.1",
+ "grunt-cli": "~0.1.9",
+ "grunt-contrib-handlebars": "~0.5.10",
+ "grunt-contrib-concat": "~0.3.0",
+ "grunt-contrib-uglify": "~0.2.2",
+ "grunt-contrib-watch": "~0.5.1"
+ }
+}
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/src/handlebars/chartControl.handlebars
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/src/handlebars/chartControl.handlebars
@@ -0,0 +1,56 @@
+<p class="help-text">
+ Use the following controls to how the chart is displayed.
+ The slide controls can be moved by the mouse or, if the 'handle' is in focus, your keyboard's arrow keys.
+ Move the focus between controls by using the tab or shift+tab keys on your keyboard.
+ Use the 'Draw' button to render (or re-render) the chart with the current settings.
+ </p>
+
+ <div id="datapointSize" class="form-input numeric-slider-input">
+ <label for="datapointSize">Size of data point: </label>
+ <div class="slider-output">{{datapointSize}}</div>
+ <div class="slider"></div>
+ <p class="form-help help-text-small">
+ Size of the graphic representation of each data point
+ </p>
+ </div>
+
+ <div id="animDuration" class="form-input checkbox-input">
+ <label for="animate-chart">Animate chart transitions?: </label>
+ <input type="checkbox" id="animate-chart"
+ class="checkbox control"{{#if animDuration}} checked="true"{{/if}} />
+ <p class="form-help help-text-small">
+ Uncheck this to disable the animations used on the chart
+ </p>
+ </div>
+
+ <div id="width" class="form-input numeric-slider-input">
+ <label for="width">Chart width: </label>
+ <div class="slider-output">{{width}}</div>
+ <div class="slider"></div>
+ <p class="form-help help-text-small">
+ (not including chart margins and axes)
+ </p>
+ </div>
+
+ <div id="height" class="form-input numeric-slider-input">
+ <label for="height">Chart height: </label>
+ <div class="slider-output">{{height}}</div>
+ <div class="slider"></div>
+ <p class="form-help help-text-small">
+ (not including chart margins and axes)
+ </p>
+ </div>
+
+ <div id="X-axis-label"class="text-input form-input">
+ <label for="X-axis-label">Re-label the X axis: </label>
+ <input type="text" name="X-axis-label" id="X-axis-label" value="{{xLabel}}" />
+ <p class="form-help help-text-small"></p>
+ </div>
+
+ <div id="Y-axis-label" class="text-input form-input">
+ <label for="Y-axis-label">Re-label the Y axis: </label>
+ <input type="text" name="Y-axis-label" id="Y-axis-label" value="{{yLabel}}" />
+ <p class="form-help help-text-small"></p>
+ </div>
+
+ <input id="render-button" type="button" value="Draw" />
\ No newline at end of file
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/src/handlebars/chartDisplay.handlebars
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/src/handlebars/chartDisplay.handlebars
@@ -0,0 +1,1 @@
+<svg width="{{width}}" height="{{height}}"></svg>
\ No newline at end of file
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/src/handlebars/dataControl.handlebars
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/src/handlebars/dataControl.handlebars
@@ -0,0 +1,56 @@
+<p class="help-text">
+ Use the following controls to change the data used by the chart.
+ Use the 'Draw' button to render (or re-render) the chart with the current settings.
+ </p>
+
+ {{! column selector containers }}
+ <div class="column-select">
+ <label for="X-select">Data column for X: </label>
+ <select name="X" id="X-select">
+ {{#each numericColumns}}
+ <option value="{{index}}">{{name}}</option>
+ {{/each}}
+ </select>
+ </div>
+ <div class="column-select">
+ <label for="Y-select">Data column for Y: </label>
+ <select name="Y" id="Y-select">
+ {{#each numericColumns}}
+ <option value="{{index}}">{{name}}</option>
+ {{/each}}
+ </select>
+ </div>
+
+ {{! optional id column }}
+ <div id="include-id">
+ <label for="include-id-checkbox">Include a third column as data point IDs?</label>
+ <input type="checkbox" name="include-id" id="include-id-checkbox" />
+ <p class="help-text-small">
+ These will be displayed (along with the x and y values) when you hover over
+ a data point.
+ </p>
+ </div>
+ <div class="column-select" style="display: none">
+ <label for="ID-select">Data column for IDs: </label>
+ <select name="ID" id="ID-select">
+ {{#each allColumns}}
+ <option value="{{index}}">{{name}}</option>
+ {{/each}}
+ </select>
+ </div>
+
+ {{! if we're using generic column selection names ('column 1') - allow the user to use the first line }}
+ <div id="first-line-header" style="display: none;">
+ <p>Possible headers: {{ possibleHeaders }}
+ </p>
+ <label for="first-line-header-checkbox">Use the above as column headers?</label>
+ <input type="checkbox" name="include-id" id="first-line-header-checkbox"
+ {{#if usePossibleHeaders }}checked="true"{{/if}}/>
+ <p class="help-text-small">
+ It looks like Galaxy couldn't get proper column headers for this data.
+ Would you like to use the column headers above as column names to select columns?
+ </p>
+ </div>
+
+ <input id="render-button" type="button" value="Draw" />
+ <div class="clear"></div>
\ No newline at end of file
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/src/handlebars/scatterplotControlForm.handlebars
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/src/handlebars/scatterplotControlForm.handlebars
@@ -0,0 +1,46 @@
+{{! main layout }}
+
+<div class="scatterplot-container chart-container tabbable tabs-left">
+ {{! tab buttons/headers using Bootstrap }}
+ <ul class="nav nav-tabs">
+ {{! start with the data controls as the displayed tab }}
+ <li class="active"><a href="#data-control" data-toggle="tab" class="tooltip"
+ title="Use this tab to change which data are used">Data Controls</a></li>
+ <li><a href="#chart-control" data-toggle="tab" class="tooltip"
+ title="Use this tab to change how the chart is drawn">Chart Controls</a></li>
+ <li><a href="#stats-display" data-toggle="tab" class="tooltip"
+ title="This tab will display overall statistics for your data">Statistics</a></li>
+ <li><a href="#chart-display" data-toggle="tab" class="tooltip"
+ title="This tab will display the chart">Chart</a>
+ {{! loading indicator - initially hidden }}
+ <div id="loading-indicator" style="display: none;">
+ <img class="loading-img" src="{{loadingIndicatorImagePath}}" />
+ <span class="loading-message">{{message}}</span>
+ </div>
+ </li>
+ </ul>
+
+ {{! data form, chart config form, stats, and chart all get their own tab }}
+ <div class="tab-content">
+ {{! ---------------------------- tab for data settings form }}
+ <div id="data-control" class="tab-pane active">
+ {{! rendered separately }}
+ </div>
+
+ {{! ---------------------------- tab for chart graphics control form }}
+ <div id="chart-control" class="tab-pane">
+ {{! rendered separately }}
+ </div>
+
+ {{! ---------------------------- tab for data statistics }}
+ <div id="stats-display" class="tab-pane">
+ {{! rendered separately }}
+ </div>
+
+ {{! ---------------------------- tab for actual chart }}
+ <div id="chart-display" class="tab-pane">
+ {{! chart rendered separately }}
+ </div>
+
+ </div>{{! end .tab-content }}
+</div>{{! end .chart-control }}
\ No newline at end of file
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/src/handlebars/statsDisplay.handlebars
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/src/handlebars/statsDisplay.handlebars
@@ -0,0 +1,8 @@
+<p class="help-text">By column:</p>
+ <table id="chart-stats-table">
+ <thead><th></th><th>X</th><th>Y</th></thead>
+ {{#each stats}}
+ <tr><td>{{name}}</td><td>{{xval}}</td><td>{{yval}}</td></tr>
+ </tr>
+ {{/each}}
+ </table>
\ No newline at end of file
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/src/scatterplot.js
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/src/scatterplot.js
@@ -0,0 +1,488 @@
+/* =============================================================================
+todo:
+ outside this:
+ BUG: setting width, height in plot controls doesn't re-interpolate data locations!!
+ BUG?: get metadata_column_names (from datatype if necessary)
+ BUG: single vis in popupmenu should have tooltip with that name NOT 'Visualizations'
+
+ wire label setters, anim setter
+
+ TwoVarScatterplot:
+ ??: maybe better to do this with a canvas...
+ save as visualization
+ to seperate file?
+ remove underscore dependencies
+ add interface to change values (seperate)?
+ download svg -> base64 encode
+ incorporate glyphs, glyph state renderers
+
+ ScatterplotSettingsForm:
+ some css bug that lowers the width of settings form when plot-controls tab is open
+ causes chart to shift
+ what can be abstracted/reused for other graphs?
+ avoid direct manipulation of this.plot
+ allow option to put plot into seperate tab of interface (for small multiples)
+
+ provide callback in view to load data incrementally - for large sets
+ paginate
+ handle rerender
+ use endpoint (here and on the server (fileptr))
+ fetch (new?) data
+ handle rerender
+ use d3.TSV?
+ render warning on long data (> maxDataPoints)
+ adjust endpoint
+
+ selectable list of preset column comparisons (rnaseq etc.)
+ how to know what sort of Tabular the data is?
+ smarter about headers
+ validate columns selection (here or server)
+
+ set stats column names by selected columns
+ move chart into tabbed area...
+
+ Scatterplot.mako:
+ multiple plots on one page (small multiples)
+ ?? ensure svg styles thru d3 or css?
+ d3: configable (easily)
+ css: standard - better maintenance
+ ? override at config
+
+============================================================================= */
+/**
+ * Two Variable scatterplot visualization using d3
+ * Uses semi transparent circles to show density of data in x, y grid
+ * usage :
+ * var plot = new TwoVarScatterplot({ containerSelector : 'div#my-plot', ... })
+ * plot.render( xColumnData, yColumnData );
+ *
+ * depends on: d3, underscore
+ */
+function TwoVarScatterplot( config ){
+ var TICK_LINE_AND_PADDING = 10,
+ GUESS_AT_SVG_CHAR_WIDTH = 7,
+ GUESS_AT_SVG_CHAR_HEIGHT = 10,
+ PADDING = 8,
+ X_LABEL_TOO_LONG_AT = 5;
+
+ // set up logging
+ //this.debugging = true;
+ this.log = function(){
+ if( this.debugging && console && console.debug ){
+ var args = Array.prototype.slice.call( arguments );
+ args.unshift( this.toString() );
+ console.debug.apply( console, args );
+ }
+ };
+ this.log( 'new TwoVarScatterplot:', config );
+
+ // ........................................................ set up chart config
+ // config will default to these values when not passed in
+ //NOTE: called on new
+ this.defaults = {
+ id : 'TwoVarScatterplot',
+ containerSelector : 'body',
+ //TODO??: needed?
+ maxDataPoints : 30000,
+ datapointSize : 4,
+ animDuration : 500,
+ //TODO: variable effect (not always exactly # of ticks set to)
+ xNumTicks : 10,
+ yNumTicks : 10,
+ xAxisLabelBumpY : 40,
+ yAxisLabelBumpX : -40,
+ width : 400,
+ height : 400,
+ //TODO: anyway to make this a sub-obj?
+ marginTop : 50,
+ marginRight : 50,
+ marginBottom : 50,
+ marginLeft : 50,
+
+ xMin : null,
+ xMax : null,
+ yMin : null,
+ yMax : null,
+
+ xLabel : "X",
+ yLabel : "Y"
+ };
+ this.config = _.extend( {}, this.defaults, config );
+ this.log( 'intial config:', this.config );
+
+ this.updateConfig = function( newConfig, rerender ){
+ // setter for chart config
+ //TODO: validate here
+ _.extend( this.config, newConfig );
+ this.log( this + '.updateConfig:', this.config );
+ //TODO: implement rerender flag
+ };
+
+ // ........................................................ helpers
+ this.toString = function(){
+ return this.config.id;
+ };
+ // conv. methods for svg transforms
+ this.translateStr = function( x, y ){
+ return 'translate(' + x + ',' + y + ')';
+ };
+ this.rotateStr = function( d, x, y ){
+ return 'rotate(' + d + ',' + x + ',' + y + ')';
+ };
+
+ // ........................................................ initial element creation
+ this.adjustChartDimensions = function( top, right, bottom, left ){
+ //this.log( this + '.adjustChartDimensions', arguments );
+ top = top || 0;
+ right = right || 0;
+ bottom = bottom || 0;
+ left = left || 0;
+ this.svg
+ .attr( "width", this.config.width + ( this.config.marginRight + right )
+ + ( this.config.marginLeft + left ) )
+ .attr( "height", this.config.height + ( this.config.marginTop + top )
+ + ( this.config.marginBottom + bottom ) )
+ // initial is hidden - show it
+ .style( 'display', 'block' );
+
+ // move content group away from margins
+ //TODO: allow top, right axis
+ this.content = this.svg.select( "g.content" )
+ .attr( "transform", this.translateStr( this.config.marginLeft + left, this.config.marginTop + top ) );
+ };
+
+ // ........................................................ data and scales
+ this.preprocessData = function( data, min, max ){
+ //this.log( this + '.preprocessData', arguments );
+ //TODO: filter by min, max if set
+
+ // set a cap on the data, limit to first n points
+ return ( data.length > this.config.maxDataPoints )? ( data.slice( 0, this.config.maxDataPoints ) ): ( data );
+ };
+
+ this.findMinMaxes = function( xCol, yCol, meta ){
+ //this.log( this + '.findMinMaxes', arguments );
+ // configuration takes priority, otherwise meta (from the server) if passed, last-resort: compute it here
+ this.xMin = this.config.xMin || ( meta )?( meta[0].min ):( d3.min( xCol ) );
+ this.xMax = this.config.xMax || ( meta )?( meta[0].max ):( d3.max( xCol ) );
+ this.yMin = this.config.yMin || ( meta )?( meta[1].min ):( d3.min( yCol ) );
+ this.yMax = this.config.yMax || ( meta )?( meta[1].max ):( d3.max( yCol ) );
+ };
+
+ this.setUpScales = function(){
+ //this.log( this + '.setUpScales', arguments );
+ // Interpolation for x, y based on data domains
+ this.xScale = d3.scale.linear()
+ .domain([ this.xMin, this.xMax ])
+ .range([ 0, this.config.width ]),
+ this.yScale = d3.scale.linear()
+ .domain([ this.yMin, this.yMax ])
+ .range([ this.config.height, 0 ]);
+ };
+
+ // ........................................................ axis and ticks
+ this.setUpXAxis = function(){
+ //this.log( this + '.setUpXAxis', arguments );
+ // origin: bottom, left
+ //TODO: incoporate top, right
+ this.xAxisFn = d3.svg.axis()
+ .scale( this.xScale )
+ .ticks( this.config.xNumTicks )
+ .orient( 'bottom' );
+ this.xAxis// = content.select( 'g#x-axis' )
+ .attr( 'transform', this.translateStr( 0, this.config.height ) )
+ .call( this.xAxisFn );
+ //this.log( 'xAxis:', this.xAxis );
+
+ //TODO: adjust ticks when tick labels are long - move odds down and extend tick line
+ // (for now) hide them
+ var xLongestTickLabel = d3.max( _.map( [ this.xMin, this.xMax ],
+ function( number ){ return ( String( number ) ).length; } ) );
+ //this.log( 'xLongestTickLabel:', xLongestTickLabel );
+ if( xLongestTickLabel >= X_LABEL_TOO_LONG_AT ){
+ this.xAxis.selectAll( 'g' ).filter( ':nth-child(odd)' ).style( 'display', 'none' );
+ }
+
+ this.log( 'this.config.xLabel:', this.config.xLabel );
+ this.xAxisLabel// = xAxis.select( 'text#x-axis-label' )
+ .attr( 'x', this.config.width / 2 )
+ .attr( 'y', this.config.xAxisLabelBumpY )
+ .attr( 'text-anchor', 'middle' )
+ .text( this.config.xLabel );
+ this.log( 'xAxisLabel:', this.xAxisLabel );
+ };
+
+ this.setUpYAxis = function(){
+ //this.log( this + '.setUpYAxis', arguments );
+ this.yAxisFn = d3.svg.axis()
+ .scale( this.yScale )
+ .ticks( this.config.yNumTicks )
+ .orient( 'left' );
+ this.yAxis// = content.select( 'g#y-axis' )
+ .call( this.yAxisFn );
+ //this.log( 'yAxis:', this.yAxis );
+
+ // a too complicated section for increasing the left margin when tick labels are long
+ // get the tick labels for the y axis
+ var yTickLabels = this.yAxis.selectAll( 'text' ).filter( function( e, i ){ return i !== 0; } );
+ this.log( 'yTickLabels:', yTickLabels );
+
+ // get the longest label length (or 0 if no labels)
+ this.yLongestLabel = d3.max(
+ //NOTE: d3 returns an nested array - use the plain array inside ([0])
+ yTickLabels[0].map( function( e, i ){
+ return ( d3.select( e ).text() ).length;
+ })
+ ) || 0;
+ //this.log( 'yLongestLabel:', this.yLongestLabel );
+ //TODO: lose the guessing if possible
+ var neededY = TICK_LINE_AND_PADDING + ( this.yLongestLabel * GUESS_AT_SVG_CHAR_WIDTH )
+ + PADDING + GUESS_AT_SVG_CHAR_HEIGHT;
+ //this.log( 'neededY:', neededY );
+
+ // increase width for yLongerStr, increase margin for y
+ //TODO??: (or transform each number: 2k)
+ this.config.yAxisLabelBumpX = -( neededY - GUESS_AT_SVG_CHAR_HEIGHT );
+ if( this.config.marginLeft < neededY ){
+ var adjusting = ( neededY ) - this.config.marginLeft;
+ adjusting = ( adjusting < 0 )?( 0 ):( adjusting );
+ //this.log( 'adjusting:', adjusting );
+
+ // update dimensions, translations
+ this.adjustChartDimensions( 0, 0, 0, adjusting );
+ }
+ //this.log( 'this.config.yAxisLableBumpx, this.config.marginLeft:',
+ // this.config.yAxisLabelBumpX, this.config.marginLeft );
+
+ this.yAxisLabel// = yAxis.select( 'text#y-axis-label' )
+ .attr( 'x', this.config.yAxisLabelBumpX )
+ .attr( 'y', this.config.height / 2 )
+ .attr( 'text-anchor', 'middle' )
+ .attr( 'transform', this.rotateStr( -90, this.config.yAxisLabelBumpX, this.config.height / 2 ) )
+ .text( this.config.yLabel );
+ //this.log( 'yAxisLabel:', this.yAxisLabel );
+ };
+
+ // ........................................................ grid lines
+ this.renderGrid = function(){
+ //this.log( this + '.renderGrid', arguments );
+ // VERTICAL
+ // select existing
+ this.vGridLines = this.content.selectAll( 'line.v-grid-line' )
+ .data( this.xScale.ticks( this.xAxisFn.ticks()[0] ) );
+
+ // append any extra lines needed (more ticks)
+ this.vGridLines.enter().append( 'svg:line' )
+ .classed( 'grid-line v-grid-line', true );
+
+ // update the attributes of existing and appended
+ this.vGridLines
+ .attr( 'x1', this.xScale )
+ .attr( 'y1', 0 )
+ .attr( 'x2', this.xScale )
+ .attr( 'y2', this.config.height );
+
+ // remove unneeded (less ticks)
+ this.vGridLines.exit().remove();
+ //this.log( 'vGridLines:', this.vGridLines );
+
+ // HORIZONTAL
+ this.hGridLines = this.content.selectAll( 'line.h-grid-line' )
+ .data( this.yScale.ticks( this.yAxisFn.ticks()[0] ) );
+
+ this.hGridLines.enter().append( 'svg:line' )
+ .classed( 'grid-line h-grid-line', true );
+
+ this.hGridLines
+ .attr( 'x1', 0 )
+ .attr( 'y1', this.yScale )
+ .attr( 'x2', this.config.width )
+ .attr( 'y2', this.yScale );
+
+ this.hGridLines.exit().remove();
+ //this.log( 'hGridLines:', this.hGridLines );
+ };
+
+ // ........................................................ data points
+ this.renderDatapoints = function( xCol, yCol, ids ){
+ this.log( this + '.renderDatapoints', arguments );
+ var count = 0,
+ plot = this,
+ xPosFn = function( d, i ){
+ //if( d ){ this.log( 'x.data:', newXCol[ i ], 'plotted:', plot.xScale( newXCol[ i ] ) ); }
+ return plot.xScale( xCol[ i ] );
+ },
+ yPosFn = function( d, i ){
+ //if( d ){ this.log( 'y.data:', newYCol[ i ], 'plotted:', plot.yScale( newYCol[ i ] ) ); }
+ return plot.yScale( yCol[ i ] );
+ };
+
+ //this.datapoints = this.addDatapoints( xCol, yCol, ids, ".glyph" );
+ var datapoints = this.content.selectAll( '.glyph' ).data( xCol );
+
+ // enter - NEW data to be added as glyphs: give them a 'entry' position and style
+ count = 0;
+ datapoints.enter()
+ .append( 'svg:circle' )
+ .each( function(){ count += 1; } )
+ .classed( "glyph", true )
+ .attr( "cx", 0 )
+ .attr( "cy", this.config.height )
+ // start all bubbles small...
+ .attr( "r", 0 );
+ this.log( count, ' new glyphs created' );
+
+ // for all EXISTING glyphs and those that need to be added: transition anim to final state
+ count = 0;
+ datapoints
+ // ...animate to final position
+ .transition().duration( this.config.animDuration )
+ .each( function(){ count += 1; } )
+ .attr( "cx", xPosFn )
+ .attr( "cy", yPosFn )
+ .attr( "r", plot.config.datapointSize );
+ this.log( count, ' existing glyphs transitioned' );
+
+ // events
+ // glyphs that need to be removed: transition to from normal state to 'exit' state, remove from DOM
+ datapoints.exit()
+ .each( function(){ count += 1; } )
+ .transition().duration( this.config.animDuration )
+ .attr( "cy", this.config.height )
+ .attr( "r", 0 )
+ .remove();
+ this.log( count, ' glyphs removed' );
+
+ this._addDatapointEventhandlers( datapoints, xCol, yCol, ids );
+ };
+
+ this._addDatapointEventhandlers = function( datapoints, xCol, yCol, ids ){
+ var plot = this;
+ datapoints
+ //TODO: remove magic numbers
+ .on( 'mouseover', function( d, i ){
+ var datapoint = d3.select( this );
+ datapoint
+ .style( 'fill', 'red' )
+ .style( 'fill-opacity', 1 );
+
+ // create horiz, vert lines to axis
+ plot.content.append( 'line' )
+ .attr( 'stroke', 'red' )
+ .attr( 'stroke-width', 1 )
+ // start not at center, but at the edge of the circle - to prevent mouseover thrashing
+ .attr( 'x1', datapoint.attr( 'cx' ) - plot.config.datapointSize )
+ .attr( 'y1', datapoint.attr( 'cy' ) )
+ .attr( 'x2', 0 )
+ .attr( 'y2', datapoint.attr( 'cy' ) )
+ .classed( 'hoverline', true );
+
+ // if the vertical hoverline
+ if( datapoint.attr( 'cy' ) < plot.config.height ){
+ plot.content.append( 'line' )
+ .attr( 'stroke', 'red' )
+ .attr( 'stroke-width', 1 )
+ .attr( 'x1', datapoint.attr( 'cx' ) )
+ .attr( 'y1', datapoint.attr( 'cy' ) + plot.config.datapointSize )
+ .attr( 'x2', datapoint.attr( 'cx' ) )
+ .attr( 'y2', plot.config.height )
+ .classed( 'hoverline', true );
+ }
+
+ var datapointWindowPos = $( this ).offset();
+ plot.datapointInfoBox = plot.infoBox(
+ datapointWindowPos.top, datapointWindowPos.left,
+ plot.infoHtml( xCol[ i ], yCol[ i ], ( ids )?( ids[ i ] ):( undefined ) )
+ );
+ $( 'body' ).append( plot.datapointInfoBox );
+ })
+ .on( 'mouseout', function(){
+ d3.select( this )
+ .style( 'fill', 'black' )
+ .style( 'fill-opacity', 0.2 );
+ plot.content.selectAll( '.hoverline' ).remove();
+ if( plot.datapointInfoBox ){
+ plot.datapointInfoBox.remove();
+ }
+ });
+ },
+
+ this.render = function( columnData, meta ){
+ this.log( this + '.render', arguments );
+ this.log( '\t config:', this.config );
+
+ // prepare the data
+ //pre: columns passed are numeric
+ //pre: at least two columns are passed
+ //assume: first column is x, second column is y, any remaining aren't used
+ var xCol = columnData[0],
+ yCol = columnData[1],
+ ids = ( columnData.length > 2 )?( columnData[2] ):( undefined );
+ //this.log( this + '.render', xCol.length, yCol.length, this.config );
+
+ //pre: xCol.len == yCol.len
+ xCol = this.preprocessData( xCol );
+ yCol = this.preprocessData( yCol );
+ this.log( 'xCol len', xCol.length, 'yCol len', yCol.length );
+
+ this.findMinMaxes( xCol, yCol, meta );
+ //this.log( 'xMin, xMax, yMin, yMax:', this.xMin, this.xMax, this.yMin, this.yMax );
+ this.setUpScales();
+
+ // find (or build if it doesn't exist) the svg dom infrastructure
+ if( !this.svg ){ this.svg = d3.select( 'svg' ).attr( "class", "chart" ); }
+ if( !this.content ){
+ this.content = this.svg.append( "svg:g" ).attr( "class", "content" ).attr( 'id', this.config.id );
+ }
+ //this.log( 'svg:', this.svg );
+ //this.log( 'content:', this.content );
+
+ this.adjustChartDimensions();
+
+ if( !this.xAxis ){ this.xAxis = this.content.append( 'g' ).attr( 'class', 'axis' ).attr( 'id', 'x-axis' ); }
+ if( !this.xAxisLabel ){
+ this.xAxisLabel = this.xAxis.append( 'text' ).attr( 'class', 'axis-label' ).attr( 'id', 'x-axis-label' );
+ }
+ //this.log( 'xAxis:', this.xAxis, 'xAxisLabel:', this.xAxisLabel );
+
+ if( !this.yAxis ){ this.yAxis = this.content.append( 'g' ).attr( 'class', 'axis' ).attr( 'id', 'y-axis' ); }
+ if( !this.yAxisLabel ){
+ this.yAxisLabel = this.yAxis.append( 'text' ).attr( 'class', 'axis-label' ).attr( 'id', 'y-axis-label' );
+ }
+ //this.log( 'yAxis:', this.yAxis, 'yAxisLabel:', this.yAxisLabel );
+
+ this.setUpXAxis();
+ this.setUpYAxis();
+
+ this.renderGrid();
+ this.renderDatapoints( xCol, yCol, ids );
+ };
+
+ this.infoHtml = function( x, y, id ){
+ var retDiv = $( '<div/>' );
+ if( id ){
+ $( '<div/>' ).text( id ).css( 'font-weight', 'bold' ).appendTo( retDiv );
+ }
+ $( '<div/>' ).text( x ).appendTo( retDiv );
+ $( '<div/>' ).text( y ).appendTo( retDiv );
+ return retDiv.html();
+ };
+
+ //TODO: html for now
+ this.infoBox = function( top, left, html, adjTop, adjLeft ){
+ adjTop = adjTop || 0;
+ adjLeft = adjLeft || 20;
+ var infoBox = $( '<div />' )
+ .addClass( 'chart-info-box' )
+ .css({
+ 'position' : 'absolute',
+ 'top' : top + adjTop,
+ 'left' : left + adjLeft
+ });
+ infoBox.html( html );
+ return infoBox;
+ };
+
+}
+
+//==============================================================================
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/src/scatterplotControlForm.js
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/src/scatterplotControlForm.js
@@ -0,0 +1,631 @@
+/* =============================================================================
+todo:
+ I'd like to move the svg creation out of the splot constr. to:
+ allow adding splots to an existing canvas
+ allow mult. splots sharing a canvas
+
+
+ outside this:
+ BUG: setting width, height in plot controls doesn't re-interpolate data locations!!
+ BUG?: get metadata_column_names (from datatype if necessary)
+ BUG: single vis in popupmenu should have tooltip with that name NOT 'Visualizations'
+
+ wire label setters, anim setter
+
+ TwoVarScatterplot:
+ ??: maybe better to do this with a canvas...
+ save as visualization
+ to seperate file?
+ remove underscore dependencies
+ add interface to change values (seperate)?
+ download svg -> base64 encode
+ incorporate glyphs, glyph state renderers
+
+ ScatterplotSettingsForm:
+ some css bug that lowers the width of settings form when plot-controls tab is open
+ causes chart to shift
+ what can be abstracted/reused for other graphs?
+ avoid direct manipulation of this.plot
+ allow option to put plot into seperate tab of interface (for small multiples)
+
+ provide callback in view to load data incrementally - for large sets
+ paginate
+ handle rerender
+ use endpoint (here and on the server (fileptr))
+ fetch (new?) data
+ handle rerender
+ use d3.TSV?
+ render warning on long data (> maxDataPoints)
+ adjust endpoint
+
+ selectable list of preset column comparisons (rnaseq etc.)
+ how to know what sort of Tabular the data is?
+ smarter about headers
+ validate columns selection (here or server)
+
+ set stats column names by selected columns
+ move chart into tabbed area...
+
+ Scatterplot.mako:
+ multiple plots on one page (small multiples)
+ ?? ensure svg styles thru d3 or css?
+ d3: configable (easily)
+ css: standard - better maintenance
+ ? override at config
+
+============================================================================= */
+/**
+ * Scatterplot control UI as a backbone view
+ * handles:
+ * getting the desired data
+ * configuring the plot display
+ * showing (general) statistics
+ *
+ * initialize attributes REQUIRES a dataset and an apiDatasetsURL
+ */
+var ScatterplotControlForm = BaseView.extend( LoggableMixin ).extend({
+ //logger : console,
+ className : 'scatterplot-control-form',
+
+ //NOTE: should include time needed to render
+ dataLoadDelay : 4000,
+ dataLoadSize : 5000,
+
+ loadingIndicatorImage : 'loading_small_white_bg.gif',
+ fetchMsg : 'Fetching data...',
+ renderMsg : 'Rendering...',
+
+ initialize : function( attributes ){
+ this.log( this + '.initialize, attributes:', attributes );
+
+ this.dataset = null;
+ this.chartConfig = null;
+ this.chart = null;
+ this.loader = null;
+
+ // set up refs to the four tab areas
+ this.$dataControl = null;
+ this.$chartControl = null;
+ this.$statsDisplay = null;
+ this.$chartDisplay = null;
+
+ this.dataFetch = null;
+
+ this.initializeFromAttributes( attributes );
+ this.initializeChart( attributes );
+ this.initializeDataLoader( attributes );
+ },
+
+ initializeFromAttributes : function( attributes ){
+ // required settings: ensure certain vars we need are passed in attributes
+ if( !attributes || !attributes.dataset ){
+ throw( "ScatterplotView requires a dataset" );
+ } else {
+ this.dataset = attributes.dataset;
+ }
+ if( jQuery.type( this.dataset.metadata_column_types ) === 'string' ){
+ this.dataset.metadata_column_types = this.dataset.metadata_column_types.split( ', ' );
+ }
+ this.log( '\t dataset:', this.dataset );
+
+ // attempt to get possible headers from the data's first line
+ if( this.dataset.comment_lines && this.dataset.comment_lines.length ){
+ //TODO:??
+ var firstLine = this.dataset.comment_lines[0],
+ possibleHeaders = firstLine.split( '\t' );
+ if( possibleHeaders.length === this.dataset.metadata_column_types.length ){
+ this.possibleHeaders = possibleHeaders;
+ }
+ }
+
+ // passed from mako helper
+ //TODO: integrate to galaxyPaths
+ //TODO: ?? seems like data loader section would be better
+ if( !attributes.apiDatasetsURL ){
+ throw( "ScatterplotView requires a apiDatasetsURL" );
+ } else {
+ this.dataURL = attributes.apiDatasetsURL + '/' + this.dataset.id + '?';
+ }
+ this.log( '\t dataURL:', this.dataURL );
+ },
+
+ initializeChart : function( attributes ){
+ // set up the basic chart infrastructure and config (if any)
+ this.chartConfig = attributes.chartConfig || {};
+ //if( this.logger ){ this.chartConfig.debugging = true; }
+ this.log( '\t initial chartConfig:', this.chartConfig );
+
+ this.chart = new TwoVarScatterplot( this.chartConfig );
+ //TODO: remove 2nd ref, use this.chart.config
+ this.chartConfig = this.chart.config;
+ },
+
+ initializeDataLoader : function( attributes ){
+ // set up data loader
+ var view = this;
+ this.loader = new LazyDataLoader({
+ //logger : ( this.logger )?( this.logger ):( null ),
+ // we'll generate this when columns are chosen
+ url : null,
+ start : attributes.start || 0,
+ //NOTE: metadata_data_lines can be null (so we won't know the total)
+ total : attributes.total || this.dataset.metadata_data_lines,
+ delay : this.dataLoadDelay,
+ size : this.dataLoadSize,
+
+ buildUrl : function( start, size ){
+ // currently VERY SPECIFIC to using data_providers.py start_val, max_vals params
+ return this.url + '&' + jQuery.param({
+ start_val: start,
+ max_vals: size
+ });
+ }
+ });
+ $( this.loader ).bind( 'error', function( event, status, error ){
+ view.log( 'ERROR:', status, error );
+ alert( 'ERROR fetching data:\n' + status + '\n' + error );
+ view.hideLoadingIndicator();
+ });
+ },
+
+ // ------------------------------------------------------------------------- CONTROLS RENDERING
+ render : function(){
+ this.log( this + '.render' );
+
+ // render the tab controls, areas and loading indicator
+ this.$el.append( ScatterplotControlForm.templates.mainLayout({
+ loadingIndicatorImagePath : '/static/images/' + this.loadingIndicatorImage,
+ message : ''
+ }));
+
+ // render the tab content
+ this.$dataControl = this._render_dataControl();
+ this.$chartControl = this._render_chartControl();
+ this.$statsDisplay = this.$el.find( '.tab-pane#stats-display' );
+ this.$chartDisplay = this._render_chartDisplay();
+
+ // auto render if given both x, y column choices in query for page
+ //TODO:?? add autoRender=1 to query maybe?
+ if( this.chartConfig.xColumn && this.chartConfig.yColumn ){
+ this.renderChart();
+ }
+
+ // set up behaviours
+ this.$el.find( '.tooltip' ).tooltip();
+
+ // uncomment any of the following to have that tab show on initial load (for testing)
+ //this.$el.find( 'ul.nav' ).find( 'a[href="#data-control"]' ).tab( 'show' );
+ //this.$el.find( 'ul.nav' ).find( 'a[href="#chart-control"]' ).tab( 'show' );
+ //this.$el.find( 'ul.nav' ).find( 'a[href="#stats-display"]' ).tab( 'show' );
+ //this.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' );
+ return this;
+ },
+
+ _render_dataControl : function(){
+ // controls for which columns are used to plot datapoints (and ids/additional info to attach if desired)
+ var view = this,
+ allColumns = [],
+ numericColumns = [],
+ usePossibleHeaders = ( this.possibleHeaders && this.$dataControl )?
+ ( this.$dataControl.find( '#first-line-header-checkbox' ).is( ':checked' ) ):( false );
+
+ // gather column indeces (from metadata_column_types) and names (from metadata_columnnames)
+ _.each( this.dataset.metadata_column_types, function( type, index ){
+ // use a 1 based index in names/values within the form (will be dec. when parsed out)
+ var oneBasedIndex = index + 1,
+ // default name is 'column <index>'...
+ name = 'column ' + oneBasedIndex;
+
+ // ...but label with the name if available...
+ if( view.dataset.metadata_column_names ){
+ name = view.dataset.metadata_column_names[ index ];
+
+ // ...or, use the first line as headers if the user wants
+ } else if( usePossibleHeaders ){
+ name = view.possibleHeaders[ index ];
+ }
+
+ // cache all columns here
+ allColumns.push({ index: oneBasedIndex, name: name });
+
+ // filter numeric columns to their own list
+ if( type === 'int' || type === 'float' ){
+ numericColumns.push({ index: oneBasedIndex, name: name });
+ }
+ });
+ //TODO: other vals: max_vals, start_val, pagination (chart-settings)
+
+ // render the html
+ var $dataControl = this.$el.find( '.tab-pane#data-control' );
+ $dataControl.html( ScatterplotControlForm.templates.dataControl({
+ allColumns : allColumns,
+ numericColumns : numericColumns,
+ possibleHeaders : ( this.possibleHeaders )?( this.possibleHeaders.join( ', ' ) ):( '' ),
+ usePossibleHeaders : usePossibleHeaders
+ }));
+
+ if( !this.dataset.metadata_column_names && this.possibleHeaders ){
+ $dataControl.find( '#first-line-header' ).show();
+ }
+
+ // preset to column selectors if they were passed in the config in the query string
+ $dataControl.find( '#X-select' ).val( this.chartConfig.xColumn );
+ $dataControl.find( '#Y-select' ).val( this.chartConfig.yColumn );
+ if( this.chartConfig.idColumn !== undefined ){
+ $dataControl.find( '#include-id-checkbox' )
+ .attr( 'checked', true ).trigger( 'change' );
+ $dataControl.find( '#ID-select' ).val( this.chartConfig.idColumn );
+ }
+
+ return $dataControl;
+ },
+
+ _render_chartControl : function(){
+ // tab content to control how the chart is rendered (data glyph size, chart size, etc.)
+ var view = this,
+ $chartControl = this.$el.find( '.tab-pane#chart-control' ),
+ // limits for controls (by control/chartConfig id)
+ //TODO: move into TwoVarScatterplot
+ controlRanges = {
+ 'datapointSize' : { min: 2, max: 10, step: 1 },
+ 'width' : { min: 200, max: 800, step: 20 },
+ 'height' : { min: 200, max: 800, step: 20 }
+ };
+
+ // render the html
+ $chartControl.append( ScatterplotControlForm.templates.chartControl( this.chartConfig ) );
+
+ // set up behaviours, js on sliders
+ $chartControl.find( '.numeric-slider-input' ).each( function(){
+ var $this = $( this ),
+ $output = $this.find( '.slider-output' ),
+ $slider = $this.find( '.slider' ),
+ id = $this.attr( 'id' );
+ //chartControl.log( 'slider set up', 'this:', $this, 'slider:', $slider, 'id', id );
+
+ // what to do when the slider changes: update display and update chartConfig
+ //TODO: move out of loop
+ function onSliderChange(){
+ var $this = $( this ),
+ newValue = $this.slider( 'value' );
+ //chartControl.log( 'slider change', 'this:', $this, 'output:', $output, 'value', newValue );
+ $output.text( newValue );
+ //chartControl.chartConfig[ id ] = newValue;
+ }
+
+ $slider.slider( _.extend( controlRanges[ id ], {
+ value : view.chartConfig[ id ],
+ change : onSliderChange,
+ slide : onSliderChange
+ }));
+ });
+
+ return $chartControl;
+ },
+
+ _render_chartDisplay : function(){
+ // render the tab content where the chart is displayed (but not the chart itself)
+ var $chartDisplay = this.$el.find( '.tab-pane#chart-display' );
+ $chartDisplay.append( ScatterplotControlForm.templates.chartDisplay( this.chartConfig ) );
+ return $chartDisplay;
+ },
+
+ // ------------------------------------------------------------------------- EVENTS
+ events : {
+ 'change #include-id-checkbox' : 'toggleThirdColumnSelector',
+ 'change #first-line-header-checkbox' : 'rerenderDataControl',
+ 'click #data-control #render-button' : 'renderChart',
+ 'click #chart-control #render-button' : 'changeChartSettings'
+ },
+
+ toggleThirdColumnSelector : function(){
+ // show/hide the id selector on the data settings panel
+ this.$el.find( 'select[name="ID"]' ).parent().toggle();
+ },
+
+ rerenderDataControl : function(){
+ this.$dataControl = this._render_dataControl();
+ },
+
+ showLoadingIndicator : function( message, callback ){
+ // display the loading indicator over the tab panels if hidden, update message (if passed)
+ message = message || '';
+ var indicator = this.$el.find( 'div#loading-indicator' );
+ messageBox = indicator.find( '.loading-message' );
+
+ if( indicator.is( ':visible' ) ){
+ if( message ){
+ messageBox.fadeOut( 'fast', function(){
+ messageBox.text( message );
+ messageBox.fadeIn( 'fast', callback );
+ });
+ } else {
+ callback();
+ }
+
+ } else {
+ if( message ){ messageBox.text( message ); }
+ indicator.fadeIn( 'fast', callback );
+ }
+ },
+
+ hideLoadingIndicator : function( callback ){
+ this.$el.find( 'div#loading-indicator' ).fadeOut( 'fast', callback );
+ },
+
+ // ------------------------------------------------------------------------- CHART/STATS RENDERING
+ renderChart : function(){
+ // fetch the data, (re-)render the chart
+ this.log( this + '.renderChart' );
+
+ //TODO: separate data fetch
+
+ // this is a complete re-render, so clear the prev. data
+ this.data = null;
+ this.meta = null;
+
+ // update the chartConfig (here and chart) using chart settings
+ //TODO: separate and improve (used in changeChartSettings too)
+ _.extend( this.chartConfig, this.getChartSettings() );
+ this.log( '\t chartConfig:', this.chartConfig );
+ this.chart.updateConfig( this.chartConfig, false );
+
+ // build the url with the current data settings
+ this.loader.url = this.dataURL + '&' + jQuery.param( this.getDataSettings() );
+ this.log( '\t loader: total lines:', this.loader.total, ' url:', this.loader.url );
+
+ // bind the new data event to: aggregate data, update the chart and stats with new data
+ var view = this;
+ $( this.loader ).bind( 'loaded.new', function( event, response ){
+ view.log( view + ' loaded.new', response );
+
+ // aggregate data and meta
+ view.postProcessDataFetchResponse( response );
+ view.log( '\t postprocessed data:', view.data );
+ view.log( '\t postprocessed meta:', view.meta );
+
+ // update the chart and stats
+ view.showLoadingIndicator( view.renderMsg, function(){
+ view.chart.render( view.data, view.meta );
+ view.renderStats( view.data, view.meta );
+ view.hideLoadingIndicator();
+ });
+ });
+ // when all data loaded - unbind (or we'll start doubling event handlers)
+ $( this.loader ).bind( 'complete', function( event, data ){
+ view.log( view + ' complete', data );
+ $( view.loader ).unbind();
+ });
+
+ // begin loading the data, switch to the chart display tab
+ view.showLoadingIndicator( view.fetchMsg, function(){
+ view.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' );
+ view.loader.load();
+ });
+ },
+
+ renderStats : function(){
+ this.log( this + '.renderStats' );
+ // render the stats table in the stats panel
+ //TODO: there's a better way
+ this.$statsDisplay.html( ScatterplotControlForm.templates.statsDisplay({
+ stats: [
+ { name: 'Count', xval: this.meta[0].count, yval: this.meta[1].count },
+ { name: 'Min', xval: this.meta[0].min, yval: this.meta[1].min },
+ { name: 'Max', xval: this.meta[0].max, yval: this.meta[1].max },
+ { name: 'Sum', xval: this.meta[0].sum, yval: this.meta[1].sum },
+ { name: 'Mean', xval: this.meta[0].mean, yval: this.meta[1].mean },
+ { name: 'Median', xval: this.meta[0].median, yval: this.meta[1].median }
+ ]
+ }));
+ },
+
+ changeChartSettings : function(){
+ // re-render the chart with new chart settings and OLD data
+ var view = this;
+ newChartSettings = this.getChartSettings();
+
+ // update the chart config from the chartSettings panel controls
+ _.extend( this.chartConfig, newChartSettings );
+ this.log( 'this.chartConfig:', this.chartConfig );
+ this.chart.updateConfig( this.chartConfig, false );
+
+ // if there's current data, call chart.render with it (no data fetch)
+ if( view.data && view.meta ){
+ view.showLoadingIndicator( view.renderMsg, function(){
+ view.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' );
+ view.chart.render( view.data, view.meta );
+ view.hideLoadingIndicator();
+ });
+
+ // no current data, call renderChart instead (which will fetch data)
+ } else {
+ this.renderChart();
+ }
+ },
+
+ // ------------------------------------------------------------------------- DATA AGGREGATION
+ postProcessDataFetchResponse : function( response ){
+ // the loader only returns new data - it's up to this to munge the fetches together properly
+ //TODO: we're now storing data in two places: loader and here
+ // can't we reduce incoming data into loader.data[0]? are there concurrency problems?
+ this.postProcessData( response.data );
+ this.postProcessMeta( response.meta );
+ },
+
+ postProcessData : function( newData ){
+ // stack the column data on top of each other into this.data
+ //this.log( this + '.postProcessData:', newData );
+ var view = this;
+
+ // if we already have data: aggregate
+ if( view.data ){
+ _.each( newData, function( newColData, colIndex ){
+ //view.log( colIndex + ' data:', newColData );
+ //TODO??: time, space efficiency of this?
+ view.data[ colIndex ] = view.data[ colIndex ].concat( newColData );
+ });
+
+ // otherwise: assign (first load)
+ } else {
+ view.data = newData;
+ }
+ },
+
+ postProcessMeta : function( newMeta ){
+ // munge the meta data (stats) from the server fetches together
+ //pre: this.data must be preprocessed (needed for medians)
+ //this.log( this + '.postProcessMeta:', newMeta );
+ var view = this,
+ colTypes = this.dataset.metadata_column_types;
+
+ // if we already have meta: aggregate
+ if( view.meta ){
+ _.each( newMeta, function( newColMeta, colIndex ){
+ var colMeta = view.meta[ colIndex ],
+ colType = colTypes[ colIndex ];
+ //view.log( '\t ' + colIndex + ' postprocessing meta:', newColMeta );
+ //view.log( colIndex + ' old meta:',
+ // 'min:', colMeta.min,
+ // 'max:', colMeta.max,
+ // 'sum:', colMeta.sum,
+ // 'mean:', colMeta.mean,
+ // 'median:', colMeta.median
+ //);
+
+ //!TODO: at what point are we getting int/float overflow on these?!
+ //??: need to be null safe?
+ colMeta.count += ( newColMeta.count )?( newColMeta.count ):( 0 );
+ //view.log( colIndex, 'count:', colMeta.count );
+
+ if( ( colType === 'int' ) || ( colType === 'float' ) ){
+ //view.log( colIndex + ' incoming meta:',
+ // 'min:', newColMeta.min,
+ // 'max:', newColMeta.max,
+ // 'sum:', newColMeta.sum,
+ // 'mean:', newColMeta.mean,
+ // 'median:', newColMeta.median
+ //);
+
+ colMeta.min = Math.min( newColMeta.min, colMeta.min );
+ colMeta.max = Math.max( newColMeta.max, colMeta.max );
+ colMeta.sum = newColMeta.sum + colMeta.sum;
+ colMeta.mean = ( colMeta.count )?( colMeta.sum / colMeta.count ):( null );
+
+ // median's a pain bc of sorting (requires the data as well)
+ var sortedCol = view.data[ colIndex ].slice().sort(),
+ middleIndex = Math.floor( sortedCol.length / 2 );
+
+ if( sortedCol.length % 2 === 0 ){
+ colMeta.median = ( ( sortedCol[ middleIndex ] + sortedCol[( middleIndex + 1 )] ) / 2 );
+
+ } else {
+ colMeta.median = sortedCol[ middleIndex ];
+ }
+
+ //view.log( colIndex + ' new meta:',
+ // 'min:', colMeta.min,
+ // 'max:', colMeta.max,
+ // 'sum:', colMeta.sum,
+ // 'mean:', colMeta.mean,
+ // 'median:', colMeta.median
+ //);
+ }
+ });
+
+ // otherwise: assign (first load)
+ } else {
+ view.meta = newMeta;
+ //view.log( '\t meta (first load):', view.meta );
+ }
+ },
+
+ // ------------------------------------------------------------------------- GET DATA/CHART SETTINGS
+ getDataSettings : function(){
+ // parse the column values for both indeces (for the data fetch) and names (for the chart)
+ var columnSelections = this.getColumnSelections(),
+ columns = [];
+ this.log( '\t columnSelections:', columnSelections );
+
+ //TODO: validate columns - minimally: we can assume either set by selectors or via a good query string
+
+ // get column indices for params, include the desired ID column (if any)
+ //NOTE: these are presented in human-readable 1 base index (to match the data.peek) - adjust
+ columns = [
+ columnSelections.X.colIndex - 1,
+ columnSelections.Y.colIndex - 1
+ ];
+ if( this.$dataControl.find( '#include-id-checkbox' ).attr( 'checked' ) ){
+ columns.push( columnSelections.ID.colIndex - 1 );
+ }
+ //TODO: other vals: max, start, page
+
+ var params = {
+ data_type : 'raw_data',
+ provider : 'column_with_stats',
+ columns : '[' + columns + ']'
+ };
+ this.log( '\t data settings (url params):', params );
+ return params;
+ },
+
+ getColumnSelections : function(){
+ // gets the current user-selected values for which columns to fetch from the data settings panel
+ // returns a map: { column-select name (eg. X) : { colIndex : column-selector val,
+ // colName : selected option text }, ... }
+ var selections = {};
+ this.$dataControl.find( 'div.column-select select' ).each( function(){
+ var $this = $( this ),
+ val = $this.val();
+ selections[ $this.attr( 'name' ) ] = {
+ colIndex : val,
+ colName : $this.children( '[value="' + val + '"]' ).text()
+ };
+ });
+ return selections;
+ },
+
+ getChartSettings : function(){
+ // gets the user-selected chartConfig from the chart settings panel
+ var settings = {},
+ colSelections = this.getColumnSelections();
+ //this.log( 'colSelections:', colSelections );
+
+ //TODO: simplify with keys and loop
+ settings.datapointSize = this.$chartControl.find( '#datapointSize.numeric-slider-input' )
+ .find( '.slider' ).slider( 'value' );
+ settings.width = this.$chartControl.find( '#width.numeric-slider-input' )
+ .find( '.slider' ).slider( 'value' );
+ settings.height = this.$chartControl.find( '#height.numeric-slider-input' )
+ .find( '.slider' ).slider( 'value' );
+
+ // update axes labels using chartSettings inputs (if not at defaults), otherwise the selects' colName
+ //TODO: a little confusing
+ var chartSettingsXLabel = this.$chartControl.find( 'input#X-axis-label' ).val(),
+ chartSettingsYLabel = this.$chartControl.find( 'input#Y-axis-label' ).val();
+ settings.xLabel = ( chartSettingsXLabel === 'X' )?
+ ( colSelections.X.colName ):( chartSettingsXLabel );
+ settings.yLabel = ( chartSettingsYLabel === 'Y' )?
+ ( colSelections.Y.colName ):( chartSettingsYLabel );
+
+ settings.animDuration = ( this.$chartControl.find( '#animate-chart' ).is( ':checked' ) )?
+ ( this.chart.defaults.animDuration ):( 0 );
+
+ this.log( '\t chartSettings:', settings );
+ return settings;
+ },
+
+ toString : function(){
+ return 'ScatterplotControlForm(' + (( this.dataset )?( this.dataset.id ):( '' )) + ')';
+ }
+});
+
+ScatterplotControlForm.templates = {
+ mainLayout : Templates.scatterplotControlForm,
+ dataControl : Templates.dataControl,
+ chartControl : Templates.chartControl,
+ statsDisplay : Templates.statsDisplay,
+ chartDisplay : Templates.chartDisplay
+};
+
+//==============================================================================
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/src/visualization-templates.html
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/src/visualization-templates.html
@@ -0,0 +1,182 @@
+<script type="text/template" class="template-visualization" id="template-visualization-scatterplotControlForm">
+{{! main layout }}
+
+<div class="scatterplot-container chart-container tabbable tabs-left">
+ {{! tab buttons/headers using Bootstrap }}
+ <ul class="nav nav-tabs">
+ {{! start with the data controls as the displayed tab }}
+ <li class="active"><a href="#data-control" data-toggle="tab" class="tooltip"
+ title="Use this tab to change which data are used">Data Controls</a></li>
+ <li><a href="#chart-control" data-toggle="tab" class="tooltip"
+ title="Use this tab to change how the chart is drawn">Chart Controls</a></li>
+ <li><a href="#stats-display" data-toggle="tab" class="tooltip"
+ title="This tab will display overall statistics for your data">Statistics</a></li>
+ <li><a href="#chart-display" data-toggle="tab" class="tooltip"
+ title="This tab will display the chart">Chart</a>
+ {{! loading indicator - initially hidden }}
+ <div id="loading-indicator" style="display: none;">
+ <img class="loading-img" src="{{loadingIndicatorImagePath}}" />
+ <span class="loading-message">{{message}}</span>
+ </div>
+ </li>
+ </ul>
+
+ {{! data form, chart config form, stats, and chart all get their own tab }}
+ <div class="tab-content">
+ {{! ---------------------------- tab for data settings form }}
+ <div id="data-control" class="tab-pane active">
+ {{! rendered separately }}
+ </div>
+
+ {{! ---------------------------- tab for chart graphics control form }}
+ <div id="chart-control" class="tab-pane">
+ {{! rendered separately }}
+ </div>
+
+ {{! ---------------------------- tab for data statistics }}
+ <div id="stats-display" class="tab-pane">
+ {{! rendered separately }}
+ </div>
+
+ {{! ---------------------------- tab for actual chart }}
+ <div id="chart-display" class="tab-pane">
+ {{! chart rendered separately }}
+ </div>
+
+ </div>{{! end .tab-content }}
+</div>{{! end .chart-control }}
+</script>
+
+<script type="text/template" class="template-visualization" id="template-visualization-dataControl">
+
+ <p class="help-text">
+ Use the following controls to change the data used by the chart.
+ Use the 'Draw' button to render (or re-render) the chart with the current settings.
+ </p>
+
+ {{! column selector containers }}
+ <div class="column-select">
+ <label for="X-select">Data column for X: </label>
+ <select name="X" id="X-select">
+ {{#each numericColumns}}
+ <option value="{{index}}">{{name}}</option>
+ {{/each}}
+ </select>
+ </div>
+ <div class="column-select">
+ <label for="Y-select">Data column for Y: </label>
+ <select name="Y" id="Y-select">
+ {{#each numericColumns}}
+ <option value="{{index}}">{{name}}</option>
+ {{/each}}
+ </select>
+ </div>
+
+ {{! optional id column }}
+ <div id="include-id">
+ <label for="include-id-checkbox">Include a third column as data point IDs?</label>
+ <input type="checkbox" name="include-id" id="include-id-checkbox" />
+ <p class="help-text-small">
+ These will be displayed (along with the x and y values) when you hover over
+ a data point.
+ </p>
+ </div>
+ <div class="column-select" style="display: none">
+ <label for="ID-select">Data column for IDs: </label>
+ <select name="ID" id="ID-select">
+ {{#each allColumns}}
+ <option value="{{index}}">{{name}}</option>
+ {{/each}}
+ </select>
+ </div>
+
+ {{! if we're using generic column selection names ('column 1') - allow the user to use the first line }}
+ <div id="first-line-header" style="display: none;">
+ <p>Possible headers: {{ possibleHeaders }}
+ </p>
+ <label for="first-line-header-checkbox">Use the above as column headers?</label>
+ <input type="checkbox" name="include-id" id="first-line-header-checkbox"
+ {{#if usePossibleHeaders }}checked="true"{{/if}}/>
+ <p class="help-text-small">
+ It looks like Galaxy couldn't get proper column headers for this data.
+ Would you like to use the column headers above as column names to select columns?
+ </p>
+ </div>
+
+ <input id="render-button" type="button" value="Draw" />
+ <div class="clear"></div>
+</script>
+
+<script type="text/template" class="template-visualization" id="template-visualization-chartControl">
+ <p class="help-text">
+ Use the following controls to how the chart is displayed.
+ The slide controls can be moved by the mouse or, if the 'handle' is in focus, your keyboard's arrow keys.
+ Move the focus between controls by using the tab or shift+tab keys on your keyboard.
+ Use the 'Draw' button to render (or re-render) the chart with the current settings.
+ </p>
+
+ <div id="datapointSize" class="form-input numeric-slider-input">
+ <label for="datapointSize">Size of data point: </label>
+ <div class="slider-output">{{datapointSize}}</div>
+ <div class="slider"></div>
+ <p class="form-help help-text-small">
+ Size of the graphic representation of each data point
+ </p>
+ </div>
+
+ <div id="animDuration" class="form-input checkbox-input">
+ <label for="animate-chart">Animate chart transitions?: </label>
+ <input type="checkbox" id="animate-chart"
+ class="checkbox control"{{#if animDuration}} checked="true"{{/if}} />
+ <p class="form-help help-text-small">
+ Uncheck this to disable the animations used on the chart
+ </p>
+ </div>
+
+ <div id="width" class="form-input numeric-slider-input">
+ <label for="width">Chart width: </label>
+ <div class="slider-output">{{width}}</div>
+ <div class="slider"></div>
+ <p class="form-help help-text-small">
+ (not including chart margins and axes)
+ </p>
+ </div>
+
+ <div id="height" class="form-input numeric-slider-input">
+ <label for="height">Chart height: </label>
+ <div class="slider-output">{{height}}</div>
+ <div class="slider"></div>
+ <p class="form-help help-text-small">
+ (not including chart margins and axes)
+ </p>
+ </div>
+
+ <div id="X-axis-label"class="text-input form-input">
+ <label for="X-axis-label">Re-label the X axis: </label>
+ <input type="text" name="X-axis-label" id="X-axis-label" value="{{xLabel}}" />
+ <p class="form-help help-text-small"></p>
+ </div>
+
+ <div id="Y-axis-label" class="text-input form-input">
+ <label for="Y-axis-label">Re-label the Y axis: </label>
+ <input type="text" name="Y-axis-label" id="Y-axis-label" value="{{yLabel}}" />
+ <p class="form-help help-text-small"></p>
+ </div>
+
+ <input id="render-button" type="button" value="Draw" />
+</script>
+
+<script type="text/template" class="template-visualization" id="template-visualization-statsDisplay">
+ <p class="help-text">By column:</p>
+ <table id="chart-stats-table">
+ <thead><th></th><th>X</th><th>Y</th></thead>
+ {{#each stats}}
+ <tr><td>{{name}}</td><td>{{xval}}</td><td>{{yval}}</td></tr>
+ </tr>
+ {{/each}}
+ </table>
+</script>
+
+<script type="text/template" class="template-visualization" id="template-visualization-chartDisplay">
+ <svg width="{{width}}" height="{{height}}"></svg>
+</script>
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/static/scatterplot.css
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/static/scatterplot.css
@@ -0,0 +1,181 @@
+/*TODO: use/move into base.less*/
+* { margin: 0px; padding: 0px; }
+
+/* -------------------------------------------- general layout */
+div.tab-pane {
+ padding: 8px;
+}
+
+/* -------------------------------------------- header */
+.header {
+ margin-bottom: 8px;
+}
+
+#chart-header {
+ padding : 8px;
+ background-color: #ebd9b2;
+ margin-bottom: 16px;
+ overflow: auto;
+}
+
+#chart-header .subtitle {
+ margin: -4px 0px 0px 4px;
+ padding : 0;
+ color: white;
+ font-size: small;
+}
+
+/* -------------------------------------------- main layout */
+#scatterplot {
+ /*from width + margin of chart?*/
+}
+
+.scatterplot-container .tab-pane {
+}
+
+/* -------------------------------------------- all controls */
+
+#scatterplot input[type=button],
+#scatterplot select {
+ width: 100%;
+ max-width: 256px;
+ margin-bottom: 8px;
+}
+
+#scatterplot .help-text,
+#scatterplot .help-text-small {
+ color: grey;
+}
+
+#scatterplot .help-text {
+ padding-bottom: 16px;
+}
+
+#scatterplot .help-text-small {
+ padding: 4px;
+ font-size: smaller;
+}
+
+#scatterplot > * {
+}
+
+#scatterplot input[value=Draw] {
+ display: block;
+ margin-top: 16px;
+}
+
+#scatterplot .numeric-slider-input {
+ max-width: 70%;
+}
+
+/* -------------------------------------------- data controls */
+
+/* -------------------------------------------- chart controls */
+#chart-control .form-input {
+ /*display: table-row;*/
+}
+
+#chart-control label {
+ /*text-align: right;*/
+ margin-bottom: 8px;
+ /*display: table-cell;*/
+}
+
+#chart-control .slider {
+ /*display: table-cell;*/
+ height: 8px;
+ display: block;
+ margin: 8px 0px 0px 8px;
+}
+
+#chart-control .slider-output {
+ /*display: table-cell;*/
+ float: right;
+}
+
+#chart-control input[type="text"] {
+ border: 1px solid lightgrey;
+}
+
+
+/* -------------------------------------------- statistics */
+#stats-display table#chart-stats-table {
+ width: 100%;
+}
+
+#stats-display #chart-stats-table th {
+ width: 30%;
+ padding: 4px;
+ font-weight: bold;
+ color: grey;
+}
+
+#stats-display #chart-stats-table td {
+ border: solid lightgrey;
+ border-width: 1px 0px 0px 1px;
+ padding: 4px;
+}
+
+#stats-display #chart-stats-table td:nth-child(1) {
+ border-width: 1px 0px 0px 0px;
+ padding-right: 1em;
+ text-align: right;
+ font-weight: bold;
+ color: grey;
+}
+
+/* -------------------------------------------- load indicators */
+#loading-indicator {
+ margin: 12px 0px 0px 8px;
+}
+
+#scatterplot #loading-indicator .loading-message {
+ font-style: italic;
+ font-size: smaller;
+ color: grey;
+}
+
+/* -------------------------------------------- chart area */
+#chart-holder {
+ overflow: auto;
+ margin-left: 8px;
+}
+
+svg .grid-line {
+ fill: none;
+ stroke: lightgrey;
+ stroke-opacity: 0.5;
+ shape-rendering: crispEdges;
+ stroke-dasharray: 3, 3;
+}
+
+svg .axis path, svg .axis line {
+ fill: none;
+ stroke: black;
+ shape-rendering: crispEdges;
+}
+
+svg .axis text {
+ font-family: monospace;
+ font-size: 12px;
+}
+
+svg #x-axis-label, svg #y-axis-label {
+ font-family: sans-serif;
+ font-size: 10px;
+}
+
+svg .glyph {
+ stroke: none;
+ fill: black;
+ fill-opacity: 0.2;
+}
+
+/* -------------------------------------------- info box */
+.chart-info-box {
+ border-radius: 4px;
+ padding: 4px;
+ background-color: white;
+ border: 1px solid black;
+}
+
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/static/scatterplot.js
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/static/scatterplot.js
@@ -0,0 +1,1 @@
+function TwoVarScatterplot(a){var b=10,c=7,d=10,e=8,f=5;this.log=function(){if(this.debugging&&console&&console.debug){var a=Array.prototype.slice.call(arguments);a.unshift(this.toString()),console.debug.apply(console,a)}},this.log("new TwoVarScatterplot:",a),this.defaults={id:"TwoVarScatterplot",containerSelector:"body",maxDataPoints:3e4,datapointSize:4,animDuration:500,xNumTicks:10,yNumTicks:10,xAxisLabelBumpY:40,yAxisLabelBumpX:-40,width:400,height:400,marginTop:50,marginRight:50,marginBottom:50,marginLeft:50,xMin:null,xMax:null,yMin:null,yMax:null,xLabel:"X",yLabel:"Y"},this.config=_.extend({},this.defaults,a),this.log("intial config:",this.config),this.updateConfig=function(a){_.extend(this.config,a),this.log(this+".updateConfig:",this.config)},this.toString=function(){return this.config.id},this.translateStr=function(a,b){return"translate("+a+","+b+")"},this.rotateStr=function(a,b,c){return"rotate("+a+","+b+","+c+")"},this.adjustChartDimensions=function(a,b,c,d){a=a||0,b=b||0,c=c||0,d=d||0,this.svg.attr("width",this.config.width+(this.config.marginRight+b)+(this.config.marginLeft+d)).attr("height",this.config.height+(this.config.marginTop+a)+(this.config.marginBottom+c)).style("display","block"),this.content=this.svg.select("g.content").attr("transform",this.translateStr(this.config.marginLeft+d,this.config.marginTop+a))},this.preprocessData=function(a){return a.length>this.config.maxDataPoints?a.slice(0,this.config.maxDataPoints):a},this.findMinMaxes=function(a,b,c){this.xMin=this.config.xMin||c?c[0].min:d3.min(a),this.xMax=this.config.xMax||c?c[0].max:d3.max(a),this.yMin=this.config.yMin||c?c[1].min:d3.min(b),this.yMax=this.config.yMax||c?c[1].max:d3.max(b)},this.setUpScales=function(){this.xScale=d3.scale.linear().domain([this.xMin,this.xMax]).range([0,this.config.width]),this.yScale=d3.scale.linear().domain([this.yMin,this.yMax]).range([this.config.height,0])},this.setUpXAxis=function(){this.xAxisFn=d3.svg.axis().scale(this.xScale).ticks(this.config.xNumTicks).orient("bottom"),this.xAxis.attr("transform",this.translateStr(0,this.config.height)).call(this.xAxisFn);var a=d3.max(_.map([this.xMin,this.xMax],function(a){return String(a).length}));a>=f&&this.xAxis.selectAll("g").filter(":nth-child(odd)").style("display","none"),this.log("this.config.xLabel:",this.config.xLabel),this.xAxisLabel.attr("x",this.config.width/2).attr("y",this.config.xAxisLabelBumpY).attr("text-anchor","middle").text(this.config.xLabel),this.log("xAxisLabel:",this.xAxisLabel)},this.setUpYAxis=function(){this.yAxisFn=d3.svg.axis().scale(this.yScale).ticks(this.config.yNumTicks).orient("left"),this.yAxis.call(this.yAxisFn);var a=this.yAxis.selectAll("text").filter(function(a,b){return 0!==b});this.log("yTickLabels:",a),this.yLongestLabel=d3.max(a[0].map(function(a){return d3.select(a).text().length}))||0;var f=b+this.yLongestLabel*c+e+d;if(this.config.yAxisLabelBumpX=-(f-d),this.config.marginLeft<f){var g=f-this.config.marginLeft;g=0>g?0:g,this.adjustChartDimensions(0,0,0,g)}this.yAxisLabel.attr("x",this.config.yAxisLabelBumpX).attr("y",this.config.height/2).attr("text-anchor","middle").attr("transform",this.rotateStr(-90,this.config.yAxisLabelBumpX,this.config.height/2)).text(this.config.yLabel)},this.renderGrid=function(){this.vGridLines=this.content.selectAll("line.v-grid-line").data(this.xScale.ticks(this.xAxisFn.ticks()[0])),this.vGridLines.enter().append("svg:line").classed("grid-line v-grid-line",!0),this.vGridLines.attr("x1",this.xScale).attr("y1",0).attr("x2",this.xScale).attr("y2",this.config.height),this.vGridLines.exit().remove(),this.hGridLines=this.content.selectAll("line.h-grid-line").data(this.yScale.ticks(this.yAxisFn.ticks()[0])),this.hGridLines.enter().append("svg:line").classed("grid-line h-grid-line",!0),this.hGridLines.attr("x1",0).attr("y1",this.yScale).attr("x2",this.config.width).attr("y2",this.yScale),this.hGridLines.exit().remove()},this.renderDatapoints=function(a,b,c){this.log(this+".renderDatapoints",arguments);var d=0,e=this,f=function(b,c){return e.xScale(a[c])},g=function(a,c){return e.yScale(b[c])},h=this.content.selectAll(".glyph").data(a);d=0,h.enter().append("svg:circle").each(function(){d+=1}).classed("glyph",!0).attr("cx",0).attr("cy",this.config.height).attr("r",0),this.log(d," new glyphs created"),d=0,h.transition().duration(this.config.animDuration).each(function(){d+=1}).attr("cx",f).attr("cy",g).attr("r",e.config.datapointSize),this.log(d," existing glyphs transitioned"),h.exit().each(function(){d+=1}).transition().duration(this.config.animDuration).attr("cy",this.config.height).attr("r",0).remove(),this.log(d," glyphs removed"),this._addDatapointEventhandlers(h,a,b,c)},this._addDatapointEventhandlers=function(a,b,c,d){var e=this;a.on("mouseover",function(a,f){var g=d3.select(this);g.style("fill","red").style("fill-opacity",1),e.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",g.attr("cx")-e.config.datapointSize).attr("y1",g.attr("cy")).attr("x2",0).attr("y2",g.attr("cy")).classed("hoverline",!0),g.attr("cy")<e.config.height&&e.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",g.attr("cx")).attr("y1",g.attr("cy")+e.config.datapointSize).attr("x2",g.attr("cx")).attr("y2",e.config.height).classed("hoverline",!0);var h=$(this).offset();e.datapointInfoBox=e.infoBox(h.top,h.left,e.infoHtml(b[f],c[f],d?d[f]:void 0)),$("body").append(e.datapointInfoBox)}).on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",.2),e.content.selectAll(".hoverline").remove(),e.datapointInfoBox&&e.datapointInfoBox.remove()})},this.render=function(a,b){this.log(this+".render",arguments),this.log(" config:",this.config);var c=a[0],d=a[1],e=a.length>2?a[2]:void 0;c=this.preprocessData(c),d=this.preprocessData(d),this.log("xCol len",c.length,"yCol len",d.length),this.findMinMaxes(c,d,b),this.setUpScales(),this.svg||(this.svg=d3.select("svg").attr("class","chart")),this.content||(this.content=this.svg.append("svg:g").attr("class","content").attr("id",this.config.id)),this.adjustChartDimensions(),this.xAxis||(this.xAxis=this.content.append("g").attr("class","axis").attr("id","x-axis")),this.xAxisLabel||(this.xAxisLabel=this.xAxis.append("text").attr("class","axis-label").attr("id","x-axis-label")),this.yAxis||(this.yAxis=this.content.append("g").attr("class","axis").attr("id","y-axis")),this.yAxisLabel||(this.yAxisLabel=this.yAxis.append("text").attr("class","axis-label").attr("id","y-axis-label")),this.setUpXAxis(),this.setUpYAxis(),this.renderGrid(),this.renderDatapoints(c,d,e)},this.infoHtml=function(a,b,c){var d=$("<div/>");return c&&$("<div/>").text(c).css("font-weight","bold").appendTo(d),$("<div/>").text(a).appendTo(d),$("<div/>").text(b).appendTo(d),d.html()},this.infoBox=function(a,b,c,d,e){d=d||0,e=e||20;var f=$("<div />").addClass("chart-info-box").css({position:"absolute",top:a+d,left:b+e});return f.html(c),f}}this.Templates=this.Templates||{},this.Templates.chartControl=Handlebars.template(function(a,b,c,d,e){function f(){return' checked="true"'}this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var g,h="",i="function",j=this.escapeExpression,k=this;return h+='<p class="help-text">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n </p>\n\n <div id="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">',(g=c.datapointSize)?g=g.call(b,{hash:{},data:e}):(g=b.datapointSize,g=typeof g===i?g.apply(b):g),h+=j(g)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id="animDuration" class="form-input checkbox-input">\n <label for="animate-chart">Animate chart transitions?: </label>\n <input type="checkbox" id="animate-chart"\n class="checkbox control"',g=c["if"].call(b,b.animDuration,{hash:{},inverse:k.noop,fn:k.program(1,f,e),data:e}),(g||0===g)&&(h+=g),h+=' />\n <p class="form-help help-text-small">\n Uncheck this to disable the animations used on the chart\n </p>\n </div>\n\n <div id="width" class="form-input numeric-slider-input">\n <label for="width">Chart width: </label>\n <div class="slider-output">',(g=c.width)?g=g.call(b,{hash:{},data:e}):(g=b.width,g=typeof g===i?g.apply(b):g),h+=j(g)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n </div>\n\n <div id="height" class="form-input numeric-slider-input">\n <label for="height">Chart height: </label>\n <div class="slider-output">',(g=c.height)?g=g.call(b,{hash:{},data:e}):(g=b.height,g=typeof g===i?g.apply(b):g),h+=j(g)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n </div>\n\n <div id="X-axis-label"class="text-input form-input">\n <label for="X-axis-label">Re-label the X axis: </label>\n <input type="text" name="X-axis-label" id="X-axis-label" value="',(g=c.xLabel)?g=g.call(b,{hash:{},data:e}):(g=b.xLabel,g=typeof g===i?g.apply(b):g),h+=j(g)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <div id="Y-axis-label" class="text-input form-input">\n <label for="Y-axis-label">Re-label the Y axis: </label>\n <input type="text" name="Y-axis-label" id="Y-axis-label" value="',(g=c.yLabel)?g=g.call(b,{hash:{},data:e}):(g=b.yLabel,g=typeof g===i?g.apply(b):g),h+=j(g)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <input id="render-button" type="button" value="Draw" />'}),this.Templates.chartDisplay=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function",i=this.escapeExpression;return g+='<svg width="',(f=c.width)?f=f.call(b,{hash:{},data:e}):(f=b.width,f=typeof f===h?f.apply(b):f),g+=i(f)+'" height="',(f=c.height)?f=f.call(b,{hash:{},data:e}):(f=b.height,f=typeof f===h?f.apply(b):f),g+=i(f)+'"></svg>'}),this.Templates.dataControl=Handlebars.template(function(a,b,c,d,e){function f(a,b){var d,e="";return e+='\n <option value="',(d=c.index)?d=d.call(a,{hash:{},data:b}):(d=a.index,d=typeof d===j?d.apply(a):d),e+=k(d)+'">',(d=c.name)?d=d.call(a,{hash:{},data:b}):(d=a.name,d=typeof d===j?d.apply(a):d),e+=k(d)+"</option>\n "}function g(){return'checked="true"'}this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var h,i="",j="function",k=this.escapeExpression,l=this;return i+='<p class="help-text">\n Use the following controls to change the data used by the chart.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n </p>\n\n \n <div class="column-select">\n <label for="X-select">Data column for X: </label>\n <select name="X" id="X-select">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n </div>\n <div class="column-select">\n <label for="Y-select">Data column for Y: </label>\n <select name="Y" id="Y-select">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n </div>\n\n \n <div id="include-id">\n <label for="include-id-checkbox">Include a third column as data point IDs?</label>\n <input type="checkbox" name="include-id" id="include-id-checkbox" />\n <p class="help-text-small">\n These will be displayed (along with the x and y values) when you hover over\n a data point.\n </p>\n </div>\n <div class="column-select" style="display: none">\n <label for="ID-select">Data column for IDs: </label>\n <select name="ID" id="ID-select">\n ',h=c.each.call(b,b.allColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n </div>\n\n \n <div id="first-line-header" style="display: none;">\n <p>Possible headers: ',(h=c.possibleHeaders)?h=h.call(b,{hash:{},data:e}):(h=b.possibleHeaders,h=typeof h===j?h.apply(b):h),i+=k(h)+'\n </p>\n <label for="first-line-header-checkbox">Use the above as column headers?</label>\n <input type="checkbox" name="include-id" id="first-line-header-checkbox"\n ',h=c["if"].call(b,b.usePossibleHeaders,{hash:{},inverse:l.noop,fn:l.program(3,g,e),data:e}),(h||0===h)&&(i+=h),i+='/>\n <p class="help-text-small">\n It looks like Galaxy couldn\'t get proper column headers for this data.\n Would you like to use the column headers above as column names to select columns?\n </p>\n </div>\n\n <input id="render-button" type="button" value="Draw" />\n <div class="clear"></div>'}),this.Templates.scatterplotControlForm=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function",i=this.escapeExpression;return g+='\n\n<div class="scatterplot-container chart-container tabbable tabs-left">\n \n <ul class="nav nav-tabs">\n \n <li class="active"><a href="#data-control" data-toggle="tab" class="tooltip"\n title="Use this tab to change which data are used">Data Controls</a></li>\n <li><a href="#chart-control" data-toggle="tab" class="tooltip"\n title="Use this tab to change how the chart is drawn">Chart Controls</a></li>\n <li><a href="#stats-display" data-toggle="tab" class="tooltip"\n title="This tab will display overall statistics for your data">Statistics</a></li>\n <li><a href="#chart-display" data-toggle="tab" class="tooltip"\n title="This tab will display the chart">Chart</a>\n \n <div id="loading-indicator" style="display: none;">\n <img class="loading-img" src="',(f=c.loadingIndicatorImagePath)?f=f.call(b,{hash:{},data:e}):(f=b.loadingIndicatorImagePath,f=typeof f===h?f.apply(b):f),g+=i(f)+'" />\n <span class="loading-message">',(f=c.message)?f=f.call(b,{hash:{},data:e}):(f=b.message,f=typeof f===h?f.apply(b):f),g+=i(f)+"</span>\n </div>\n </li>\n </ul>\n\n "+'\n <div class="tab-content">\n '+'\n <div id="data-control" class="tab-pane active">\n '+"\n </div>\n \n "+'\n <div id="chart-control" class="tab-pane">\n '+"\n </div>\n\n "+'\n <div id="stats-display" class="tab-pane">\n '+"\n </div>\n\n "+'\n <div id="chart-display" class="tab-pane">\n '+"\n </div>\n\n </div>"+"\n</div>"}),this.Templates.statsDisplay=Handlebars.template(function(a,b,c,d,e){function f(a,b){var d,e="";return e+="\n <tr><td>",(d=c.name)?d=d.call(a,{hash:{},data:b}):(d=a.name,d=typeof d===i?d.apply(a):d),e+=j(d)+"</td><td>",(d=c.xval)?d=d.call(a,{hash:{},data:b}):(d=a.xval,d=typeof d===i?d.apply(a):d),e+=j(d)+"</td><td>",(d=c.yval)?d=d.call(a,{hash:{},data:b}):(d=a.yval,d=typeof d===i?d.apply(a):d),e+=j(d)+"</td></tr>\n </tr>\n "}this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var g,h="",i="function",j=this.escapeExpression,k=this;return h+='<p class="help-text">By column:</p>\n <table id="chart-stats-table">\n <thead><th></th><th>X</th><th>Y</th></thead>\n ',g=c.each.call(b,b.stats,{hash:{},inverse:k.noop,fn:k.program(1,f,e),data:e}),(g||0===g)&&(h+=g),h+="\n </table>"});var ScatterplotControlForm=BaseView.extend(LoggableMixin).extend({className:"scatterplot-control-form",dataLoadDelay:4e3,dataLoadSize:5e3,loadingIndicatorImage:"loading_small_white_bg.gif",fetchMsg:"Fetching data...",renderMsg:"Rendering...",initialize:function(a){this.log(this+".initialize, attributes:",a),this.dataset=null,this.chartConfig=null,this.chart=null,this.loader=null,this.$dataControl=null,this.$chartControl=null,this.$statsDisplay=null,this.$chartDisplay=null,this.dataFetch=null,this.initializeFromAttributes(a),this.initializeChart(a),this.initializeDataLoader(a)},initializeFromAttributes:function(a){if(!a||!a.dataset)throw"ScatterplotView requires a dataset";if(this.dataset=a.dataset,"string"===jQuery.type(this.dataset.metadata_column_types)&&(this.dataset.metadata_column_types=this.dataset.metadata_column_types.split(", ")),this.log(" dataset:",this.dataset),this.dataset.comment_lines&&this.dataset.comment_lines.length){var b=this.dataset.comment_lines[0],c=b.split(" ");c.length===this.dataset.metadata_column_types.length&&(this.possibleHeaders=c)}if(!a.apiDatasetsURL)throw"ScatterplotView requires a apiDatasetsURL";this.dataURL=a.apiDatasetsURL+"/"+this.dataset.id+"?",this.log(" dataURL:",this.dataURL)},initializeChart:function(a){this.chartConfig=a.chartConfig||{},this.log(" initial chartConfig:",this.chartConfig),this.chart=new TwoVarScatterplot(this.chartConfig),this.chartConfig=this.chart.config},initializeDataLoader:function(a){var b=this;this.loader=new LazyDataLoader({url:null,start:a.start||0,total:a.total||this.dataset.metadata_data_lines,delay:this.dataLoadDelay,size:this.dataLoadSize,buildUrl:function(a,b){return this.url+"&"+jQuery.param({start_val:a,max_vals:b})}}),$(this.loader).bind("error",function(a,c,d){b.log("ERROR:",c,d),alert("ERROR fetching data:\n"+c+"\n"+d),b.hideLoadingIndicator()})},render:function(){return this.log(this+".render"),this.$el.append(ScatterplotControlForm.templates.mainLayout({loadingIndicatorImagePath:"/static/images/"+this.loadingIndicatorImage,message:""})),this.$dataControl=this._render_dataControl(),this.$chartControl=this._render_chartControl(),this.$statsDisplay=this.$el.find(".tab-pane#stats-display"),this.$chartDisplay=this._render_chartDisplay(),this.chartConfig.xColumn&&this.chartConfig.yColumn&&this.renderChart(),this.$el.find(".tooltip").tooltip(),this},_render_dataControl:function(){var a=this,b=[],c=[],d=this.possibleHeaders&&this.$dataControl?this.$dataControl.find("#first-line-header-checkbox").is(":checked"):!1;_.each(this.dataset.metadata_column_types,function(e,f){var g=f+1,h="column "+g;a.dataset.metadata_column_names?h=a.dataset.metadata_column_names[f]:d&&(h=a.possibleHeaders[f]),b.push({index:g,name:h}),("int"===e||"float"===e)&&c.push({index:g,name:h})});var e=this.$el.find(".tab-pane#data-control");return e.html(ScatterplotControlForm.templates.dataControl({allColumns:b,numericColumns:c,possibleHeaders:this.possibleHeaders?this.possibleHeaders.join(", "):"",usePossibleHeaders:d})),!this.dataset.metadata_column_names&&this.possibleHeaders&&e.find("#first-line-header").show(),e.find("#X-select").val(this.chartConfig.xColumn),e.find("#Y-select").val(this.chartConfig.yColumn),void 0!==this.chartConfig.idColumn&&(e.find("#include-id-checkbox").attr("checked",!0).trigger("change"),e.find("#ID-select").val(this.chartConfig.idColumn)),e},_render_chartControl:function(){var a=this,b=this.$el.find(".tab-pane#chart-control"),c={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};return b.append(ScatterplotControlForm.templates.chartControl(this.chartConfig)),b.find(".numeric-slider-input").each(function(){function b(){var a=$(this),b=a.slider("value");e.text(b)}var d=$(this),e=d.find(".slider-output"),f=d.find(".slider"),g=d.attr("id");f.slider(_.extend(c[g],{value:a.chartConfig[g],change:b,slide:b}))}),b},_render_chartDisplay:function(){var a=this.$el.find(".tab-pane#chart-display");return a.append(ScatterplotControlForm.templates.chartDisplay(this.chartConfig)),a},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","change #first-line-header-checkbox":"rerenderDataControl","click #data-control #render-button":"renderChart","click #chart-control #render-button":"changeChartSettings"},toggleThirdColumnSelector:function(){this.$el.find('select[name="ID"]').parent().toggle()},rerenderDataControl:function(){this.$dataControl=this._render_dataControl()},showLoadingIndicator:function(a,b){a=a||"";var c=this.$el.find("div#loading-indicator");messageBox=c.find(".loading-message"),c.is(":visible")?a?messageBox.fadeOut("fast",function(){messageBox.text(a),messageBox.fadeIn("fast",b)}):b():(a&&messageBox.text(a),c.fadeIn("fast",b))},hideLoadingIndicator:function(a){this.$el.find("div#loading-indicator").fadeOut("fast",a)},renderChart:function(){this.log(this+".renderChart"),this.data=null,this.meta=null,_.extend(this.chartConfig,this.getChartSettings()),this.log(" chartConfig:",this.chartConfig),this.chart.updateConfig(this.chartConfig,!1),this.loader.url=this.dataURL+"&"+jQuery.param(this.getDataSettings()),this.log(" loader: total lines:",this.loader.total," url:",this.loader.url);var a=this;$(this.loader).bind("loaded.new",function(b,c){a.log(a+" loaded.new",c),a.postProcessDataFetchResponse(c),a.log(" postprocessed data:",a.data),a.log(" postprocessed meta:",a.meta),a.showLoadingIndicator(a.renderMsg,function(){a.chart.render(a.data,a.meta),a.renderStats(a.data,a.meta),a.hideLoadingIndicator()})}),$(this.loader).bind("complete",function(b,c){a.log(a+" complete",c),$(a.loader).unbind()}),a.showLoadingIndicator(a.fetchMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show"),a.loader.load()})},renderStats:function(){this.log(this+".renderStats"),this.$statsDisplay.html(ScatterplotControlForm.templates.statsDisplay({stats:[{name:"Count",xval:this.meta[0].count,yval:this.meta[1].count},{name:"Min",xval:this.meta[0].min,yval:this.meta[1].min},{name:"Max",xval:this.meta[0].max,yval:this.meta[1].max},{name:"Sum",xval:this.meta[0].sum,yval:this.meta[1].sum},{name:"Mean",xval:this.meta[0].mean,yval:this.meta[1].mean},{name:"Median",xval:this.meta[0].median,yval:this.meta[1].median}]}))},changeChartSettings:function(){var a=this;newChartSettings=this.getChartSettings(),_.extend(this.chartConfig,newChartSettings),this.log("this.chartConfig:",this.chartConfig),this.chart.updateConfig(this.chartConfig,!1),a.data&&a.meta?a.showLoadingIndicator(a.renderMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show"),a.chart.render(a.data,a.meta),a.hideLoadingIndicator()}):this.renderChart()},postProcessDataFetchResponse:function(a){this.postProcessData(a.data),this.postProcessMeta(a.meta)},postProcessData:function(a){var b=this;b.data?_.each(a,function(a,c){b.data[c]=b.data[c].concat(a)}):b.data=a},postProcessMeta:function(a){var b=this,c=this.dataset.metadata_column_types;b.meta?_.each(a,function(a,d){var e=b.meta[d],f=c[d];if(e.count+=a.count?a.count:0,"int"===f||"float"===f){e.min=Math.min(a.min,e.min),e.max=Math.max(a.max,e.max),e.sum=a.sum+e.sum,e.mean=e.count?e.sum/e.count:null;var g=b.data[d].slice().sort(),h=Math.floor(g.length/2);e.median=0===g.length%2?(g[h]+g[h+1])/2:g[h]}}):b.meta=a},getDataSettings:function(){var a=this.getColumnSelections(),b=[];this.log(" columnSelections:",a),b=[a.X.colIndex-1,a.Y.colIndex-1],this.$dataControl.find("#include-id-checkbox").attr("checked")&&b.push(a.ID.colIndex-1);var c={data_type:"raw_data",provider:"column_with_stats",columns:"["+b+"]"};return this.log(" data settings (url params):",c),c},getColumnSelections:function(){var a={};return this.$dataControl.find("div.column-select select").each(function(){var b=$(this),c=b.val();a[b.attr("name")]={colIndex:c,colName:b.children('[value="'+c+'"]').text()}}),a},getChartSettings:function(){var a={},b=this.getColumnSelections();a.datapointSize=this.$chartControl.find("#datapointSize.numeric-slider-input").find(".slider").slider("value"),a.width=this.$chartControl.find("#width.numeric-slider-input").find(".slider").slider("value"),a.height=this.$chartControl.find("#height.numeric-slider-input").find(".slider").slider("value");var c=this.$chartControl.find("input#X-axis-label").val(),d=this.$chartControl.find("input#Y-axis-label").val();return a.xLabel="X"===c?b.X.colName:c,a.yLabel="Y"===d?b.Y.colName:d,a.animDuration=this.$chartControl.find("#animate-chart").is(":checked")?this.chart.defaults.animDuration:0,this.log(" chartSettings:",a),a},toString:function(){return"ScatterplotControlForm("+(this.dataset?this.dataset.id:"")+")"}});ScatterplotControlForm.templates={mainLayout:Templates.scatterplotControlForm,dataControl:Templates.dataControl,chartControl:Templates.chartControl,statsDisplay:Templates.statsDisplay,chartDisplay:Templates.chartDisplay};
\ No newline at end of file
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/scatterplot/templates/scatterplot.mako
--- /dev/null
+++ b/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>${hda.name} | ${visualization_name}</title>
+
+## ----------------------------------------------------------------------------
+<link type="text/css" rel="Stylesheet" media="screen" href="/static/style/base.css">
+<link type="text/css" rel="Stylesheet" media="screen" href="/static/style/jquery-ui/smoothness/jquery-ui.css">
+
+<link type="text/css" rel="Stylesheet" media="screen" href="/plugins/visualizations/scatterplot/static/scatterplot.css">
+
+## ----------------------------------------------------------------------------
+<script type="text/javascript" src="/static/scripts/libs/jquery/jquery.js"></script>
+<script type="text/javascript" src="/static/scripts/libs/jquery/jquery.migrate.js"></script>
+<script type="text/javascript" src="/static/scripts/libs/underscore.js"></script>
+<script type="text/javascript" src="/static/scripts/libs/backbone/backbone.js"></script>
+<script type="text/javascript" src="/static/scripts/libs/backbone/backbone-relational.js"></script>
+<script type="text/javascript" src="/static/scripts/libs/handlebars.runtime.js"></script>
+<script type="text/javascript" src="/static/scripts/libs/d3.js"></script>
+<script type="text/javascript" src="/static/scripts/libs/bootstrap.js"></script>
+<script type="text/javascript" src="/static/scripts/libs/jquery/jquery-ui.js"></script>
+<script type="text/javascript" src="/static/scripts/utils/LazyDataLoader.js"></script>
+<script type="text/javascript" src="/static/scripts/mvc/base-mvc.js"></script>
+
+<script type="text/javascript" src="/plugins/visualizations/scatterplot/static/scatterplot.js"></script>
+
+</head>
+
+## ----------------------------------------------------------------------------
+<body>
+%if not embedded:
+## dataset info: only show if on own page
+<div id="chart-header" class="header">
+ <h2 class="title">Scatterplot of '${hda.name}'</h2>
+ <p class="subtitle">${hda.info}</p>
+</div>
+%endif
+
+<div id="scatterplot" class="scatterplot-control-form"></div>
+
+<script type="text/javascript">
+$(function(){
+ var hda = ${h.to_json_string( trans.security.encode_dict_ids( hda.get_api_value() ) )},
+ querySettings = ${h.to_json_string( query_args )},
+ chartConfig = _.extend( querySettings, {
+ containerSelector : '#chart',
+ //TODO: move to ScatterplotControlForm.initialize
+ marginTop : ( querySettings.marginTop > 20 )?( querySettings.marginTop ):( 20 ),
+
+ xColumn : querySettings.xColumn,
+ yColumn : querySettings.yColumn,
+ idColumn : querySettings.idColumn
+ });
+ //console.debug( querySettings );
+
+ var settingsForm = new ScatterplotControlForm({
+ dataset : hda,
+ apiDatasetsURL : "${h.url_for( controller='/api/datasets', action='index' )}",
+ el : $( '#scatterplot' ),
+ chartConfig : chartConfig
+ }).render();
+
+});
+</script>
+
+</body>
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/sweepster/config/sweepster.xml
--- /dev/null
+++ b/config/plugins/visualizations/sweepster/config/sweepster.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
+<visualization name="sweepster">
+ <data_sources>
+ <data_source>
+ <model_class>HistoryDatasetAssociation</model_class>
+ <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
+ <to_param param_attr="id">dataset_id</to_param>
+ <to_param assign="hda">hda_ldda</to_param>
+ </data_source>
+ <data_source>
+ <model_class>LibraryDatasetDatasetAssociation</model_class>
+ <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
+ <to_param param_attr="id">dataset_id</to_param>
+ <to_param assign="ldda">hda_ldda</to_param>
+ </data_source>
+ </data_sources>
+ <params>
+ <param type="visualization" var_name_in_template="viz">visualization</param>
+ <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_location>_top</render_location>
+</visualization>
diff -r b076688903e1f96fb0453c61d0ea38b5d92cd89c -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 config/plugins/visualizations/trackster/config/trackster.xml
--- /dev/null
+++ b/config/plugins/visualizations/trackster/config/trackster.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
+<visualization name="trackster">
+ <!--not tested yet -->
+ <data_sources>
+ <data_source>
+ <model_class>HistoryDatasetAssociation</model_class>
+ <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
+ <to_param param_attr="id">dataset_id</to_param>
+ <to_param assign="hda">hda_ldda</to_param>
+ <to_param param_attr="dbkey">dbkey</to_param>
+ </data_source>
+ <data_source>
+ <model_class>LibraryDatasetDatasetAssociation</model_class>
+ <test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
+ <to_param param_attr="id">dataset_id</to_param>
+ <to_param assign="ldda">hda_ldda</to_param>
+ </data_source>
+ </data_sources>
+ <params>
+ <param type="visualization">id</param>
+ <param type="dataset">dataset_id</param>
+ <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_location>_top</render_location>
+</visualization>
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/31092efd9e1e/
Changeset: 31092efd9e1e
User: ross...(a)gmail.com
Date: 2013-08-12 05:04:57
Summary: tweaks. ready for branch merge
Affected #: 2 files
diff -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 -r 31092efd9e1e5d80adb8c968d34f4c04a3a487d1 tools/rgenetics/htseqsams2mx.xml
--- a/tools/rgenetics/htseqsams2mx.xml
+++ b/tools/rgenetics/htseqsams2mx.xml
@@ -1,5 +1,5 @@
-<tool id="htseqsams2mx" name="SAM/BAM to count matrix" version="0.4">
- <description>using HTSeq code</description>
+<tool id="htseqsams2mxlocal" name="SAM/BAM to count matrix" version="0.4">
+ <description>using HTSeq code locally</description><stdio><exit_code range="666" level="warning"
description="Exit code 666 encountered" />
diff -r c7d0b32602fb1f3ed4290359212ca24ea3bd1931 -r 31092efd9e1e5d80adb8c968d34f4c04a3a487d1 tools/sr_mapping/bowtie2_wrapper.xml
--- a/tools/sr_mapping/bowtie2_wrapper.xml
+++ b/tools/sr_mapping/bowtie2_wrapper.xml
@@ -233,7 +233,7 @@
</action></actions></data>
- <data format="bam" name="output" label="${tool.name} on ${on_string}_${jobtitle}_aligned.bam">
+ <data format="bam" name="output" label="${on_string}_${jobtitle}_aligned.bam"><actions><conditional name="reference_genome.source"><when value="indexed">
https://bitbucket.org/galaxy/galaxy-central/commits/f830e3d7c6c0/
Changeset: f830e3d7c6c0
User: ross...(a)gmail.com
Date: 2013-08-12 05:07:48
Summary: branch merge
Affected #: 198 files
diff -r 009088d5e76fb00794da78dc0ee3cdaa8524b7d8 -r f830e3d7c6c096a9a840dad523540bdcc84327b2 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -109,6 +109,7 @@
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
self.allow_user_deletion = string_as_bool( kwargs.get( "allow_user_deletion", "False" ) )
self.allow_user_dataset_purge = string_as_bool( kwargs.get( "allow_user_dataset_purge", "False" ) )
+ self.use_data_id_on_string = string_as_bool( kwargs.get( "use_data_id_on_string", "False" ) )
self.allow_user_impersonation = string_as_bool( kwargs.get( "allow_user_impersonation", "False" ) )
self.new_user_dataset_access_role_default_private = string_as_bool( kwargs.get( "new_user_dataset_access_role_default_private", "False" ) )
self.collect_outputs_from = [ x.strip() for x in kwargs.get( 'collect_outputs_from', 'new_file_path,job_working_directory' ).lower().split(',') ]
diff -r 009088d5e76fb00794da78dc0ee3cdaa8524b7d8 -r f830e3d7c6c096a9a840dad523540bdcc84327b2 lib/galaxy/tools/actions/__init__.py
--- a/lib/galaxy/tools/actions/__init__.py
+++ b/lib/galaxy/tools/actions/__init__.py
@@ -1,5 +1,6 @@
import os
import galaxy.tools
+import re
from galaxy.exceptions import ObjectInvalid
from galaxy.model import LibraryDatasetDatasetAssociation
@@ -191,14 +192,20 @@
data = data.to_history_dataset_association( None )
inp_data[name] = data
- else: # HDA
- if data.hid:
- input_names.append( 'data %s' % data.hid )
+# else: # HDA
+# if data.hid:
+# input_names.append( 'data %s' % data.hid )
input_ext = data.ext
if data.dbkey not in [None, '?']:
input_dbkey = data.dbkey
-
+ data_name_sane = re.sub('[^a-zA-Z0-9_]+', '', data.name)
+ if trans.app.config.use_data_id_on_string:
+ # we want names in our on_strings not numbers
+ input_names.append(data_name_sane)
+ else:
+ if data.hid:
+ input_names.append('data %s' % data.hid)
# Collect chromInfo dataset and add as parameters to incoming
db_datasets = {}
db_dataset = trans.db_dataset_for( input_dbkey )
@@ -232,11 +239,14 @@
if len( input_names ) == 1:
on_text = input_names[0]
elif len( input_names ) == 2:
- on_text = '%s and %s' % tuple(input_names[0:2])
+ #on_text = '%s and %s' % tuple(input_names[0:2])
+ on_text = '%s_%s' % tuple(input_names[0:2])
elif len( input_names ) == 3:
- on_text = '%s, %s, and %s' % tuple(input_names[0:3])
+ #on_text = '%s, %s, and %s' % tuple(input_names[0:3])
+ on_text = '%s_%s_%s' % tuple(input_names[0:3])
elif len( input_names ) > 3:
- on_text = '%s, %s, and others' % tuple(input_names[0:2])
+ #on_text = '%s, %s, and others' % tuple(input_names[0:2])
+ on_text = '%s_%s_and_others' % tuple(input_names[0:2])
else:
on_text = ""
# Add the dbkey to the incoming parameters
diff -r 009088d5e76fb00794da78dc0ee3cdaa8524b7d8 -r f830e3d7c6c096a9a840dad523540bdcc84327b2 tool-data/bowtie2_indices.loc.sample
--- a/tool-data/bowtie2_indices.loc.sample
+++ b/tool-data/bowtie2_indices.loc.sample
@@ -1,37 +1,37 @@
-# bowtie2_indices.loc.sample
-# This is a *.loc.sample file distributed with Galaxy that enables tools
-# to use a directory of indexed data files. This one is for Bowtie2 and Tophat2.
-# See the wiki: http://wiki.galaxyproject.org/Admin/NGS%20Local%20Setup
-# First create these data files and save them in your own data directory structure.
-# Then, create a bowtie_indices.loc file to use those indexes with tools.
-# Copy this file, save it with the same name (minus the .sample),
-# follow the format examples, and store the result in this directory.
-# The file should include an one line entry for each index set.
-# The path points to the "basename" for the set, not a specific file.
-# It has four text columns seperated by TABS.
+#This is a sample file distributed with Galaxy that enables tools
+#to use a directory of Bowtie2 indexed sequences data files. You will
+#need to create these data files and then create a bowtie_indices.loc
+#file similar to this one (store it in this directory) that points to
+#the directories in which those files are stored. The bowtie2_indices.loc
+#file has this format (longer white space characters are TAB characters):
#
-# <unique_build_id><dbkey><display_name><file_base_path>
+#<unique_build_id><dbkey><display_name><file_base_path>
#
-# So, for example, if you had hg18 indexes stored in:
+#So, for example, if you had hg18 indexed stored in
+#/depot/data2/galaxy/bowtie2/hg18/,
+#then the bowtie2_indices.loc entry would look like this:
#
-# /depot/data2/galaxy/hg19/bowtie2/
+#hg18 hg18 hg18 /depot/data2/galaxy/bowtie2/hg18/hg18
#
-# containing hg19 genome and hg19.*.bt2 files, such as:
-# -rw-rw-r-- 1 james james 914M Feb 10 18:56 hg19canon.fa
-# -rw-rw-r-- 1 james james 914M Feb 10 18:56 hg19canon.1.bt2
-# -rw-rw-r-- 1 james james 683M Feb 10 18:56 hg19canon.2.bt2
-# -rw-rw-r-- 1 james james 3.3K Feb 10 16:54 hg19canon.3.bt2
-# -rw-rw-r-- 1 james james 683M Feb 10 16:54 hg19canon.4.bt2
-# -rw-rw-r-- 1 james james 914M Feb 10 20:45 hg19canon.rev.1.bt2
-# -rw-rw-r-- 1 james james 683M Feb 10 20:45 hg19canon.rev.2.bt2
+#and your /depot/data2/galaxy/bowtie2/hg18/ directory
+#would contain hg18.*.ebwt files:
#
-# then the bowtie2_indices.loc entry could look like this:
+#-rw-r--r-- 1 james universe 830134 2005-09-13 10:12 hg18.1.ebwt
+#-rw-r--r-- 1 james universe 527388 2005-09-13 10:12 hg18.2.ebwt
+#-rw-r--r-- 1 james universe 269808 2005-09-13 10:12 hg18.3.ebwt
+#...etc...
#
-#hg19 hg19 Human (hg19) /depot/data2/galaxy/hg19/bowtie2/hg19canon
+#Your bowtie2_indices.loc file should include an entry per line for each
+#index set you have stored. The "file" in the path does not actually
+#exist, but it is the prefix for the actual index files. For example:
#
-#More examples:
+#hg18canon hg18 hg18 Canonical /depot/data2/galaxy/bowtie2/hg18/hg18canon
+#hg18full hg18 hg18 Full /depot/data2/galaxy/bowtie2/hg18/hg18full
+#/orig/path/hg19 hg19 hg19 /depot/data2/galaxy/bowtie2/hg19/hg19
+#...etc...
#
-#mm10 mm10 Mouse (mm10) /depot/data2/galaxy/mm10/bowtie2/mm10
-#dm3 dm3 D. melanogaster (dm3) /depot/data2/galaxy/mm10/bowtie2/dm3
+#Note that for backwards compatibility with workflows, the unique ID of
+#an entry must be the path that was in the original loc file, because that
+#is the value stored in the workflow for that parameter. That is why the
+#hg19 entry above looks odd. New genomes can be better-looking.
#
-#
diff -r 009088d5e76fb00794da78dc0ee3cdaa8524b7d8 -r f830e3d7c6c096a9a840dad523540bdcc84327b2 tools/ngs_rna/tophat2_wrapper.xml
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -1,5 +1,5 @@
-<tool id="tophat2" name="Tophat2" version="0.6">
- <!-- Wrapper compatible with Tophat version 2.0.0+ -->
+<tool id="tophat2" name="Tophat2" version="0.5">
+ <!-- Wrapper compatible with Tophat version 2.0.0 --><description>Gapped-read mapper for RNA-seq data</description><version_command>tophat2 --version</version_command><requirements>
@@ -126,6 +126,14 @@
</command><inputs>
+ <param name="jobname" type="text" value="Tophat2" size="80" label="Job title for outputs"
+ help="Output name to remind you what this was for">
+ <sanitizer invalid_char="">
+ <valid initial="string.letters,string.digits">
+ <add value="_" />
+ </valid>
+ </sanitizer>
+ </param><conditional name="singlePaired"><param name="sPaired" type="select" label="Is this library mate-paired?"><option value="single">Single-end</option>
@@ -268,25 +276,24 @@
</inputs><stdio>
- <regex match="Exception|Error" source="both" level="fatal" description="Tool execution failed"/>
+ <regex match="Exception" source="both" level="fatal" description="Tool exception"/><regex match=".*" source="both" level="log" description="tool progress"/></stdio><outputs>
- <data format="txt" name="align_summary" label="${tool.name} on ${on_string}: align_summary" from_work_dir="tophat_out/align_summary.txt"/>
- <data format="tabular" name="fusions" label="${tool.name} on ${on_string}: fusions" from_work_dir="tophat_out/fusions.out">
+ <data format="tabular" name="fusions" label="${on_string}_${jobname}_fusions.xls" from_work_dir="tophat_out/fusions.out"><filter>(params['settingsType'] == 'full' and params['fusion_search']['do_search'] == 'Yes')</filter></data>
- <data format="bed" name="insertions" label="${tool.name} on ${on_string}: insertions" from_work_dir="tophat_out/insertions.bed">
+ <data format="bed" name="insertions" label="${on_string}_${jobname}_ins.bed" from_work_dir="tophat_out/insertions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="deletions" label="${tool.name} on ${on_string}: deletions" from_work_dir="tophat_out/deletions.bed">
+ <data format="bed" name="deletions" label="${on_string}_${jobname}_del.bed" from_work_dir="tophat_out/deletions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bed" name="junctions" label="${tool.name} on ${on_string}: splice junctions" from_work_dir="tophat_out/junctions.bed">
+ <data format="bed" name="junctions" label="${on_string}_${jobname}_splicejunc.bed" from_work_dir="tophat_out/junctions.bed"><expand macro="dbKeyActions" /></data>
- <data format="bam" name="accepted_hits" label="${tool.name} on ${on_string}: accepted_hits" from_work_dir="tophat_out/accepted_hits.bam">
+ <data format="bam" name="accepted_hits" label="${on_string}_${jobname}_hits.bam" from_work_dir="tophat_out/accepted_hits.bam"><expand macro="dbKeyActions" /></data></outputs>
@@ -313,7 +320,6 @@
</actions></macro></macros>
-
<tests><!-- Test base-space single-end reads with pre-built index and preset parameters --><test>
@@ -452,8 +458,7 @@
<help>
**Tophat Overview**
-TopHat_ is a fast splice junction mapper for RNA-Seq reads. It aligns RNA-Seq reads to mammalian-sized genomes using the ultra high-throughput short read aligner Bowtie(2), and then analyzes the mapping results to identify splice junctions between exons. Please cite: Kim D, Pertea G, Trapnell C, Pimentel H, Kelley R, and Salzberg SL. TopHat2: accurate alignment
-of transcriptomes in the presence of insertions, deletions and gene fusions. Genome Biol 14:R36, 2013.
+TopHat_ is a fast splice junction mapper for RNA-Seq reads. It aligns RNA-Seq reads to mammalian-sized genomes using the ultra high-throughput short read aligner Bowtie, and then analyzes the mapping results to identify splice junctions between exons. Please cite: Trapnell, C., Pachter, L. and Salzberg, S.L. TopHat: discovering splice junctions with RNA-Seq. Bioinformatics 25, 1105-1111 (2009).
.. _Tophat: http://tophat.cbcb.umd.edu/
diff -r 009088d5e76fb00794da78dc0ee3cdaa8524b7d8 -r f830e3d7c6c096a9a840dad523540bdcc84327b2 tools/rgedgeR/rgToolFactory.py
--- /dev/null
+++ b/tools/rgedgeR/rgToolFactory.py
@@ -0,0 +1,605 @@
+# rgToolFactory.py
+# see https://bitbucket.org/fubar/galaxytoolfactory/wiki/Home
+#
+# copyright ross lazarus (ross stop lazarus at gmail stop com) May 2012
+#
+# all rights reserved
+# Licensed under the LGPL
+# suggestions for improvement and bug fixes welcome at https://bitbucket.org/fubar/galaxytoolfactory/wiki/Home
+#
+# july 2013
+# added ability to combine images and individual log files into html output
+# just make sure there's a log file foo.log and it will be output
+# together with all images named like "foo_*.pdf
+# otherwise old format for html
+#
+# January 2013
+# problem pointed out by Carlos Borroto
+# added escaping for <>$ - thought I did that ages ago...
+#
+# August 11 2012
+# changed to use shell=False and cl as a sequence
+
+# This is a Galaxy tool factory for simple scripts in python, R or whatever ails ye.
+# It also serves as the wrapper for the new tool.
+#
+# you paste and run your script
+# Only works for simple scripts that read one input from the history.
+# Optionally can write one new history dataset,
+# and optionally collect any number of outputs into links on an autogenerated HTML page.
+
+# DO NOT install on a public or important site - please.
+
+# installed generated tools are fine if the script is safe.
+# They just run normally and their user cannot do anything unusually insecure
+# but please, practice safe toolshed.
+# Read the fucking code before you install any tool
+# especially this one
+
+# After you get the script working on some test data, you can
+# optionally generate a toolshed compatible gzip file
+# containing your script safely wrapped as an ordinary Galaxy script in your local toolshed for
+# safe and largely automated installation in a production Galaxy.
+
+# If you opt for an HTML output, you get all the script outputs arranged
+# as a single Html history item - all output files are linked, thumbnails for all the pdfs.
+# Ugly but really inexpensive.
+#
+# Patches appreciated please.
+#
+#
+# long route to June 2012 product
+# Behold the awesome power of Galaxy and the toolshed with the tool factory to bind them
+# derived from an integrated script model
+# called rgBaseScriptWrapper.py
+# Note to the unwary:
+# This tool allows arbitrary scripting on your Galaxy as the Galaxy user
+# There is nothing stopping a malicious user doing whatever they choose
+# Extremely dangerous!!
+# Totally insecure. So, trusted users only
+#
+# preferred model is a developer using their throw away workstation instance - ie a private site.
+# no real risk. The universe_wsgi.ini admin_users string is checked - only admin users are permitted to run this tool.
+#
+
+import sys
+import shutil
+import subprocess
+import os
+import time
+import tempfile
+import optparse
+import tarfile
+import re
+import shutil
+import math
+
+progname = os.path.split(sys.argv[0])[1]
+myversion = 'V000.2 June 2012'
+verbose = False
+debug = False
+toolFactoryURL = 'https://bitbucket.org/fubar/galaxytoolfactory'
+
+def timenow():
+ """return current time as a string
+ """
+ return time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(time.time()))
+
+html_escape_table = {
+ "&": "&",
+ ">": ">",
+ "<": "<",
+ "$": "\$"
+ }
+
+def html_escape(text):
+ """Produce entities within text."""
+ return "".join(html_escape_table.get(c,c) for c in text)
+
+def cmd_exists(cmd):
+ return subprocess.call("type " + cmd, shell=True,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
+
+
+class ScriptRunner:
+ """class is a wrapper for an arbitrary script
+ """
+
+ def __init__(self,opts=None,treatbashSpecial=True):
+ """
+ cleanup inputs, setup some outputs
+
+ """
+ self.useGM = cmd_exists('gm')
+ self.useIM = cmd_exists('convert')
+ self.useGS = cmd_exists('gs')
+ self.treatbashSpecial = treatbashSpecial
+ if opts.output_dir: # simplify for the tool tarball
+ os.chdir(opts.output_dir)
+ self.thumbformat = 'png'
+ self.opts = opts
+ self.toolname = re.sub('[^a-zA-Z0-9_]+', '', opts.tool_name) # a sanitizer now does this but..
+ self.toolid = self.toolname
+ self.myname = sys.argv[0] # get our name because we write ourselves out as a tool later
+ self.pyfile = self.myname # crude but efficient - the cruft won't hurt much
+ self.xmlfile = '%s.xml' % self.toolname
+ s = open(self.opts.script_path,'r').readlines()
+ s = [x.rstrip() for x in s] # remove pesky dos line endings if needed
+ self.script = '\n'.join(s)
+ fhandle,self.sfile = tempfile.mkstemp(prefix=self.toolname,suffix=".%s" % (opts.interpreter))
+ tscript = open(self.sfile,'w') # use self.sfile as script source for Popen
+ tscript.write(self.script)
+ tscript.close()
+ self.indentedScript = '\n'.join([' %s' % x for x in s]) # for restructured text in help
+ self.escapedScript = '\n'.join([html_escape(x) for x in s])
+ self.elog = os.path.join(self.opts.output_dir,"%s_error.log" % self.toolname)
+ if opts.output_dir: # may not want these complexities
+ self.tlog = os.path.join(self.opts.output_dir,"%s_runner.log" % self.toolname)
+ art = '%s.%s' % (self.toolname,opts.interpreter)
+ artpath = os.path.join(self.opts.output_dir,art) # need full path
+ artifact = open(artpath,'w') # use self.sfile as script source for Popen
+ artifact.write(self.script)
+ artifact.close()
+ self.cl = []
+ self.html = []
+ a = self.cl.append
+ a(opts.interpreter)
+ if self.treatbashSpecial and opts.interpreter in ['bash','sh']:
+ a(self.sfile)
+ else:
+ a('-') # stdin
+ a(opts.input_tab)
+ a(opts.output_tab)
+ self.outFormats = 'tabular' # TODO make this an option at tool generation time
+ self.inputFormats = 'tabular' # TODO make this an option at tool generation time
+ self.test1Input = '%s_test1_input.xls' % self.toolname
+ self.test1Output = '%s_test1_output.xls' % self.toolname
+ self.test1HTML = '%s_test1_output.html' % self.toolname
+
+ def makeXML(self):
+ """
+ Create a Galaxy xml tool wrapper for the new script as a string to write out
+ fixme - use templating or something less fugly than this example of what we produce
+
+ <tool id="reverse" name="reverse" version="0.01">
+ <description>a tabular file</description>
+ <command interpreter="python">
+ reverse.py --script_path "$runMe" --interpreter "python"
+ --tool_name "reverse" --input_tab "$input1" --output_tab "$tab_file"
+ </command>
+ <inputs>
+ <param name="input1" type="data" format="tabular" label="Select a suitable input file from your history"/><param name="job_name" type="text" label="Supply a name for the outputs to remind you what they contain" value="reverse"/>
+
+ </inputs>
+ <outputs>
+ <data format="tabular" name="tab_file" label="${job_name}"/>
+
+ </outputs>
+ <help>
+
+**What it Does**
+
+Reverse the columns in a tabular file
+
+ </help>
+ <configfiles>
+ <configfile name="runMe">
+
+# reverse order of columns in a tabular file
+import sys
+inp = sys.argv[1]
+outp = sys.argv[2]
+i = open(inp,'r')
+o = open(outp,'w')
+for row in i:
+ rs = row.rstrip().split('\t')
+ rs.reverse()
+ o.write('\t'.join(rs))
+ o.write('\n')
+i.close()
+o.close()
+
+
+ </configfile>
+ </configfiles>
+ </tool>
+
+ """
+ newXML="""<tool id="%(toolid)s" name="%(toolname)s" version="%(tool_version)s">
+ %(tooldesc)s
+ %(command)s
+ <inputs>
+ %(inputs)s
+ </inputs>
+ <outputs>
+ %(outputs)s
+ </outputs>
+ <configfiles>
+ <configfile name="runMe">
+ %(script)s
+ </configfile>
+ </configfiles>
+ %(tooltests)s
+ <help>
+ %(help)s
+ </help>
+ </tool>""" # needs a dict with toolname, toolid, interpreter, scriptname, command, inputs as a multi line string ready to write, outputs ditto, help ditto
+
+ newCommand="""<command interpreter="python">
+ %(toolname)s.py --script_path "$runMe" --interpreter "%(interpreter)s"
+ --tool_name "%(toolname)s" %(command_inputs)s %(command_outputs)s
+ </command>""" # may NOT be an input or htmlout
+ tooltestsTabOnly = """<tests><test>
+ <param name="input1" value="%(test1Input)s" ftype="tabular"/>
+ <param name="job_name" value="test1"/>
+ <param name="runMe" value="$runMe"/>
+ <output name="tab_file" file="%(test1Output)s" ftype="tabular"/>
+ </test></tests>"""
+ tooltestsHTMLOnly = """<tests><test>
+ <param name="input1" value="%(test1Input)s" ftype="tabular"/>
+ <param name="job_name" value="test1"/>
+ <param name="runMe" value="$runMe"/>
+ <output name="html_file" file="%(test1HTML)s" ftype="html" lines_diff="5"/>
+ </test></tests>"""
+ tooltestsBoth = """<tests><test>
+ <param name="input1" value="%(test1Input)s" ftype="tabular"/>
+ <param name="job_name" value="test1"/>
+ <param name="runMe" value="$runMe"/>
+ <output name="tab_file" file="%(test1Output)s" ftype="tabular" />
+ <output name="html_file" file="%(test1HTML)s" ftype="html" lines_diff="10"/>
+ </test></tests>"""
+ xdict = {}
+ xdict['tool_version'] = self.opts.tool_version
+ xdict['test1Input'] = self.test1Input
+ xdict['test1HTML'] = self.test1HTML
+ xdict['test1Output'] = self.test1Output
+ if self.opts.make_HTML and self.opts.output_tab <> 'None':
+ xdict['tooltests'] = tooltestsBoth % xdict
+ elif self.opts.make_HTML:
+ xdict['tooltests'] = tooltestsHTMLOnly % xdict
+ else:
+ xdict['tooltests'] = tooltestsTabOnly % xdict
+ xdict['script'] = self.escapedScript
+ # configfile is least painful way to embed script to avoid external dependencies
+ # but requires escaping of <, > and $ to avoid Mako parsing
+ if self.opts.help_text:
+ xdict['help'] = open(self.opts.help_text,'r').read()
+ else:
+ xdict['help'] = 'Please ask the tool author for help as none was supplied at tool generation'
+ coda = ['**Script**','Pressing execute will run the following code over your input file and generate some outputs in your history::']
+ coda.append(self.indentedScript)
+ coda.append('**Attribution** This Galaxy tool was created by %s at %s\nusing the Galaxy Tool Factory.' % (self.opts.user_email,timenow()))
+ coda.append('See %s for details of that project' % (toolFactoryURL))
+ coda.append('Please cite: Creating re-usable tools from scripts: The Galaxy Tool Factory. Ross Lazarus; Antony Kaspi; Mark Ziemann; The Galaxy Team. ')
+ coda.append('Bioinformatics 2012; doi: 10.1093/bioinformatics/bts573')
+ xdict['help'] = '%s\n%s' % (xdict['help'],'\n'.join(coda))
+ if self.opts.tool_desc:
+ xdict['tooldesc'] = '<description>%s</description>' % self.opts.tool_desc
+ else:
+ xdict['tooldesc'] = ''
+ xdict['command_outputs'] = ''
+ xdict['outputs'] = ''
+ if self.opts.input_tab <> 'None':
+ xdict['command_inputs'] = '--input_tab "$input1" ' # the space may matter a lot if we append something
+ xdict['inputs'] = '<param name="input1" type="data" format="%s" label="Select a suitable input file from your history"/> \n' % self.inputFormats
+ else:
+ xdict['command_inputs'] = '' # assume no input - eg a random data generator
+ xdict['inputs'] = ''
+ xdict['inputs'] += '<param name="job_name" type="text" label="Supply a name for the outputs to remind you what they contain" value="%s"/> \n' % self.toolname
+ xdict['toolname'] = self.toolname
+ xdict['toolid'] = self.toolid
+ xdict['interpreter'] = self.opts.interpreter
+ xdict['scriptname'] = self.sfile
+ if self.opts.make_HTML:
+ xdict['command_outputs'] += ' --output_dir "$html_file.files_path" --output_html "$html_file" --make_HTML "yes" '
+ xdict['outputs'] += ' <data format="html" name="html_file" label="${job_name}.html"/>\n'
+ if self.opts.output_tab <> 'None':
+ xdict['command_outputs'] += ' --output_tab "$tab_file"'
+ xdict['outputs'] += ' <data format="%s" name="tab_file" label="${job_name}"/>\n' % self.outFormats
+ xdict['command'] = newCommand % xdict
+ xmls = newXML % xdict
+ xf = open(self.xmlfile,'w')
+ xf.write(xmls)
+ xf.write('\n')
+ xf.close()
+ # ready for the tarball
+
+
+ def makeTooltar(self):
+ """
+ a tool is a gz tarball with eg
+ /toolname/tool.xml /toolname/tool.py /toolname/test-data/test1_in.foo ...
+ """
+ retval = self.run()
+ if retval:
+ print >> sys.stderr,'## Run failed. Cannot build yet. Please fix and retry'
+ sys.exit(1)
+ self.makeXML()
+ tdir = self.toolname
+ os.mkdir(tdir)
+ if self.opts.input_tab <> 'None': # no reproducible test otherwise? TODO: maybe..
+ testdir = os.path.join(tdir,'test-data')
+ os.mkdir(testdir) # make tests directory
+ shutil.copyfile(self.opts.input_tab,os.path.join(testdir,self.test1Input))
+ if self.opts.output_tab <> 'None':
+ shutil.copyfile(self.opts.output_tab,os.path.join(testdir,self.test1Output))
+ if self.opts.make_HTML:
+ shutil.copyfile(self.opts.output_html,os.path.join(testdir,self.test1HTML))
+ if self.opts.output_dir:
+ shutil.copyfile(self.tlog,os.path.join(testdir,'test1_out.log'))
+ op = '%s.py' % self.toolname # new name
+ outpiname = os.path.join(tdir,op) # path for the tool tarball
+ pyin = os.path.basename(self.pyfile) # our name - we rewrite ourselves (TM)
+ notes = ['# %s - a self annotated version of %s generated by running %s\n' % (op,pyin,pyin),]
+ notes.append('# to make a new Galaxy tool called %s\n' % self.toolname)
+ notes.append('# User %s at %s\n' % (self.opts.user_email,timenow()))
+ pi = open(self.pyfile,'r').readlines() # our code becomes new tool wrapper (!) - first Galaxy worm
+ notes += pi
+ outpi = open(outpiname,'w')
+ outpi.write(''.join(notes))
+ outpi.write('\n')
+ outpi.close()
+ stname = os.path.join(tdir,self.sfile)
+ if not os.path.exists(stname):
+ shutil.copyfile(self.sfile, stname)
+ xtname = os.path.join(tdir,self.xmlfile)
+ if not os.path.exists(xtname):
+ shutil.copyfile(self.xmlfile,xtname)
+ tarpath = "%s.gz" % self.toolname
+ tar = tarfile.open(tarpath, "w:gz")
+ tar.add(tdir,arcname=self.toolname)
+ tar.close()
+ shutil.copyfile(tarpath,self.opts.new_tool)
+ shutil.rmtree(tdir)
+ ## TODO: replace with optional direct upload to local toolshed?
+ return retval
+
+
+ def compressPDF(self,inpdf=None,thumbformat='png'):
+ """need absolute path to pdf
+ """
+ assert os.path.isfile(inpdf), "## Input %s supplied to %s compressPDF not found" % (inpdf,self.myName)
+ hf,hlog = tempfile.mkstemp(suffix="%s.log" % self.toolname)
+ sto = open(hlog,'w')
+ outpdf = '%s_compressed' % inpdf
+ cl = ["gs", "-sDEVICE=pdfwrite", "-dNOPAUSE", "-dBATCH","-dPDFSETTINGS=/printer", "-sOutputFile=%s" % outpdf,inpdf]
+ x = subprocess.Popen(cl,stdout=sto,stderr=sto,cwd=self.opts.output_dir)
+ retval1 = x.wait()
+ if retval1 == 0:
+ os.unlink(inpdf)
+ shutil.move(outpdf,inpdf)
+ outpng = '%s.%s' % (os.path.splitext(inpdf)[0],thumbformat)
+ if self.useGM:
+ cl2 = ['gm convert', inpdf, outpng]
+ else: # assume imagemagick
+ cl2 = ['convert', inpdf, outpng]
+ x = subprocess.Popen(cl2,stdout=sto,stderr=sto,cwd=self.opts.output_dir)
+ retval2 = x.wait()
+ sto.close()
+ retval = retval1 or retval2
+ return retval
+
+
+ def getfSize(self,fpath,outpath):
+ """
+ format a nice file size string
+ """
+ size = ''
+ fp = os.path.join(outpath,fpath)
+ if os.path.isfile(fp):
+ size = '0 B'
+ n = float(os.path.getsize(fp))
+ if n > 2**20:
+ size = '%1.1f MB' % (n/2**20)
+ elif n > 2**10:
+ size = '%1.1f KB' % (n/2**10)
+ elif n > 0:
+ size = '%d B' % (int(n))
+ return size
+
+ def makeHtml(self):
+ """ Create an HTML file content to list all the artifacts found in the output_dir
+ """
+
+ galhtmlprefix = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="generator" content="Galaxy %s tool output - see http://g2.trac.bx.psu.edu/" />
+ <title></title>
+ <link rel="stylesheet" href="/static/style/base.css" type="text/css" />
+ </head>
+ <body>
+ <div class="toolFormBody">
+ """
+ galhtmlattr = """<hr/><div class="infomessage">This tool (%s) was generated by the <a href="https://bitbucket.org/fubar/galaxytoolfactory/overview">Galaxy Tool Factory</a></div><br/>"""
+ galhtmlpostfix = """</div></body></html>\n"""
+
+ flist = os.listdir(self.opts.output_dir)
+ flist = [x for x in flist if x <> 'Rplots.pdf']
+ flist.sort()
+ html = []
+ html.append(galhtmlprefix % progname)
+ html.append('<div class="infomessage">Galaxy Tool "%s" run at %s</div><br/>' % (self.toolname,timenow()))
+ fhtml = []
+ if len(flist) > 0:
+ logfiles = [x for x in flist if x.lower().endswith('.log')] # log file names determine sections
+ logfiles.sort()
+ logfiles = [x for x in logfiles if os.path.abspath(x) <> os.path.abspath(self.tlog)]
+ logfiles.append(os.path.abspath(self.tlog)) # make it the last one
+ pdflist = []
+ npdf = len([x for x in flist if os.path.splitext(x)[-1].lower() == '.pdf'])
+ for rownum,fname in enumerate(flist):
+ dname,e = os.path.splitext(fname)
+ sfsize = self.getfSize(fname,self.opts.output_dir)
+ if e.lower() == '.pdf' : # compress and make a thumbnail
+ thumb = '%s.%s' % (dname,self.thumbformat)
+ pdff = os.path.join(self.opts.output_dir,fname)
+ retval = self.compressPDF(inpdf=pdff,thumbformat=self.thumbformat)
+ if retval == 0:
+ pdflist.append((fname,thumb))
+ if (rownum+1) % 2 == 0:
+ fhtml.append('<tr class="odd_row"><td><a href="%s">%s</a></td><td>%s</td></tr>' % (fname,fname,sfsize))
+ else:
+ fhtml.append('<tr><td><a href="%s">%s</a></td><td>%s</td></tr>' % (fname,fname,sfsize))
+ for logfname in logfiles: # expect at least tlog - if more
+ if os.path.abspath(logfname) == os.path.abspath(self.tlog): # handled later
+ sectionname = 'All tool run'
+ if (len(logfiles) > 1):
+ sectionname = 'Other'
+ ourpdfs = pdflist
+ else:
+ realname = os.path.basename(logfname)
+ sectionname = os.path.splitext(realname)[0].split('_')[0] # break in case _ added to log
+ ourpdfs = [x for x in pdflist if os.path.basename(x[0]).split('_')[0] == sectionname]
+ pdflist = [x for x in pdflist if os.path.basename(x[0]).split('_')[0] <> sectionname] # remove
+ nacross = 1
+ npdf = len(ourpdfs)
+
+ if npdf > 0:
+ nacross = math.sqrt(npdf) ## int(round(math.log(npdf,2)))
+ if int(nacross)**2 != npdf:
+ nacross += 1
+ nacross = int(nacross)
+ width = min(400,int(1200/nacross))
+ html.append('<div class="toolFormTitle">%s images and outputs</div>' % sectionname)
+ html.append('(Click on a thumbnail image to download the corresponding original PDF image)<br/>')
+ ntogo = nacross # counter for table row padding with empty cells
+ html.append('<div><table class="simple" cellpadding="2" cellspacing="2">\n<tr>')
+ for i,paths in enumerate(ourpdfs):
+ fname,thumb = paths
+ s= """<td><a href="%s"><img src="%s" title="Click to download a PDF of %s" hspace="5" width="%d"
+ alt="Image called %s"/></a></td>\n""" % (fname,thumb,fname,width,fname)
+ if ((i+1) % nacross == 0):
+ s += '</tr>\n'
+ ntogo = 0
+ if i < (npdf - 1): # more to come
+ s += '<tr>'
+ ntogo = nacross
+ else:
+ ntogo -= 1
+ html.append(s)
+ if html[-1].strip().endswith('</tr>'):
+ html.append('</table></div>\n')
+ else:
+ if ntogo > 0: # pad
+ html.append('<td> </td>'*ntogo)
+ html.append('</tr></table></div>\n')
+ logt = open(logfname,'r').readlines()
+ logtext = [x for x in logt if x.strip() > '']
+ html.append('<div class="toolFormTitle">%s log output</div>' % sectionname)
+ if len(logtext) > 1:
+ html.append('\n<pre>\n')
+ html += logtext
+ html.append('\n</pre>\n')
+ else:
+ html.append('%s is empty<br/>' % logfname)
+ if len(fhtml) > 0:
+ fhtml.insert(0,'<div><table class="colored" cellpadding="3" cellspacing="3"><tr><th>Output File Name (click to view)</th><th>Size</th></tr>\n')
+ fhtml.append('</table></div><br/>')
+ html.append('<div class="toolFormTitle">All output files available for downloading</div>\n')
+ html += fhtml # add all non-pdf files to the end of the display
+ else:
+ html.append('<div class="warningmessagelarge">### Error - %s returned no files - please confirm that parameters are sane</div>' % self.opts.interpreter)
+ html.append(galhtmlpostfix)
+ htmlf = file(self.opts.output_html,'w')
+ htmlf.write('\n'.join(html))
+ htmlf.write('\n')
+ htmlf.close()
+ self.html = html
+
+
+ def run(self):
+ """
+ scripts must be small enough not to fill the pipe!
+ """
+ if self.treatbashSpecial and self.opts.interpreter in ['bash','sh']:
+ retval = self.runBash()
+ else:
+ if self.opts.output_dir:
+ ste = open(self.elog,'w')
+ sto = open(self.tlog,'w')
+ sto.write('## Toolfactory generated command line = %s\n' % ' '.join(self.cl))
+ sto.flush()
+ p = subprocess.Popen(self.cl,shell=False,stdout=sto,stderr=ste,stdin=subprocess.PIPE,cwd=self.opts.output_dir)
+ else:
+ p = subprocess.Popen(self.cl,shell=False,stdin=subprocess.PIPE)
+ p.stdin.write(self.script)
+ p.stdin.close()
+ retval = p.wait()
+ if self.opts.output_dir:
+ sto.close()
+ ste.close()
+ err = open(self.elog,'r').readlines()
+ if retval <> 0 and err: # problem
+ print >> sys.stderr,err
+ if self.opts.make_HTML:
+ self.makeHtml()
+ return retval
+
+ def runBash(self):
+ """
+ cannot use - for bash so use self.sfile
+ """
+ if self.opts.output_dir:
+ s = '## Toolfactory generated command line = %s\n' % ' '.join(self.cl)
+ sto = open(self.tlog,'w')
+ sto.write(s)
+ sto.flush()
+ p = subprocess.Popen(self.cl,shell=False,stdout=sto,stderr=sto,cwd=self.opts.output_dir)
+ else:
+ p = subprocess.Popen(self.cl,shell=False)
+ retval = p.wait()
+ if self.opts.output_dir:
+ sto.close()
+ if self.opts.make_HTML:
+ self.makeHtml()
+ return retval
+
+
+def main():
+ u = """
+ This is a Galaxy wrapper. It expects to be called by a special purpose tool.xml as:
+ <command interpreter="python">rgBaseScriptWrapper.py --script_path "$scriptPath" --tool_name "foo" --interpreter "Rscript"
+ </command>
+ """
+ op = optparse.OptionParser()
+ a = op.add_option
+ a('--script_path',default=None)
+ a('--tool_name',default=None)
+ a('--interpreter',default=None)
+ a('--output_dir',default=None)
+ a('--output_html',default=None)
+ a('--input_tab',default="None")
+ a('--output_tab',default="None")
+ a('--user_email',default='Unknown')
+ a('--bad_user',default=None)
+ a('--make_Tool',default=None)
+ a('--make_HTML',default=None)
+ a('--help_text',default=None)
+ a('--tool_desc',default=None)
+ a('--new_tool',default=None)
+ a('--tool_version',default=None)
+ opts, args = op.parse_args()
+ assert not opts.bad_user,'UNAUTHORISED: %s is NOT authorized to use this tool until Galaxy admin adds %s to admin_users in universe_wsgi.ini' % (opts.bad_user,opts.bad_user)
+ assert opts.tool_name,'## Tool Factory expects a tool name - eg --tool_name=DESeq'
+ assert opts.interpreter,'## Tool Factory wrapper expects an interpreter - eg --interpreter=Rscript'
+ assert os.path.isfile(opts.script_path),'## Tool Factory wrapper expects a script path - eg --script_path=foo.R'
+ if opts.output_dir:
+ try:
+ os.makedirs(opts.output_dir)
+ except:
+ pass
+ r = ScriptRunner(opts)
+ if opts.make_Tool:
+ retcode = r.makeTooltar()
+ else:
+ retcode = r.run()
+ os.unlink(r.sfile)
+ if retcode:
+ sys.exit(retcode) # indicate failure to job runner
+
+
+if __name__ == "__main__":
+ main()
+
+
diff -r 009088d5e76fb00794da78dc0ee3cdaa8524b7d8 -r f830e3d7c6c096a9a840dad523540bdcc84327b2 tools/rgedgeR/rgedgeRpaired.xml
--- /dev/null
+++ b/tools/rgedgeR/rgedgeRpaired.xml
@@ -0,0 +1,1079 @@
+<tool id="rgDifferentialCount" name="Differential_Count" version="0.20">
+ <description>models using BioConductor packages</description>
+ <requirements>
+ <requirement type="package" version="2.12">biocbasics</requirement>
+ <requirement type="package" version="3.0.1">r3</requirement>
+ <requirement type="package" version="1.3.18">graphicsmagick</requirement>
+ <requirement type="package" version="9.07">ghostscript</requirement>
+ </requirements>
+
+ <command interpreter="python">
+ rgToolFactory.py --script_path "$runme" --interpreter "Rscript" --tool_name "DifferentialCounts"
+ --output_dir "$html_file.files_path" --output_html "$html_file" --make_HTML "yes"
+ </command>
+ <inputs>
+ <param name="input1" type="data" format="tabular" label="Select an input matrix - rows are contigs, columns are counts for each sample"
+ help="Use the HTSeq based count matrix preparation tool to create these matrices from BAM/SAM files and a GTF file of genomic features"/>
+ <param name="title" type="text" value="Differential Counts" size="80" label="Title for job outputs"
+ help="Supply a meaningful name here to remind you what the outputs contain">
+ <sanitizer invalid_char="">
+ <valid initial="string.letters,string.digits"><add value="_" /></valid>
+ </sanitizer>
+ </param>
+ <param name="treatment_name" type="text" value="Treatment" size="50" label="Treatment Name"/>
+ <param name="Treat_cols" label="Select columns containing treatment." type="data_column" data_ref="input1" numerical="True"
+ multiple="true" use_header_names="true" size="120" display="checkboxes">
+ <validator type="no_options" message="Please select at least one column."/>
+ </param>
+ <param name="control_name" type="text" value="Control" size="50" label="Control Name"/>
+ <param name="Control_cols" label="Select columns containing control." type="data_column" data_ref="input1" numerical="True"
+ multiple="true" use_header_names="true" size="120" display="checkboxes" optional="true">
+ </param>
+ <param name="subjectids" type="text" optional="true" size="120" value = ""
+ label="IF SUBJECTS NOT ALL INDEPENDENT! Enter comma separated strings to indicate sample labels for (eg) pairing - must be one for every column in input"
+ help="Leave blank if no pairing, but eg if data from sample id A99 is in columns 2,4 and id C21 is in 3,5 then enter 'A99,C21,A99,C21'">
+ <sanitizer>
+ <valid initial="string.letters,string.digits"><add value="," /></valid>
+ </sanitizer>
+ </param>
+ <param name="fQ" type="float" value="0.3" size="5" label="Non-differential contig count quantile threshold - zero to analyze all non-zero read count contigs"
+ help="May be a good or a bad idea depending on the biology and the question. EG 0.3 = sparsest 30% of contigs with at least one read are removed before analysis"/>
+ <param name="useNDF" type="boolean" truevalue="T" falsevalue="F" checked="false" size="1"
+ label="Non differential filter - remove contigs below a threshold (1 per million) for half or more samples"
+ help="May be a good or a bad idea depending on the biology and the question. This was the old default. Quantile based is available as an alternative"/>
+
+ <conditional name="edgeR">
+ <param name="doedgeR" type="select"
+ label="Run this model using edgeR"
+ help="edgeR uses a negative binomial model and seems to be powerful, even with few replicates">
+ <option value="F">Do not run edgeR</option>
+ <option value="T" selected="true">Run edgeR</option>
+ </param>
+ <when value="T">
+ <param name="edgeR_priordf" type="integer" value="20" size="3"
+ label="prior.df for tagwise dispersion - lower value = more emphasis on each tag's variance. Replaces prior.n and prior.df = prior.n * residual.df"
+ help="0 = Use edgeR default. Use a small value to 'smooth' small samples. See edgeR docs and note below"/>
+ </when>
+ <when value="F"></when>
+ </conditional>
+ <conditional name="DESeq2">
+ <param name="doDESeq2" type="select"
+ label="Run the same model with DESeq2 and compare findings"
+ help="DESeq2 is an update to the DESeq package. It uses different assumptions and methods to edgeR">
+ <option value="F" selected="true">Do not run DESeq2</option>
+ <option value="T">Run DESeq2</option>
+ </param>
+ <when value="T">
+ <param name="DESeq_fitType" type="select">
+ <option value="parametric" selected="true">Parametric (default) fit for dispersions</option>
+ <option value="local">Local fit - this will automagically be used if parametric fit fails</option>
+ <option value="mean">Mean dispersion fit- use this if you really understand what you're doing - read the fine manual linked below in the documentation</option>
+ </param>
+ </when>
+ <when value="F"></when>
+ </conditional>
+ <param name="doVoom" type="select"
+ label="Run the same model with Voom/limma and compare findings"
+ help="Voom uses counts per million and a precise transformation of variance so count data can be analysed using limma">
+ <option value="F" selected="true">Do not run VOOM</option>
+ <option value="T">Run VOOM</option>
+ </param>
+ <conditional name="camera">
+ <param name="doCamera" type="select" label="Run the edgeR implementation of Camera GSEA for up/down gene sets"
+ help="If yes, you can choose a set of genesets to test and/or supply a gmt format geneset collection from your history">
+ <option value="F" selected="true">Do not run GSEA tests with the Camera algorithm</option>
+ <option value="T">Run GSEA tests with the Camera algorithm</option>
+ </param>
+ <when value="T">
+ <conditional name="gmtSource">
+ <param name="refgmtSource" type="select"
+ label="Use a gene set (.gmt) from your history and/or use a built-in (MSigDB etc) gene set">
+ <option value="indexed" selected="true">Use a built-in gene set</option>
+ <option value="history">Use a gene set from my history</option>
+ <option value="both">Add a gene set from my history to a built in gene set</option>
+ </param>
+ <when value="indexed">
+ <param name="builtinGMT" type="select" label="Select a gene set matrix (.gmt) file to use for the analysis">
+ <options from_data_table="gseaGMT_3.1">
+ <filter type="sort_by" column="2" />
+ <validator type="no_options" message="No GMT v3.1 files are available - please install them"/>
+ </options>
+ </param>
+ </when>
+ <when value="history">
+ <param name="ownGMT" type="data" format="gmt" label="Select a Gene Set from your history" />
+ </when>
+ <when value="both">
+ <param name="ownGMT" type="data" format="gseagmt" label="Select a Gene Set from your history" />
+ <param name="builtinGMT" type="select" label="Select a gene set matrix (.gmt) file to use for the analysis">
+ <options from_data_table="gseaGMT_4">
+ <filter type="sort_by" column="2" />
+ <validator type="no_options" message="No GMT v4 files are available - please fix tool_data_table and loc files"/>
+ </options>
+ </param>
+ </when>
+ </conditional>
+ </when>
+ <when value="F">
+ </when>
+ </conditional>
+ <param name="fdrthresh" type="float" value="0.05" size="5" label="P value threshold for FDR filtering for amily wise error rate control"
+ help="Conventional default value of 0.05 recommended"/>
+ <param name="fdrtype" type="select" label="FDR (Type II error) control method"
+ help="Use fdr or bh typically to control for the number of tests in a reliable way">
+ <option value="fdr" selected="true">fdr</option>
+ <option value="BH">Benjamini Hochberg</option>
+ <option value="BY">Benjamini Yukateli</option>
+ <option value="bonferroni">Bonferroni</option>
+ <option value="hochberg">Hochberg</option>
+ <option value="holm">Holm</option>
+ <option value="hommel">Hommel</option>
+ <option value="none">no control for multiple tests</option>
+ </param>
+ </inputs>
+ <outputs>
+ <data format="tabular" name="out_edgeR" label="${title}_topTable_edgeR.xls">
+ <filter>edgeR['doedgeR'] == "T"</filter>
+ </data>
+ <data format="tabular" name="out_DESeq2" label="${title}_topTable_DESeq2.xls">
+ <filter>DESeq2['doDESeq2'] == "T"</filter>
+ </data>
+ <data format="tabular" name="out_VOOM" label="${title}_topTable_VOOM.xls">
+ <filter>doVoom == "T"</filter>
+ </data>
+ <data format="html" name="html_file" label="${title}.html"/>
+ </outputs>
+ <stdio>
+ <exit_code range="4" level="fatal" description="Number of subject ids must match total number of samples in the input matrix" />
+ </stdio>
+ <tests>
+<test>
+<param name='input1' value='test_bams2mx.xls' ftype='tabular' />
+ <param name='treatment_name' value='case' />
+ <param name='title' value='edgeRtest' />
+ <param name='useNDF' value='' />
+ <param name='doedgeR' value='T' />
+ <param name='doVoom' value='T' />
+ <param name='doDESeq2' value='T' />
+ <param name='fdrtype' value='fdr' />
+ <param name='edgeR_priordf' value="8" />
+ <param name='fdrthresh' value="0.05" />
+ <param name='control_name' value='control' />
+ <param name='subjectids' value='' />
+ <param name='Treat_cols' value='3,4,5,9' />
+ <param name='Control_cols' value='2,6,7,8' />
+ <output name='out_edgeR' file='edgeRtest1out.xls' compare='diff' />
+ <output name='html_file' file='edgeRtest1out.html' compare='diff' lines_diff='20' />
+</test>
+</tests>
+
+<configfiles>
+<configfile name="runme">
+<![CDATA[
+#
+# edgeR.Rscript
+# updated npv 2011 for R 2.14.0 and edgeR 2.4.0 by ross
+# Performs DGE on a count table containing n replicates of two conditions
+#
+# Parameters
+#
+# 1 - Output Dir
+
+# Original edgeR code by: S.Lunke and A.Kaspi
+reallybig = log10(.Machine\$double.xmax)
+reallysmall = log10(.Machine\$double.xmin)
+library('stringr')
+library('gplots')
+library('edgeR')
+hmap2 = function(cmat,nsamp=100,outpdfname='heatmap2.pdf', TName='Treatment',group=NA,myTitle='title goes here')
+{
+# Perform clustering for significant pvalues after controlling FWER
+ samples = colnames(cmat)
+ gu = unique(group)
+ gn = rownames(cmat)
+ if (length(gu) == 2) {
+ col.map = function(g) {if (g==gu[1]) "#FF0000" else "#0000FF"}
+ pcols = unlist(lapply(group,col.map))
+ } else {
+ colours = rainbow(length(gu),start=0,end=4/6)
+ pcols = colours[match(group,gu)] }
+ dm = cmat[(! is.na(gn)),]
+ # remove unlabelled hm rows
+ nprobes = nrow(dm)
+ # sub = paste('Showing',nprobes,'contigs ranked for evidence of differential abundance')
+ if (nprobes > nsamp) {
+ dm =dm[1:nsamp,]
+ #sub = paste('Showing',nsamp,'contigs ranked for evidence for differential abundance out of',nprobes,'total')
+ }
+ newcolnames = substr(colnames(dm),1,20)
+ colnames(dm) = newcolnames
+ pdf(outpdfname)
+ heatmap.2(dm,main=myTitle,ColSideColors=pcols,col=topo.colors(100),dendrogram="col",key=T,density.info='none',
+ Rowv=F,scale='row',trace='none',margins=c(8,8),cexRow=0.4,cexCol=0.5)
+ dev.off()
+}
+
+hmap = function(cmat,nmeans=4,outpdfname="heatMap.pdf",nsamp=250,TName='Treatment',group=NA,myTitle="Title goes here")
+{
+ # for 2 groups only was
+ #col.map = function(g) {if (g==TName) "#FF0000" else "#0000FF"}
+ #pcols = unlist(lapply(group,col.map))
+ gu = unique(group)
+ colours = rainbow(length(gu),start=0.3,end=0.6)
+ pcols = colours[match(group,gu)]
+ nrows = nrow(cmat)
+ mtitle = paste(myTitle,'Heatmap: n contigs =',nrows)
+ if (nrows > nsamp) {
+ cmat = cmat[c(1:nsamp),]
+ mtitle = paste('Heatmap: Top ',nsamp,' DE contigs (of ',nrows,')',sep='')
+ }
+ newcolnames = substr(colnames(cmat),1,20)
+ colnames(cmat) = newcolnames
+ pdf(outpdfname)
+ heatmap(cmat,scale='row',main=mtitle,cexRow=0.3,cexCol=0.4,Rowv=NA,ColSideColors=pcols)
+ dev.off()
+}
+
+qqPlot = function(descr='qqplot',pvector, outpdf='qqplot.pdf',...)
+# stolen from https://gist.github.com/703512
+{
+ o = -log10(sort(pvector,decreasing=F))
+ e = -log10( 1:length(o)/length(o) )
+ o[o==-Inf] = reallysmall
+ o[o==Inf] = reallybig
+ maint = descr
+ pdf(outpdf)
+ plot(e,o,pch=19,cex=1, main=maint, ...,
+ xlab=expression(Expected~~-log[10](italic(p))),
+ ylab=expression(Observed~~-log[10](italic(p))),
+ xlim=c(0,max(e)), ylim=c(0,max(o)))
+ lines(e,e,col="red")
+ grid(col = "lightgray", lty = "dotted")
+ dev.off()
+}
+
+smearPlot = function(DGEList,deTags, outSmear, outMain)
+ {
+ pdf(outSmear)
+ plotSmear(DGEList,de.tags=deTags,main=outMain)
+ grid(col="lightgray", lty="dotted")
+ dev.off()
+ }
+
+boxPlot = function(rawrs,cleanrs,maint,myTitle,pdfname)
+{ #
+ nc = ncol(rawrs)
+ for (i in c(1:nc)) {rawrs[(rawrs[,i] < 0),i] = NA}
+ fullnames = colnames(rawrs)
+ newcolnames = substr(colnames(rawrs),1,20)
+ colnames(rawrs) = newcolnames
+ newcolnames = substr(colnames(cleanrs),1,20)
+ colnames(cleanrs) = newcolnames
+ defpar = par(no.readonly=T)
+ print.noquote('raw contig counts by sample:')
+ print.noquote(summary(rawrs))
+ print.noquote('normalised contig counts by sample:')
+ print.noquote(summary(cleanrs))
+ pdf(pdfname)
+ par(mfrow=c(1,2))
+ boxplot(rawrs,varwidth=T,notch=T,ylab='log contig count',col="maroon",las=3,cex.axis=0.35,main=paste('Raw:',maint))
+ grid(col="lightgray",lty="dotted")
+ boxplot(cleanrs,varwidth=T,notch=T,ylab='log contig count',col="maroon",las=3,cex.axis=0.35,main=paste('After ',maint))
+ grid(col="lightgray",lty="dotted")
+ dev.off()
+ pdfname = "sample_counts_histogram.pdf"
+ nc = ncol(rawrs)
+ print.noquote(paste('Using ncol rawrs=',nc))
+ ncroot = round(sqrt(nc))
+ if (ncroot*ncroot < nc) { ncroot = ncroot + 1 }
+ m = c()
+ for (i in c(1:nc)) {
+ rhist = hist(rawrs[,i],breaks=100,plot=F)
+ m = append(m,max(rhist\$counts))
+ }
+ ymax = max(m)
+ ncols = length(fullnames)
+ if (ncols > 20)
+ {
+ scale = 7*ncols/20
+ pdf(pdfname,width=scale,height=scale)
+ } else {
+ pdf(pdfname)
+ }
+ par(mfrow=c(ncroot,ncroot))
+ for (i in c(1:nc)) {
+ hist(rawrs[,i], main=paste("Contig logcount",i), xlab='log raw count', col="maroon",
+ breaks=100,sub=fullnames[i],cex=0.8,ylim=c(0,ymax))
+ }
+ dev.off()
+ par(defpar)
+
+}
+
+cumPlot = function(rawrs,cleanrs,maint,myTitle)
+{ # updated to use ecdf
+ pdfname = "Filtering_rowsum_bar_charts.pdf"
+ defpar = par(no.readonly=T)
+ lrs = log(rawrs,10)
+ lim = max(lrs)
+ pdf(pdfname)
+ par(mfrow=c(2,1))
+ hist(lrs,breaks=100,main=paste('Before:',maint),xlab="# Reads (log)",
+ ylab="Count",col="maroon",sub=myTitle, xlim=c(0,lim),las=1)
+ grid(col="lightgray", lty="dotted")
+ lrs = log(cleanrs,10)
+ hist(lrs,breaks=100,main=paste('After:',maint),xlab="# Reads (log)",
+ ylab="Count",col="maroon",sub=myTitle,xlim=c(0,lim),las=1)
+ grid(col="lightgray", lty="dotted")
+ dev.off()
+ par(defpar)
+}
+
+cumPlot1 = function(rawrs,cleanrs,maint,myTitle)
+{ # updated to use ecdf
+ pdfname = paste(gsub(" ","", myTitle , fixed=TRUE),"RowsumCum.pdf",sep='_')
+ pdf(pdfname)
+ par(mfrow=c(2,1))
+ lastx = max(rawrs)
+ rawe = knots(ecdf(rawrs))
+ cleane = knots(ecdf(cleanrs))
+ cy = 1:length(cleane)/length(cleane)
+ ry = 1:length(rawe)/length(rawe)
+ plot(rawe,ry,type='l',main=paste('Before',maint),xlab="Log Contig Total Reads",
+ ylab="Cumulative proportion",col="maroon",log='x',xlim=c(1,lastx),sub=myTitle)
+ grid(col="blue")
+ plot(cleane,cy,type='l',main=paste('After',maint),xlab="Log Contig Total Reads",
+ ylab="Cumulative proportion",col="maroon",log='x',xlim=c(1,lastx),sub=myTitle)
+ grid(col="blue")
+ dev.off()
+}
+
+
+
+doGSEAold = function(y=NULL,design=NULL,histgmt="",
+ bigmt="/data/genomes/gsea/3.1/Abetterchoice_nocgp_c2_c3_c5_symbols_all.gmt",
+ ntest=0, myTitle="myTitle", outfname="GSEA.xls", minnin=5, maxnin=2000,fdrthresh=0.05,fdrtype="BH")
+{
+ sink('Camera.log')
+ genesets = c()
+ if (bigmt > "")
+ {
+ bigenesets = readLines(bigmt)
+ genesets = bigenesets
+ }
+ if (histgmt > "")
+ {
+ hgenesets = readLines(histgmt)
+ if (bigmt > "") {
+ genesets = rbind(genesets,hgenesets)
+ } else {
+ genesets = hgenesets
+ } # use only history if no bi
+ }
+ print.noquote(paste("@@@read",length(genesets), 'genesets from',histgmt,bigmt))
+ genesets = strsplit(genesets,'\t') # tabular. genesetid\tURLorwhatever\tgene_1\t..\tgene_n
+ outf = outfname
+ head=paste(myTitle,'edgeR GSEA')
+ write(head,file=outfname,append=F)
+ ntest=length(genesets)
+ urownames = toupper(rownames(y))
+ upcam = c()
+ downcam = c()
+ for (i in 1:ntest) {
+ gs = unlist(genesets[i])
+ g = gs[1] # geneset_id
+ u = gs[2]
+ if (u > "") { u = paste("<a href=\'",u,"\'>",u,"</a>",sep="") }
+ glist = gs[3:length(gs)] # member gene symbols
+ glist = toupper(glist)
+ inglist = urownames %in% glist
+ nin = sum(inglist)
+ if ((nin > minnin) && (nin < maxnin)) {
+ ### print(paste('@@found',sum(inglist),'genes in glist'))
+ camres = camera(y=y,index=inglist,design=design)
+ if (! is.null(camres)) {
+ rownames(camres) = g # gene set name
+ camres = cbind(GeneSet=g,URL=u,camres)
+ if (camres\$Direction == "Up")
+ {
+ upcam = rbind(upcam,camres) } else {
+ downcam = rbind(downcam,camres)
+ }
+ }
+ }
+ }
+ uscam = upcam[order(upcam\$PValue),]
+ unadjp = uscam\$PValue
+ uscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
+ nup = max(10,sum((uscam\$adjPValue < fdrthresh)))
+ dscam = downcam[order(downcam\$PValue),]
+ unadjp = dscam\$PValue
+ dscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
+ ndown = max(10,sum((dscam\$adjPValue < fdrthresh)))
+ write.table(uscam,file=paste('camera_up',outfname,sep='_'),quote=F,sep='\t',row.names=F)
+ write.table(dscam,file=paste('camera_down',outfname,sep='_'),quote=F,sep='\t',row.names=F)
+ print.noquote(paste('@@@@@ Camera up top',nup,'gene sets:'))
+ write.table(head(uscam,nup),file="",quote=F,sep='\t',row.names=F)
+ print.noquote(paste('@@@@@ Camera down top',ndown,'gene sets:'))
+ write.table(head(dscam,ndown),file="",quote=F,sep='\t',row.names=F)
+ sink()
+}
+
+
+
+
+doGSEA = function(y=NULL,design=NULL,histgmt="",
+ bigmt="/data/genomes/gsea/3.1/Abetterchoice_nocgp_c2_c3_c5_symbols_all.gmt",
+ ntest=0, myTitle="myTitle", outfname="GSEA.xls", minnin=5, maxnin=2000,fdrthresh=0.05,fdrtype="BH")
+{
+ sink('Camera.log')
+ genesets = c()
+ if (bigmt > "")
+ {
+ bigenesets = readLines(bigmt)
+ genesets = bigenesets
+ }
+ if (histgmt > "")
+ {
+ hgenesets = readLines(histgmt)
+ if (bigmt > "") {
+ genesets = rbind(genesets,hgenesets)
+ } else {
+ genesets = hgenesets
+ } # use only history if no bi
+ }
+ print.noquote(paste("@@@read",length(genesets), 'genesets from',histgmt,bigmt))
+ genesets = strsplit(genesets,'\t') # tabular. genesetid\tURLorwhatever\tgene_1\t..\tgene_n
+ outf = outfname
+ head=paste(myTitle,'edgeR GSEA')
+ write(head,file=outfname,append=F)
+ ntest=length(genesets)
+ urownames = toupper(rownames(y))
+ upcam = c()
+ downcam = c()
+ incam = c()
+ urls = c()
+ gsids = c()
+ for (i in 1:ntest) {
+ gs = unlist(genesets[i])
+ gsid = gs[1] # geneset_id
+ url = gs[2]
+ if (url > "") { url = paste("<a href=\'",url,"\'>",url,"</a>",sep="") }
+ glist = gs[3:length(gs)] # member gene symbols
+ glist = toupper(glist)
+ inglist = urownames %in% glist
+ nin = sum(inglist)
+ if ((nin > minnin) && (nin < maxnin)) {
+ incam = c(incam,inglist)
+ gsids = c(gsids,gsid)
+ urls = c(urls,url)
+ }
+ }
+ incam = as.list(incam)
+ names(incam) = gsids
+ allcam = camera(y=y,index=incam,design=design)
+ allcamres = cbind(geneset=gsids,allcam,URL=urls)
+ for (i in 1:ntest) {
+ camres = allcamres[i]
+ res = try(test = (camres\$Direction == "Up"))
+ if ("try-error" %in% class(res)) {
+ cat("test failed, camres = :")
+ print.noquote(camres)
+ } else { if (camres\$Direction == "Up")
+ { upcam = rbind(upcam,camres)
+ } else { downcam = rbind(downcam,camres)
+ }
+
+ }
+ }
+ uscam = upcam[order(upcam\$PValue),]
+ unadjp = uscam\$PValue
+ uscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
+ nup = max(10,sum((uscam\$adjPValue < fdrthresh)))
+ dscam = downcam[order(downcam\$PValue),]
+ unadjp = dscam\$PValue
+ dscam\$adjPValue = p.adjust(unadjp,method=fdrtype)
+ ndown = max(10,sum((dscam\$adjPValue < fdrthresh)))
+ write.table(uscam,file=paste('camera_up',outfname,sep='_'),quote=F,sep='\t',row.names=F)
+ write.table(dscam,file=paste('camera_down',outfname,sep='_'),quote=F,sep='\t',row.names=F)
+ print.noquote(paste('@@@@@ Camera up top',nup,'gene sets:'))
+ write.table(head(uscam,nup),file="",quote=F,sep='\t',row.names=F)
+ print.noquote(paste('@@@@@ Camera down top',ndown,'gene sets:'))
+ write.table(head(dscam,ndown),file="",quote=F,sep='\t',row.names=F)
+ sink()
+ }
+
+
+edgeIt = function (Count_Matrix=c(),group=c(),out_edgeR=F,out_VOOM=F,out_DESeq2=F,fdrtype='fdr',priordf=5,
+ fdrthresh=0.05,outputdir='.', myTitle='Differential Counts',libSize=c(),useNDF=F,
+ filterquantile=0.2, subjects=c(),mydesign=NULL,
+ doDESeq2=T,doVoom=T,doCamera=T,doedgeR=T,org='hg19',
+ histgmt="", bigmt="/data/genomes/gsea/3.1/Abetterchoice_nocgp_c2_c3_c5_symbols_all.gmt",
+ doCook=F,DESeq_fitType="parameteric")
+{
+ # Error handling
+ if (length(unique(group))!=2){
+ print("Number of conditions identified in experiment does not equal 2")
+ q()
+ }
+ require(edgeR)
+ options(width = 512)
+ mt = paste(unlist(strsplit(myTitle,'_')),collapse=" ")
+ allN = nrow(Count_Matrix)
+ nscut = round(ncol(Count_Matrix)/2)
+ colTotmillionreads = colSums(Count_Matrix)/1e6
+ counts.dataframe = as.data.frame(c())
+ rawrs = rowSums(Count_Matrix)
+ nonzerod = Count_Matrix[(rawrs > 0),] # remove all zero count genes
+ nzN = nrow(nonzerod)
+ nzrs = rowSums(nonzerod)
+ zN = allN - nzN
+ print('# Quantiles for non-zero row counts:',quote=F)
+ print(quantile(nzrs,probs=seq(0,1,0.1)),quote=F)
+ if (useNDF == T)
+ {
+ gt1rpin3 = rowSums(Count_Matrix/expandAsMatrix(colTotmillionreads,dim(Count_Matrix)) >= 1) >= nscut
+ lo = colSums(Count_Matrix[!gt1rpin3,])
+ workCM = Count_Matrix[gt1rpin3,]
+ cleanrs = rowSums(workCM)
+ cleanN = length(cleanrs)
+ meth = paste( "After removing",length(lo),"contigs with fewer than ",nscut," sample read counts >= 1 per million, there are",sep="")
+ print(paste("Read",allN,"contigs. Removed",zN,"contigs with no reads.",meth,cleanN,"contigs"),quote=F)
+ maint = paste('Filter >=1/million reads in >=',nscut,'samples')
+ } else {
+ useme = (nzrs > quantile(nzrs,filterquantile))
+ workCM = nonzerod[useme,]
+ lo = colSums(nonzerod[!useme,])
+ cleanrs = rowSums(workCM)
+ cleanN = length(cleanrs)
+ meth = paste("After filtering at count quantile =",filterquantile,", there are",sep="")
+ print(paste('Read',allN,"contigs. Removed",zN,"with no reads.",meth,cleanN,"contigs"),quote=F)
+ maint = paste('Filter below',filterquantile,'quantile')
+ }
+ cumPlot(rawrs=rawrs,cleanrs=cleanrs,maint=maint,myTitle=myTitle)
+ allgenes = rownames(workCM)
+ reg = "^chr([0-9]+):([0-9]+)-([0-9]+)"
+ genecards="<a href=\'http://www.genecards.org/index.php?path=/Search/keyword/"
+ ucsc = paste("<a href=\'http://genome.ucsc.edu/cgi-bin/hgTracks?db=",org,sep='')
+ testreg = str_match(allgenes,reg)
+ if (sum(!is.na(testreg[,1]))/length(testreg[,1]) > 0.8) # is ucsc style string
+ {
+ print("@@ using ucsc substitution for urls")
+ contigurls = paste0(ucsc,"&position=chr",testreg[,2],":",testreg[,3],"-",testreg[,4],"\'>",allgenes,"</a>")
+ } else {
+ print("@@ using genecards substitution for urls")
+ contigurls = paste0(genecards,allgenes,"\'>",allgenes,"</a>")
+ }
+ print.noquote("# urls")
+ print.noquote(head(contigurls))
+ print(paste("# Total low count contigs per sample = ",paste(lo,collapse=',')),quote=F)
+ cmrowsums = rowSums(workCM)
+ TName=unique(group)[1]
+ CName=unique(group)[2]
+ if (is.null(mydesign)) {
+ if (length(subjects) == 0)
+ {
+ mydesign = model.matrix(~group)
+ }
+ else {
+ subjf = factor(subjects)
+ mydesign = model.matrix(~subjf+group) # we block on subject so make group last to simplify finding it
+ }
+ }
+ print.noquote(paste('Using samples:',paste(colnames(workCM),collapse=',')))
+ print.noquote('Using design matrix:')
+ print.noquote(mydesign)
+ if (doedgeR) {
+ sink('edgeR.log')
+ #### Setup DGEList object
+ DGEList = DGEList(counts=workCM, group = group)
+ DGEList = calcNormFactors(DGEList)
+
+ DGEList = estimateGLMCommonDisp(DGEList,mydesign)
+ comdisp = DGEList\$common.dispersion
+ DGEList = estimateGLMTrendedDisp(DGEList,mydesign)
+ if (edgeR_priordf > 0) {
+ print.noquote(paste("prior.df =",edgeR_priordf))
+ DGEList = estimateGLMTagwiseDisp(DGEList,mydesign,prior.df = edgeR_priordf)
+ } else {
+ DGEList = estimateGLMTagwiseDisp(DGEList,mydesign)
+ }
+ DGLM = glmFit(DGEList,design=mydesign)
+ DE = glmLRT(DGLM,coef=ncol(DGLM\$design)) # always last one - subject is first if needed
+ efflib = DGEList\$samples\$lib.size*DGEList\$samples\$norm.factors
+ normData = (1e+06*DGEList\$counts/efflib)
+ uoutput = cbind(
+ Name=as.character(rownames(DGEList\$counts)),
+ DE\$table,
+ adj.p.value=p.adjust(DE\$table\$PValue, method=fdrtype),
+ Dispersion=DGEList\$tagwise.dispersion,totreads=cmrowsums,normData,
+ DGEList\$counts
+ )
+ soutput = uoutput[order(DE\$table\$PValue),] # sorted into p value order - for quick toptable
+ goodness = gof(DGLM, pcutoff=fdrthresh)
+ if (sum(goodness\$outlier) > 0) {
+ print.noquote('GLM outliers:')
+ print(paste(rownames(DGLM)[(goodness\$outlier)],collapse=','),quote=F)
+ } else {
+ print('No GLM fit outlier genes found\n')
+ }
+ z = limma::zscoreGamma(goodness\$gof.statistic, shape=goodness\$df/2, scale=2)
+ pdf("edgeR_GoodnessofFit.pdf")
+ qq = qqnorm(z, panel.first=grid(), main="tagwise dispersion")
+ abline(0,1,lwd=3)
+ points(qq\$x[goodness\$outlier],qq\$y[goodness\$outlier], pch=16, col="maroon")
+ dev.off()
+ estpriorn = getPriorN(DGEList)
+ print(paste("Common Dispersion =",comdisp,"CV = ",sqrt(comdisp),"getPriorN = ",estpriorn),quote=F)
+ efflib = DGEList\$samples\$lib.size*DGEList\$samples\$norm.factors
+ normData = (1e+06*DGEList\$counts/efflib)
+ uniqueg = unique(group)
+ #### Plot MDS
+ sample_colors = match(group,levels(group))
+ sampleTypes = levels(factor(group))
+ print.noquote(sampleTypes)
+ pdf("edgeR_MDSplot.pdf")
+ plotMDS.DGEList(DGEList,main=paste("edgeR MDS for",myTitle),cex=0.5,col=sample_colors,pch=sample_colors)
+ legend(x="topleft", legend = sampleTypes,col=c(1:length(sampleTypes)), pch=19)
+ grid(col="blue")
+ dev.off()
+ colnames(normData) = paste( colnames(normData),'N',sep="_")
+ print(paste('Raw sample read totals',paste(colSums(nonzerod,na.rm=T),collapse=',')))
+ nzd = data.frame(log(nonzerod + 1e-2,10))
+ try( boxPlot(rawrs=nzd,cleanrs=log(normData,10),maint='TMM Normalisation',myTitle=myTitle,pdfname="edgeR_raw_norm_counts_box.pdf") )
+ write.table(soutput,file=out_edgeR, quote=FALSE, sep="\t",row.names=F)
+ tt = cbind(
+ Name=as.character(rownames(DGEList\$counts)),
+ DE\$table,
+ adj.p.value=p.adjust(DE\$table\$PValue, method=fdrtype),
+ Dispersion=DGEList\$tagwise.dispersion,totreads=cmrowsums
+ )
+ print.noquote("# edgeR Top tags\n")
+ tt = cbind(tt,URL=contigurls) # add to end so table isn't laid out strangely
+ tt = tt[order(DE\$table\$PValue),]
+ print.noquote(tt[1:50,])
+ deTags = rownames(uoutput[uoutput\$adj.p.value < fdrthresh,])
+ nsig = length(deTags)
+ print(paste('#',nsig,'tags significant at adj p=',fdrthresh),quote=F)
+ deColours = ifelse(deTags,'red','black')
+ pdf("edgeR_BCV_vs_abundance.pdf")
+ plotBCV(DGEList, cex=0.3, main="Biological CV vs abundance")
+ dev.off()
+ dg = DGEList[order(DE\$table\$PValue),]
+ #normData = (1e+06 * dg\$counts/expandAsMatrix(dg\$samples\$lib.size, dim(dg)))
+ efflib = dg\$samples\$lib.size*dg\$samples\$norm.factors
+ normData = (1e+06*dg\$counts/efflib)
+ outpdfname="edgeR_top_100_heatmap.pdf"
+ hmap2(normData,nsamp=100,TName=TName,group=group,outpdfname=outpdfname,myTitle=paste('edgeR Heatmap',myTitle))
+ outSmear = "edgeR_smearplot.pdf"
+ outMain = paste("Smear Plot for ",TName,' Vs ',CName,' (FDR@',fdrthresh,' N = ',nsig,')',sep='')
+ smearPlot(DGEList=DGEList,deTags=deTags, outSmear=outSmear, outMain = outMain)
+ qqPlot(descr=paste(myTitle,'edgeR adj p QQ plot'),pvector=tt\$adj.p.value,outpdf='edgeR_qqplot.pdf')
+ norm.factor = DGEList\$samples\$norm.factors
+ topresults.edgeR = soutput[which(soutput\$adj.p.value < fdrthresh), ]
+ edgeRcountsindex = which(allgenes %in% rownames(topresults.edgeR))
+ edgeRcounts = rep(0, length(allgenes))
+ edgeRcounts[edgeRcountsindex] = 1 # Create venn diagram of hits
+ sink()
+ } ### doedgeR
+ if (doDESeq2 == T)
+ {
+ sink("DESeq2.log")
+ # DESeq2
+ require('DESeq2')
+ library('RColorBrewer')
+ pdata = data.frame(Name=colnames(workCM),Rx=group,subjects=subjects,row.names=colnames(workCM))
+ if (length(subjects) == 0)
+ {
+ deSEQds = DESeqDataSetFromMatrix(countData = workCM, colData = pdata, design = formula(~ Rx))
+ } else {
+ deSEQds = DESeqDataSetFromMatrix(countData = workCM, colData = pdata, design = formula(~ subjects + Rx))
+ }
+ #DESeq2 = DESeq(deSEQds,fitType='local',pAdjustMethod=fdrtype)
+ #rDESeq = results(DESeq2)
+ #newCountDataSet(workCM, group)
+ deSeqDatsizefac = estimateSizeFactors(deSEQds)
+ deSeqDatdisp = estimateDispersions(deSeqDatsizefac,fitType=DESeq_fitType)
+ resDESeq = nbinomWaldTest(deSeqDatdisp, pAdjustMethod=fdrtype)
+ rDESeq = as.data.frame(results(resDESeq))
+ rDESeq = cbind(Contig=rownames(workCM),rDESeq,NReads=cmrowsums,URL=contigurls)
+ srDESeq = rDESeq[order(rDESeq\$pvalue),]
+ qqPlot(descr=paste(myTitle,'DESeq2 adj p qq plot'),pvector=rDESeq\$padj,outpdf='DESeq2_qqplot.pdf')
+ cat("# DESeq top 50\n")
+ print.noquote(srDESeq[1:50,])
+ write.table(srDESeq,file=out_DESeq2, quote=FALSE, sep="\t",row.names=F)
+ topresults.DESeq = rDESeq[which(rDESeq\$padj < fdrthresh), ]
+ DESeqcountsindex = which(allgenes %in% rownames(topresults.DESeq))
+ DESeqcounts = rep(0, length(allgenes))
+ DESeqcounts[DESeqcountsindex] = 1
+ pdf("DESeq2_dispersion_estimates.pdf")
+ plotDispEsts(resDESeq)
+ dev.off()
+ ysmall = abs(min(rDESeq\$log2FoldChange))
+ ybig = abs(max(rDESeq\$log2FoldChange))
+ ylimit = min(4,ysmall,ybig)
+ pdf("DESeq2_MA_plot.pdf")
+ plotMA(resDESeq,main=paste(myTitle,"DESeq2 MA plot"),ylim=c(-ylimit,ylimit))
+ dev.off()
+ rlogres = rlogTransformation(resDESeq)
+ sampledists = dist( t( assay(rlogres) ) )
+ sdmat = as.matrix(sampledists)
+ pdf("DESeq2_sample_distance_plot.pdf")
+ heatmap.2(sdmat,trace="none",main=paste(myTitle,"DESeq2 sample distances"),
+ col = colorRampPalette( rev(brewer.pal(9, "RdBu")) )(255))
+ dev.off()
+ ###outpdfname="DESeq2_top50_heatmap.pdf"
+ ###hmap2(sresDESeq,nsamp=50,TName=TName,group=group,outpdfname=outpdfname,myTitle=paste('DESeq2 vst rlog Heatmap',myTitle))
+ sink()
+ result = try( (ppca = plotPCA( varianceStabilizingTransformation(deSeqDatdisp,blind=T), intgroup=c("Rx","Name")) ) )
+ if ("try-error" %in% class(result)) {
+ print.noquote('DESeq2 plotPCA failed.')
+ } else {
+ pdf("DESeq2_PCA_plot.pdf")
+ #### wtf - print? Seems needed to get this to work
+ print(ppca)
+ dev.off()
+ }
+ }
+
+ if (doVoom == T) {
+ sink('VOOM.log')
+ if (doedgeR == F) {
+ #### Setup DGEList object
+ DGEList = DGEList(counts=workCM, group = group)
+ DGEList = calcNormFactors(DGEList)
+ DGEList = estimateGLMCommonDisp(DGEList,mydesign)
+ DGEList = estimateGLMTrendedDisp(DGEList,mydesign)
+ DGEList = estimateGLMTagwiseDisp(DGEList,mydesign)
+ DGEList = estimateGLMTagwiseDisp(DGEList,mydesign)
+ norm.factor = DGEList\$samples\$norm.factors
+ }
+ pdf("VOOM_mean_variance_plot.pdf")
+ dat.voomed = voom(DGEList, mydesign, plot = TRUE, lib.size = colSums(workCM) * norm.factor)
+ dev.off()
+ # Use limma to fit data
+ fit = lmFit(dat.voomed, mydesign)
+ fit = eBayes(fit)
+ rvoom = topTable(fit, coef = length(colnames(mydesign)), adj = fdrtype, n = Inf, sort="none")
+ qqPlot(descr=paste(myTitle,'VOOM-limma adj p QQ plot'),pvector=rvoom\$adj.P.Val,outpdf='VOOM_qqplot.pdf')
+ rownames(rvoom) = rownames(workCM)
+ rvoom = cbind(rvoom,NReads=cmrowsums,URL=contigurls)
+ srvoom = rvoom[order(rvoom\$P.Value),]
+ cat("# VOOM top 50\n")
+ print(srvoom[1:50,])
+ write.table(srvoom,file=out_VOOM, quote=FALSE, sep="\t",row.names=F)
+ # Use an FDR cutoff to find interesting samples for edgeR, DESeq and voom/limma
+ topresults.voom = rvoom[which(rvoom\$adj.P.Val < fdrthresh), ]
+ voomcountsindex = which(allgenes %in% topresults.voom\$ID)
+ voomcounts = rep(0, length(allgenes))
+ voomcounts[voomcountsindex] = 1
+ sink()
+ }
+
+ if (doCamera) {
+ doGSEA(y=DGEList,design=mydesign,histgmt=histgmt,bigmt=bigmt,ntest=20,myTitle=myTitle,
+ outfname=paste(mt,"GSEA.xls",sep="_"),fdrthresh=fdrthresh,fdrtype=fdrtype)
+ }
+
+ if ((doDESeq2==T) || (doVoom==T) || (doedgeR==T)) {
+ if ((doVoom==T) && (doDESeq2==T) && (doedgeR==T)) {
+ vennmain = paste(mt,'Voom,edgeR and DESeq2 overlap at FDR=',fdrthresh)
+ counts.dataframe = data.frame(edgeR = edgeRcounts, DESeq2 = DESeqcounts,
+ VOOM_limma = voomcounts, row.names = allgenes)
+ } else if ((doDESeq2==T) && (doedgeR==T)) {
+ vennmain = paste(mt,'DESeq2 and edgeR overlap at FDR=',fdrthresh)
+ counts.dataframe = data.frame(edgeR = edgeRcounts, DESeq2 = DESeqcounts, row.names = allgenes)
+ } else if ((doVoom==T) && (doedgeR==T)) {
+ vennmain = paste(mt,'Voom and edgeR overlap at FDR=',fdrthresh)
+ counts.dataframe = data.frame(edgeR = edgeRcounts, VOOM_limma = voomcounts, row.names = allgenes)
+ }
+
+ if (nrow(counts.dataframe > 1)) {
+ counts.venn = vennCounts(counts.dataframe)
+ vennf = "Venn_significant_genes_overlap.pdf"
+ pdf(vennf)
+ vennDiagram(counts.venn,main=vennmain,col="maroon")
+ dev.off()
+ }
+ } #### doDESeq2 or doVoom
+
+}
+#### Done
+
+###sink(stdout(),append=T,type="message")
+builtin_gmt = ""
+history_gmt = ""
+history_gmt_name = ""
+out_edgeR = F
+out_DESeq2 = F
+out_VOOM = "$out_VOOM"
+doDESeq2 = $DESeq2.doDESeq2 # make these T or F
+doVoom = $doVoom
+doCamera = $camera.doCamera
+doedgeR = $edgeR.doedgeR
+edgeR_priordf = 0
+
+
+#if $doVoom == "T":
+ out_VOOM = "$out_VOOM"
+#end if
+
+#if $DESeq2.doDESeq2 == "T":
+ out_DESeq2 = "$out_DESeq2"
+ DESeq_fitType = "$DESeq2.DESeq_fitType"
+#end if
+
+#if $edgeR.doedgeR == "T":
+ out_edgeR = "$out_edgeR"
+ edgeR_priordf = $edgeR.edgeR_priordf
+#end if
+
+#if $camera.doCamera == 'T'
+ #if $camera.gmtSource.refgmtSource == "indexed" or $camera.gmtSource.refgmtSource == "both":
+ builtin_gmt = "${camera.gmtSource.builtinGMT.fields.path}"
+ #end if
+ #if $camera.gmtSource.refgmtSource == "history" or $camera.gmtSource.refgmtSource == "both":
+ history_gmt = "${camera.gmtSource.ownGMT}"
+ history_gmt_name = "${camera.gmtSource.ownGMT.name}"
+ #end if
+#end if
+
+
+if (sum(c(doedgeR,doVoom,doDESeq2)) == 0)
+{
+write("No methods chosen - nothing to do! Please try again after choosing one or more methods", stderr())
+quit(save="no",status=2)
+}
+
+Out_Dir = "$html_file.files_path"
+Input = "$input1"
+TreatmentName = "$treatment_name"
+TreatmentCols = "$Treat_cols"
+ControlName = "$control_name"
+ControlCols= "$Control_cols"
+org = "$input1.dbkey"
+if (org == "") { org = "hg19"}
+fdrtype = "$fdrtype"
+fdrthresh = $fdrthresh
+useNDF = $useNDF
+fQ = $fQ # non-differential centile cutoff
+myTitle = "$title"
+sids = strsplit("$subjectids",',')
+subjects = unlist(sids)
+nsubj = length(subjects)
+TCols = as.numeric(strsplit(TreatmentCols,",")[[1]])-1
+CCols = as.numeric(strsplit(ControlCols,",")[[1]])-1
+cat('Got TCols=')
+cat(TCols)
+cat('; CCols=')
+cat(CCols)
+cat('\n')
+useCols = c(TCols,CCols)
+if (file.exists(Out_Dir) == F) dir.create(Out_Dir)
+Count_Matrix = read.table(Input,header=T,row.names=1,sep='\t') #Load tab file assume header
+snames = colnames(Count_Matrix)
+nsamples = length(snames)
+if (nsubj > 0 & nsubj != nsamples) {
+options("show.error.messages"=T)
+mess = paste('Fatal error: Supplied subject id list',paste(subjects,collapse=','),
+ 'has length',nsubj,'but there are',nsamples,'samples',paste(snames,collapse=','))
+write(mess, stderr())
+quit(save="no",status=4)
+}
+if (length(subjects) != 0) {subjects = subjects[useCols]}
+Count_Matrix = Count_Matrix[,useCols] ### reorder columns
+rn = rownames(Count_Matrix)
+islib = rn %in% c('librarySize','NotInBedRegions')
+LibSizes = Count_Matrix[subset(rn,islib),][1] # take first
+Count_Matrix = Count_Matrix[subset(rn,! islib),]
+group = c(rep(TreatmentName,length(TCols)), rep(ControlName,length(CCols)) ) #Build a group descriptor
+group = factor(group, levels=c(ControlName,TreatmentName))
+colnames(Count_Matrix) = paste(group,colnames(Count_Matrix),sep="_") #Relable columns
+results = edgeIt(Count_Matrix=Count_Matrix,group=group, out_edgeR=out_edgeR, out_VOOM=out_VOOM, out_DESeq2=out_DESeq2,
+ fdrtype='BH',mydesign=NULL,priordf=edgeR_priordf,fdrthresh=fdrthresh,outputdir='.',
+ myTitle=myTitle,useNDF=F,libSize=c(),filterquantile=fQ,subjects=subjects,
+ doDESeq2=doDESeq2,doVoom=doVoom,doCamera=doCamera,doedgeR=doedgeR,org=org,
+ histgmt=history_gmt,bigmt=builtin_gmt,DESeq_fitType=DESeq_fitType)
+sessionInfo()
+]]>
+</configfile>
+</configfiles>
+<help>
+
+**What it does**
+
+Allows short read sequence counts from controlled experiments to be analysed for differentially expressed genes.
+Optionally adds a term for subject if not all samples are independent or if some other factor needs to be blocked in the design.
+
+**Input**
+
+Requires a count matrix as a tabular file. These are best made using the companion HTSeq_ based counter Galaxy wrapper
+and your fave gene model to generate inputs. Each row is a genomic feature (gene or exon eg) and each column the
+non-negative integer count of reads from one sample overlapping the feature.
+The matrix must have a header row uniquely identifying the source samples, and unique row names in
+the first column. Typically the row names are gene symbols or probe ids for downstream use in GSEA and other methods.
+
+**Specifying comparisons**
+
+This is basically dumbed down for two factors - case vs control.
+
+More complex interfaces are possible but painful at present.
+Probably need to specify a phenotype file to do this better.
+Work in progress. Send code.
+
+If you have (eg) paired samples and wish to include a term in the GLM to account for some other factor (subject in the case of paired samples),
+put a comma separated list of indicators for every sample (whether modelled or not!) indicating (eg) the subject number or
+A list of integers, one for each subject or an empty string if samples are all independent.
+If not empty, there must be exactly as many integers in the supplied integer list as there are columns (samples) in the count matrix.
+Integers for samples that are not in the analysis *must* be present in the string as filler even if not used.
+
+So if you have 2 pairs out of 6 samples, you need to put in unique integers for the unpaired ones
+eg if you had 6 samples with the first two independent but the second and third pairs each being from independent subjects. you might use
+8,9,1,1,2,2
+as subject IDs to indicate two paired samples from the same subject in columns 3/4 and 5/6
+
+**Methods available**
+
+You can run 3 popular Bioconductor packages available for count data.
+
+edgeR - see edgeR_ for details
+
+VOOM/limma - see limma_VOOM_ for details
+
+DESeq2 - see DESeq2_ for details
+
+and optionally camera in edgeR which works better if MSigDB is installed.
+
+**Outputs**
+
+Some helpful plots and analysis results. Note that most of these are produced using R code
+suggested by the excellent documentation and vignettes for the Bioconductor
+packages invoked. The Tool Factory is used to automatically lay these out for you to enjoy.
+
+**Note on Voom**
+
+The voom from limma version 3.16.6 help in R includes this from the authors - but you should read the paper to interpret this method.
+
+This function is intended to process RNA-Seq or ChIP-Seq data prior to linear modelling in limma.
+
+voom is an acronym for mean-variance modelling at the observational level.
+The key concern is to estimate the mean-variance relationship in the data, then use this to compute appropriate weights for each observation.
+Count data almost show non-trivial mean-variance relationships. Raw counts show increasing variance with increasing count size, while log-counts typically show a decreasing mean-variance trend.
+This function estimates the mean-variance trend for log-counts, then assigns a weight to each observation based on its predicted variance.
+The weights are then used in the linear modelling process to adjust for heteroscedasticity.
+
+In an experiment, a count value is observed for each tag in each sample. A tag-wise mean-variance trend is computed using lowess.
+The tag-wise mean is the mean log2 count with an offset of 0.5, across samples for a given tag.
+The tag-wise variance is the quarter-root-variance of normalized log2 counts per million values with an offset of 0.5, across samples for a given tag.
+Tags with zero counts across all samples are not included in the lowess fit. Optional normalization is performed using normalizeBetweenArrays.
+Using fitted values of log2 counts from a linear model fit by lmFit, variances from the mean-variance trend were interpolated for each observation.
+This was carried out by approxfun. Inverse variance weights can be used to correct for mean-variance trend in the count data.
+
+
+Author(s)
+
+Charity Law and Gordon Smyth
+
+References
+
+Law, CW (2013). Precision weights for gene expression analysis. PhD Thesis. University of Melbourne, Australia.
+
+Law, CW, Chen, Y, Shi, W, Smyth, GK (2013). Voom! Precision weights unlock linear model analysis tools for RNA-seq read counts.
+Technical Report 1 May 2013, Bioinformatics Division, Walter and Eliza Hall Institute of Medical Reseach, Melbourne, Australia.
+http://www.statsci.org/smyth/pubs/VoomPreprint.pdf
+
+See Also
+
+A voom case study is given in the edgeR User's Guide.
+
+vooma is a similar function but for microarrays instead of RNA-seq.
+
+
+***old rant on changes to Bioconductor package variable names between versions***
+
+The edgeR authors made a small cosmetic change in the name of one important variable (from p.value to PValue)
+breaking this and all other code that assumed the old name for this variable,
+between edgeR2.4.4 and 2.4.6 (the version for R 2.14 as at the time of writing).
+This means that all code using edgeR is sensitive to the version. I think this was a very unwise thing
+to do because it wasted hours of my time to track down and will similarly cost other edgeR users dearly
+when their old scripts break. This tool currently now works with 2.4.6.
+
+**Note on prior.N**
+
+http://seqanswers.com/forums/showthread.php?t=5591 says:
+
+*prior.n*
+
+The value for prior.n determines the amount of smoothing of tagwise dispersions towards the common dispersion.
+You can think of it as like a "weight" for the common value. (It is actually the weight for the common likelihood
+in the weighted likelihood equation). The larger the value for prior.n, the more smoothing, i.e. the closer your
+tagwise dispersion estimates will be to the common dispersion. If you use a prior.n of 1, then that gives the
+common likelihood the weight of one observation.
+
+In answer to your question, it is a good thing to squeeze the tagwise dispersions towards a common value,
+or else you will be using very unreliable estimates of the dispersion. I would not recommend using the value that
+you obtained from estimateSmoothing()---this is far too small and would result in virtually no moderation
+(squeezing) of the tagwise dispersions. How many samples do you have in your experiment?
+What is the experimental design? If you have few samples (less than 6) then I would suggest a prior.n of at least 10.
+If you have more samples, then the tagwise dispersion estimates will be more reliable,
+so you could consider using a smaller prior.n, although I would hesitate to use a prior.n less than 5.
+
+
+From Bioconductor Digest, Vol 118, Issue 5, Gordon writes:
+
+Dear Dorota,
+
+The important settings are prior.df and trend.
+
+prior.n and prior.df are related through prior.df = prior.n * residual.df,
+and your experiment has residual.df = 36 - 12 = 24. So the old setting of
+prior.n=10 is equivalent for your data to prior.df = 240, a very large
+value. Going the other way, the new setting of prior.df=10 is equivalent
+to prior.n=10/24.
+
+To recover old results with the current software you would use
+
+ estimateTagwiseDisp(object, prior.df=240, trend="none")
+
+To get the new default from old software you would use
+
+ estimateTagwiseDisp(object, prior.n=10/24, trend=TRUE)
+
+Actually the old trend method is equivalent to trend="loess" in the new
+software. You should use plotBCV(object) to see whether a trend is
+required.
+
+Note you could also use
+
+ prior.n = getPriorN(object, prior.df=10)
+
+to map between prior.df and prior.n.
+
+----
+
+**Attributions**
+
+edgeR - edgeR_
+
+VOOM/limma - limma_VOOM_
+
+DESeq2 - DESeq2_ for details
+
+See above for Bioconductor package documentation for packages exposed in Galaxy by this tool and app store package.
+
+Galaxy_ (that's what you are using right now!) for gluing everything together
+
+Otherwise, all code and documentation comprising this tool was written by Ross Lazarus and is
+licensed to you under the LGPL_ like other rgenetics artefacts
+
+.. _LGPL: http://www.gnu.org/copyleft/lesser.html
+.. _HTSeq: http://www-huber.embl.de/users/anders/HTSeq/doc/index.html
+.. _edgeR: http://www.bioconductor.org/packages/release/bioc/html/edgeR.html
+.. _DESeq2: http://www.bioconductor.org/packages/release/bioc/html/DESeq2.html
+.. _limma_VOOM: http://www.bioconductor.org/packages/release/bioc/html/limma.html
+.. _Galaxy: http://getgalaxy.org
+</help>
+
+</tool>
+
+
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/91bece401d51/
Changeset: 91bece401d51
User: fubar
Date: 2013-08-17 01:55:18
Summary: Merged galaxy/galaxy-central into default
Affected #: 35 files
diff -r f830e3d7c6c096a9a840dad523540bdcc84327b2 -r 91bece401d51a8b9be8850208bd56fa838087a64 lib/galaxy/datatypes/dataproviders/base.py
--- a/lib/galaxy/datatypes/dataproviders/base.py
+++ b/lib/galaxy/datatypes/dataproviders/base.py
@@ -247,17 +247,22 @@
`offset`, begin providing datat, and stop when `num_data_returned`
is greater than `offset`.
"""
+ if self.limit != None and self.limit <= 0:
+ return
+ yield
+
parent_gen = super( LimitedOffsetDataProvider, self ).__iter__()
for datum in parent_gen:
-
- if self.limit != None and self.num_data_returned > self.limit:
- break
+ self.num_data_returned -= 1
+ #print 'self.num_data_returned:', self.num_data_returned
+ #print 'self.num_valid_data_read:', self.num_valid_data_read
if self.num_valid_data_read > self.offset:
+ self.num_data_returned += 1
yield datum
- else:
- # wot a cheezy way of doing this...
- self.num_data_returned -= 1
+
+ if self.limit != None and self.num_data_returned >= self.limit:
+ break
#TODO: skipping lines is inefficient - somehow cache file position/line_num pair and allow provider
# to seek to a pos/line and then begin providing lines
diff -r f830e3d7c6c096a9a840dad523540bdcc84327b2 -r 91bece401d51a8b9be8850208bd56fa838087a64 lib/galaxy/datatypes/dataproviders/dataset.py
--- a/lib/galaxy/datatypes/dataproviders/dataset.py
+++ b/lib/galaxy/datatypes/dataproviders/dataset.py
@@ -6,19 +6,13 @@
(e.g. parsing genomic regions from their source)
"""
+from galaxy import eggs
+
import pkg_resources
pkg_resources.require( 'bx-python' )
from bx import seq as bx_seq
from bx import wiggle as bx_wig
-import galaxy.model
-import galaxy.datatypes
-import galaxy.datatypes.data
-
-#TODO: can't import these due to circular ref in model/registry
-#import galaxy.datatypes.binary
-#import galaxy.datatypes.tabular
-
import exceptions
import base
import line
@@ -27,8 +21,9 @@
_TODO = """
use bx as much as possible
-the use of DatasetInstance seems to create some import problems
gff3 hierarchies
+
+change SamtoolsDataProvider to use pysam
"""
import logging
@@ -50,11 +45,8 @@
"""
:param dataset: the Galaxy dataset whose file will be the source
:type dataset: model.DatasetInstance
-
- :raises exceptions.InvalidDataProviderSource: if not a DatsetInstance
"""
- if not isinstance( dataset, galaxy.model.DatasetInstance ):
- raise exceptions.InvalidDataProviderSource( "Data provider can only be used with a DatasetInstance" )
+ #precondition: dataset is a galaxy.model.DatasetInstance
self.dataset = dataset
# this dataset file is obviously the source
#TODO: this might be a good place to interface with the object_store...
@@ -615,13 +607,7 @@
"""
#TODO: into validate_source
- #TODO: have to import these here due to circular ref in model/datatypes
- import galaxy.datatypes.binary
- import galaxy.datatypes.tabular
- if( not( isinstance( dataset.datatype, galaxy.datatypes.tabular.Sam )
- or isinstance( dataset.datatype, galaxy.datatypes.binary.Bam ) ) ):
- raise exceptions.InvalidDataProviderSource(
- 'dataset must be a Sam or Bam datatype: %s' %( str( dataset.datatype ) ) )
+ #precondition: dataset.datatype is a tabular.Sam or binary.Bam
self.dataset = dataset
options_dict = options_dict or {}
@@ -661,8 +647,9 @@
validated_flag_list = set([ flag for flag in options_string if flag in self.FLAGS_WO_ARGS ])
# if sam add -S
- if( ( isinstance( self.dataset.datatype, galaxy.datatypes.tabular.Sam )
- and ( 'S' not in validated_flag_list ) ) ):
+ #TODO: not the best test in the world...
+ if( ( self.dataset.ext == 'sam' )
+ and ( 'S' not in validated_flag_list ) ):
validated_flag_list.append( 'S' )
if validated_flag_list:
diff -r f830e3d7c6c096a9a840dad523540bdcc84327b2 -r 91bece401d51a8b9be8850208bd56fa838087a64 lib/galaxy/datatypes/dataproviders/line.py
--- a/lib/galaxy/datatypes/dataproviders/line.py
+++ b/lib/galaxy/datatypes/dataproviders/line.py
@@ -28,18 +28,25 @@
"""
DEFAULT_COMMENT_CHAR = '#'
settings = {
- 'string_lines' : 'bool',
+ 'strip_lines' : 'bool',
+ 'strip_newlines': 'bool',
'provide_blank' : 'bool',
'comment_char' : 'str',
}
- def __init__( self, source, strip_lines=True, provide_blank=False, comment_char=DEFAULT_COMMENT_CHAR, **kwargs ):
+ def __init__( self, source, strip_lines=True, strip_newlines=False, provide_blank=False,
+ comment_char=DEFAULT_COMMENT_CHAR, **kwargs ):
"""
:param strip_lines: remove whitespace from the beginning an ending
of each line (or not).
Optional: defaults to True
:type strip_lines: bool
+ :param strip_newlines: remove newlines only
+ (only functions when ``strip_lines`` is false)
+ Optional: defaults to False
+ :type strip_lines: bool
+
:param provide_blank: are empty lines considered valid and provided?
Optional: defaults to False
:type provide_blank: bool
@@ -51,6 +58,7 @@
"""
super( FilteredLineDataProvider, self ).__init__( source, **kwargs )
self.strip_lines = strip_lines
+ self.strip_newlines = strip_newlines
self.provide_blank = provide_blank
self.comment_char = comment_char
@@ -62,17 +70,18 @@
:type line: str
:returns: a line or `None`
"""
- line = super( FilteredLineDataProvider, self ).filter( line )
if line != None:
- # is this the proper order?
+ #??: shouldn't it strip newlines regardless, if not why not use on of the base.dprovs
if self.strip_lines:
line = line.strip()
+ elif self.strip_newlines:
+ line = line.strip( '\n' )
if not self.provide_blank and line == '':
return None
elif line.startswith( self.comment_char ):
return None
- return line
+ return super( FilteredLineDataProvider, self ).filter( line )
class RegexLineDataProvider( FilteredLineDataProvider ):
@@ -108,6 +117,7 @@
#NOTE: no support for flags
def filter( self, line ):
+ #NOTE: filter_fn will occur BEFORE any matching
line = super( RegexLineDataProvider, self ).filter( line )
if line != None and self.compiled_regex_list:
line = self.filter_by_regex( line )
@@ -144,16 +154,15 @@
:type block_filter_fn: function
"""
# composition - not inheritance
- #TODO: don't pass any?
- line_provider = FilteredLineDataProvider( source )
- super( BlockDataProvider, self ).__init__( line_provider, **kwargs )
+ #TODO: not a fan of this:
+ ( filter_fn, limit, offset ) = ( kwargs.pop( 'filter_fn', None ),
+ kwargs.pop( 'limit', None ), kwargs.pop( 'offset', 0 ) )
+ line_provider = FilteredLineDataProvider( source, **kwargs )
+ super( BlockDataProvider, self ).__init__( line_provider, filter_fn=filter_fn, limit=limit, offset=offset )
self.new_block_delim_fn = new_block_delim_fn
self.block_filter_fn = block_filter_fn
self.init_new_block()
- # ...well, this is kinda lame - but prevents returning first empty block
- #TODO: maybe better way in iter
- self.is_inside_block = False
def init_new_block( self ):
"""
@@ -161,7 +170,6 @@
"""
# called in __init__ and after yielding the prev. block
self.block_lines = collections.deque([])
- self.block = {}
def __iter__( self ):
"""
@@ -171,8 +179,8 @@
for block in parent_gen:
yield block
- last_block = self.filter_block( self.assemble_current_block() )
- if last_block != None and self.num_data_returned < self.limit:
+ last_block = self.handle_last_block()
+ if last_block != None:
self.num_data_returned += 1
yield last_block
@@ -186,26 +194,23 @@
:returns: a block or `None`
"""
line = super( BlockDataProvider, self ).filter( line )
+ #HACK
+ self.num_data_read -= 1
if line == None:
return None
+ block_to_return = None
if self.is_new_block( line ):
# if we're already in a block, return the prev. block and add the line to a new block
- #TODO: maybe better way in iter
- if self.is_inside_block:
- filtered_block = self.filter_block( self.assemble_current_block() )
+ if self.block_lines:
+ block_to_return = self.assemble_current_block()
+ block_to_return = self.filter_block( block_to_return )
+ self.num_data_read += 1
+
self.init_new_block()
- self.add_line_to_block( line )
-
- # return an assembled block datum if it passed the filter
- if filtered_block != None:
- return filtered_block
-
- else:
- self.is_inside_block = True
self.add_line_to_block( line )
- return None
+ return block_to_return
def is_new_block( self, line ):
"""
@@ -239,7 +244,6 @@
Called per block (just before providing).
"""
# empty block_lines and assemble block
- # NOTE: we don't want to have mem == 2*data here so - careful
return list( ( self.block_lines.popleft() for i in xrange( len( self.block_lines ) ) ) )
def filter_block( self, block ):
@@ -251,3 +255,20 @@
if self.block_filter_fn:
return self.block_filter_fn( block )
return block
+
+ def handle_last_block( self ):
+ """
+ Handle any blocks remaining after the main loop.
+ """
+ if self.limit != None and self.num_data_returned >= self.limit:
+ return None
+
+ last_block = self.assemble_current_block()
+ self.num_data_read += 1
+
+ last_block = self.filter_block( last_block )
+ if last_block != None:
+ self.num_valid_data_read += 1
+
+ return last_block
+
diff -r f830e3d7c6c096a9a840dad523540bdcc84327b2 -r 91bece401d51a8b9be8850208bd56fa838087a64 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -94,6 +94,7 @@
class ToolBox( object ):
"""Container for a collection of tools"""
+
def __init__( self, config_filenames, tool_root_dir, app ):
"""
Create a toolbox from the config files named by `config_filenames`, using
@@ -144,6 +145,7 @@
# config files, adding or removing locally developed tools or workflows. The value of integrated_tool_panel
# will be False when things like functional tests are the caller.
self.write_integrated_tool_panel_config_file()
+
def init_tools( self, config_filename ):
"""
Read the configuration file and load each tool. The following tags are currently supported:
@@ -199,11 +201,13 @@
tool_path=tool_path,
config_elems=config_elems )
self.shed_tool_confs.append( shed_tool_conf_dict )
+
def get_shed_config_dict_by_filename( self, filename, default=None ):
for shed_config_dict in self.shed_tool_confs:
if shed_config_dict[ 'config_filename' ] == filename:
return shed_config_dict
return default
+
def __add_tool_to_tool_panel( self, tool_id, panel_component, section=False ):
# See if a version of this tool is already loaded into the tool panel. The value of panel_component
# will be a ToolSection (if the value of section=True) or self.tool_panel (if section=False).
@@ -239,6 +243,7 @@
del panel_dict[ loaded_version_key ]
panel_dict.insert( index, key, tool )
log.debug( "Loaded tool id: %s, version: %s into tool panel." % ( tool.id, tool.version ) )
+
def load_tool_panel( self ):
for key, val in self.integrated_tool_panel.items():
if key.startswith( 'tool_' ):
@@ -276,6 +281,7 @@
section.elems[ section_key ] = section_val
log.debug( "Loaded label: %s" % ( section_val.text ) )
self.tool_panel[ key ] = section
+
def load_integrated_tool_panel_keys( self ):
"""
Load the integrated tool panel keys, setting values for tools and workflows to None. The values will
@@ -308,6 +314,7 @@
elif elem.tag == 'label':
key = 'label_%s' % elem.get( 'id' )
self.integrated_tool_panel[ key ] = None
+
def write_integrated_tool_panel_config_file( self ):
"""
Write the current in-memory version of the integrated_tool_panel.xml file to disk. Since Galaxy administrators
@@ -350,6 +357,7 @@
os.close( fd )
shutil.move( filename, os.path.abspath( self.integrated_tool_panel_config ) )
os.chmod( self.integrated_tool_panel_config, 0644 )
+
def get_tool( self, tool_id, tool_version=None, get_all_versions=False ):
"""Attempt to locate a tool in the tool box."""
if tool_id in self.tools_by_id and not get_all_versions:
@@ -380,6 +388,7 @@
#No tool matches by version, simply return the first available tool found
return rval[0]
return None
+
def get_loaded_tools_by_lineage( self, tool_id ):
"""Get all loaded tools associated by lineage to the tool whose id is tool_id."""
tv = self.__get_tool_version( tool_id )
@@ -395,6 +404,7 @@
tool = self.tools_by_id[ tool_id ]
return [ tool ]
return []
+
def __get_tool_version( self, tool_id ):
"""Return a ToolVersion if one exists for the tool_id"""
return self.sa_session.query( self.app.model.ToolVersion ) \
@@ -527,6 +537,7 @@
integrated_panel_dict.insert( index, key, tool )
except:
log.exception( "Error reading tool from path: %s" % path )
+
def load_workflow_tag_set( self, elem, panel_dict, integrated_panel_dict, load_panel_dict, index=None ):
try:
# TODO: should id be encoded?
@@ -543,6 +554,7 @@
integrated_panel_dict.insert( index, key, workflow )
except:
log.exception( "Error loading workflow: %s" % workflow_id )
+
def load_label_tag_set( self, elem, panel_dict, integrated_panel_dict, load_panel_dict, index=None ):
label = ToolSectionLabel( elem )
key = 'label_' + label.id
@@ -552,6 +564,7 @@
integrated_panel_dict[ key ] = label
else:
integrated_panel_dict.insert( index, key, label )
+
def load_section_tag_set( self, elem, tool_path, load_panel_dict, index=None ):
key = 'section_' + elem.get( "id" )
if key in self.tool_panel:
@@ -580,6 +593,7 @@
self.integrated_tool_panel[ key ] = integrated_section
else:
self.integrated_tool_panel.insert( index, key, integrated_section )
+
def load_tool( self, config_file, guid=None, repository_id=None, **kwds ):
"""Load a single tool from the file named by `config_file` and return an instance of `Tool`."""
# Parse XML configuration file and get the root element
@@ -597,6 +611,7 @@
else:
ToolClass = Tool
return ToolClass( config_file, root, self.app, guid=guid, repository_id=repository_id, **kwds )
+
def reload_tool_by_id( self, tool_id ):
"""
Attempt to reload the tool identified by 'tool_id', if successful
@@ -634,6 +649,7 @@
message += "<b>version:</b> %s" % old_tool.version
status = 'done'
return message, status
+
def remove_tool_by_id( self, tool_id ):
"""
Attempt to remove the tool identified by 'tool_id'.
@@ -662,6 +678,7 @@
message += "<b>version:</b> %s" % tool.version
status = 'done'
return message, status
+
def load_workflow( self, workflow_id ):
"""
Return an instance of 'Workflow' identified by `id`,
@@ -670,11 +687,13 @@
id = self.app.security.decode_id( workflow_id )
stored = self.app.model.context.query( self.app.model.StoredWorkflow ).get( id )
return stored.latest_workflow
+
def init_dependency_manager( self ):
if self.app.config.use_tool_dependencies:
self.dependency_manager = DependencyManager( [ self.app.config.tool_dependency_dir ] )
else:
self.dependency_manager = None
+
@property
def sa_session( self ):
"""
@@ -683,9 +702,10 @@
return self.app.model.context
def to_dict( self, trans, in_panel=True, **kwds ):
- #
- # Dictify toolbox.
- #
+ """
+ Dictify toolbox.
+ """
+
context = Bunch( toolbox=self, trans=trans, **kwds )
if in_panel:
panel_elts = [ val for val in self.tool_panel.itervalues() ]
@@ -705,8 +725,8 @@
rval.append( elt.to_dict( trans, for_link=True ) )
else:
tools = []
- for id, tool in self.toolbox.tools_by_id.items():
- tools.append( tool.to_dict( trans ) )
+ for id, tool in self.tools_by_id.items():
+ tools.append( tool.to_dict( trans, for_link=True ) )
rval = tools
return rval
diff -r f830e3d7c6c096a9a840dad523540bdcc84327b2 -r 91bece401d51a8b9be8850208bd56fa838087a64 lib/galaxy/tools/actions/history_imp_exp.py
--- a/lib/galaxy/tools/actions/history_imp_exp.py
+++ b/lib/galaxy/tools/actions/history_imp_exp.py
@@ -1,7 +1,7 @@
-import tempfile
+import tempfile, os
from __init__ import ToolAction
from galaxy.util.odict import odict
-from galaxy.tools.imp_exp import *
+from galaxy.tools.imp_exp import JobImportHistoryArchiveWrapper, JobExportHistoryArchiveWrapper
import logging
log = logging.getLogger( __name__ )
diff -r f830e3d7c6c096a9a840dad523540bdcc84327b2 -r 91bece401d51a8b9be8850208bd56fa838087a64 lib/galaxy/tools/imp_exp/__init__.py
--- a/lib/galaxy/tools/imp_exp/__init__.py
+++ b/lib/galaxy/tools/imp_exp/__init__.py
@@ -26,7 +26,7 @@
<data format="gzip" name="output_file"/></outputs></tool>
- """
+ """
# Load export tool.
tmp_name = tempfile.NamedTemporaryFile()
diff -r f830e3d7c6c096a9a840dad523540bdcc84327b2 -r 91bece401d51a8b9be8850208bd56fa838087a64 scripts/cleanup_datasets/cleanup_datasets.py
--- a/scripts/cleanup_datasets/cleanup_datasets.py
+++ b/scripts/cleanup_datasets/cleanup_datasets.py
@@ -175,7 +175,7 @@
app.model.History.table.c.update_time < cutoff_time ) ) \
.options( eagerload( 'datasets' ) )
for history in histories:
- print "### Processing history id %d (%s)" % (history.id, history.name)
+ print ("### Processing history id %d (%s)" % (history.id, history.name)).encode('utf-8')
for dataset_assoc in history.datasets:
_purge_dataset_instance( dataset_assoc, app, remove_from_disk, info_only = info_only ) #mark a DatasetInstance as deleted, clear associated files, and mark the Dataset as deleted if it is deletable
if not info_only:
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/0bdee4933794/
Changeset: 0bdee4933794
User: fubar
Date: 2013-08-29 05:36:22
Summary: Merged galaxy/galaxy-central into default
Affected #: 142 files
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 .hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -3,3 +3,4 @@
75f09617abaadbc8cc732bb8ee519decaeb56ea7 release_2013.04.01
2cc8d10988e03257dc7b97f8bb332c7df745d1dd security_2013.04.08
524f246ca85395082719ae7a6ff72260d7ad5612 release_2013.06.03
+1ae95b3aa98d1ccf15b243ac3ce6a895eb7efc53 release_2013.08.12
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 config/plugins/visualizations/scatterplot/templates/scatterplot.mako
--- a/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
+++ b/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
@@ -41,7 +41,7 @@
<script type="text/javascript">
$(function(){
- var hda = ${h.to_json_string( trans.security.encode_dict_ids( hda.get_api_value() ) )},
+ var hda = ${h.to_json_string( trans.security.encode_dict_ids( hda.dictify() ) )},
querySettings = ${h.to_json_string( query_args )},
chartConfig = _.extend( querySettings, {
containerSelector : '#chart',
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 datatypes_conf.xml.sample
--- a/datatypes_conf.xml.sample
+++ b/datatypes_conf.xml.sample
@@ -285,9 +285,6 @@
<sniffer type="galaxy.datatypes.tabular:Sam"/><sniffer type="galaxy.datatypes.data:Newick"/><sniffer type="galaxy.datatypes.data:Nexus"/>
- <sniffer type="galaxy.datatypes.graph:Xgmml"/>
- <sniffer type="galaxy.datatypes.graph:Sif"/>
- <sniffer type="galaxy.datatypes.graph:Rdf"/><sniffer type="galaxy.datatypes.images:Jpg"/><sniffer type="galaxy.datatypes.images:Png"/><sniffer type="galaxy.datatypes.images:Tiff"/>
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 eggs.ini
--- a/eggs.ini
+++ b/eggs.ini
@@ -18,6 +18,7 @@
MarkupSafe = 0.12
mercurial = 2.2.3
MySQL_python = 1.2.3c1
+PyRods = 3.2.4
numpy = 1.6.0
pbs_python = 4.3.5
psycopg2 = 2.0.13
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -210,6 +210,7 @@
if self.nginx_upload_store:
self.nginx_upload_store = os.path.abspath( self.nginx_upload_store )
self.object_store = kwargs.get( 'object_store', 'disk' )
+ self.object_store_cache_path = resolve_path( kwargs.get( "object_store_cache_path", "database/object_store_cache" ), self.root )
# Handle AWS-specific config options for backward compatibility
if kwargs.get( 'aws_access_key', None) is not None:
self.os_access_key= kwargs.get( 'aws_access_key', None )
@@ -229,6 +230,8 @@
self.distributed_object_store_config_file = kwargs.get( 'distributed_object_store_config_file', None )
if self.distributed_object_store_config_file is not None:
self.distributed_object_store_config_file = resolve_path( self.distributed_object_store_config_file, self.root )
+ self.irods_root_collection_path = kwargs.get( 'irods_root_collection_path', None )
+ self.irods_default_resource = kwargs.get( 'irods_default_resource', None )
# Parse global_conf and save the parser
global_conf = kwargs.get( 'global_conf', None )
global_conf_parser = ConfigParser.ConfigParser()
@@ -370,6 +373,7 @@
self.nginx_upload_store, \
'./static/genetrack/plots', \
self.whoosh_index_dir, \
+ self.object_store_cache_path, \
os.path.join( self.tool_data_path, 'shared', 'jars' ):
if path not in [ None, False ] and not os.path.isdir( path ):
try:
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 lib/galaxy/eggs/__init__.py
--- a/lib/galaxy/eggs/__init__.py
+++ b/lib/galaxy/eggs/__init__.py
@@ -387,7 +387,8 @@
"guppy": lambda: self.config.get( "app:main", "use_memdump" ),
"python_openid": lambda: self.config.get( "app:main", "enable_openid" ),
"python_daemon": lambda: sys.version_info[:2] >= ( 2, 5 ),
- "pysam": lambda: check_pysam()
+ "pysam": lambda: check_pysam(),
+ "PyRods": lambda: self.config.get( "app:main", "object_store" ) == "irods"
}.get( egg_name, lambda: True )()
except:
return False
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 lib/galaxy/jobs/actions/post.py
--- a/lib/galaxy/jobs/actions/post.py
+++ b/lib/galaxy/jobs/actions/post.py
@@ -242,8 +242,8 @@
}
"""
@classmethod
- def get_short_str(cls, trans):
- return "Hide this dataset."
+ def get_short_str(cls, pja):
+ return "Hide output '%s'." % pja.output_name
class DeleteDatasetAction(DefaultJobAction):
# This is disabled for right now. Deleting a dataset in the middle of a workflow causes errors (obviously) for the subsequent steps using the data.
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -23,7 +23,7 @@
import galaxy.datatypes.registry
import galaxy.security.passwords
from galaxy.datatypes.metadata import MetadataCollection
-from galaxy.model.item_attrs import APIItem, UsesAnnotations
+from galaxy.model.item_attrs import DictifiableMixin, UsesAnnotations
from galaxy.security import get_permitted_actions
from galaxy.util import is_multi_byte, nice_size, Params, restore_text, send_mail
from galaxy.util.bunch import Bunch
@@ -61,16 +61,16 @@
datatypes_registry = d_registry
-class User( object, APIItem ):
+class User( object, DictifiableMixin ):
use_pbkdf2 = True
"""
Data for a Galaxy user or admin and relations to their
histories, credentials, and roles.
"""
- # attributes that will be accessed and returned when calling get_api_value( view='collection' )
- api_collection_visible_keys = ( 'id', 'email' )
- # attributes that will be accessed and returned when calling get_api_value( view='element' )
- api_element_visible_keys = ( 'id', 'email', 'username', 'total_disk_usage', 'nice_total_disk_usage' )
+ # attributes that will be accessed and returned when calling dictify( view='collection' )
+ dict_collection_visible_keys = ( 'id', 'email' )
+ # attributes that will be accessed and returned when calling dictify( view='element' )
+ dict_element_visible_keys = ( 'id', 'email', 'username', 'total_disk_usage', 'nice_total_disk_usage' )
def __init__( self, email=None, password=None ):
self.email = email
@@ -157,9 +157,9 @@
return total
-class Job( object, APIItem ):
- api_collection_visible_keys = [ 'id' ]
- api_element_visible_keys = [ 'id' ]
+class Job( object, DictifiableMixin ):
+ dict_collection_visible_keys = [ 'id' ]
+ dict_element_visible_keys = [ 'id' ]
"""
A job represents a request to run a tool given input datasets, tool
@@ -363,8 +363,8 @@
dataset.blurb = 'deleted'
dataset.peek = 'Job deleted'
dataset.info = 'Job output deleted by user before job completed'
- def get_api_value( self, view='collection' ):
- rval = super( Job, self ).get_api_value( view=view )
+ def dictify( self, view='collection' ):
+ rval = super( Job, self ).dictify( view=view )
rval['tool_name'] = self.tool_id
param_dict = dict( [ ( p.name, p.value ) for p in self.parameters ] )
rval['params'] = param_dict
@@ -649,9 +649,9 @@
else:
return False
-class Group( object, APIItem ):
- api_collection_visible_keys = ( 'id', 'name' )
- api_element_visible_keys = ( 'id', 'name' )
+class Group( object, DictifiableMixin ):
+ dict_collection_visible_keys = ( 'id', 'name' )
+ dict_element_visible_keys = ( 'id', 'name' )
def __init__( self, name = None ):
self.name = name
@@ -662,10 +662,10 @@
self.user = user
self.group = group
-class History( object, UsesAnnotations ):
+class History( object, DictifiableMixin, UsesAnnotations ):
- api_collection_visible_keys = ( 'id', 'name', 'published', 'deleted' )
- api_element_visible_keys = ( 'id', 'name', 'published', 'deleted', 'genome_build', 'purged' )
+ dict_collection_visible_keys = ( 'id', 'name', 'published', 'deleted' )
+ dict_element_visible_keys = ( 'id', 'name', 'published', 'deleted', 'genome_build', 'purged' )
default_name = 'Unnamed history'
def __init__( self, id=None, name=None, user=None ):
@@ -680,6 +680,7 @@
self.user = user
self.datasets = []
self.galaxy_sessions = []
+ self.tags = []
def _next_hid( self ):
# TODO: override this with something in the database that ensures
@@ -779,45 +780,34 @@
history_name = unicode(history_name, 'utf-8')
return history_name
- def get_api_value( self, view='collection', value_mapper = None ):
- if value_mapper is None:
- value_mapper = {}
- rval = {}
+ def dictify( self, view='collection', value_mapper = None ):
- try:
- visible_keys = self.__getattribute__( 'api_' + view + '_visible_keys' )
- except AttributeError:
- raise Exception( 'Unknown API view: %s' % view )
- for key in visible_keys:
- try:
- rval[key] = self.__getattribute__( key )
- if key in value_mapper:
- rval[key] = value_mapper.get( key )( rval[key] )
- except AttributeError:
- rval[key] = None
-
+ # Get basic value.
+ rval = super( History, self ).dictify( view=view, value_mapper=value_mapper )
+
+ # Add tags.
tags_str_list = []
for tag in self.tags:
tag_str = tag.user_tname
if tag.value is not None:
tag_str += ":" + tag.user_value
tags_str_list.append( tag_str )
- rval['tags'] = tags_str_list
- rval['model_class'] = self.__class__.__name__
+ rval[ 'tags' ] = tags_str_list
+
return rval
def set_from_dict( self, new_data ):
#AKA: set_api_value
"""
Set object attributes to the values in dictionary new_data limiting
- to only those keys in api_element_visible_keys.
+ to only those keys in dict_element_visible_keys.
Returns a dictionary of the keys, values that have been changed.
"""
# precondition: keys are proper, values are parsed and validated
changed = {}
# unknown keys are ignored here
- for key in [ k for k in new_data.keys() if k in self.api_element_visible_keys ]:
+ for key in [ k for k in new_data.keys() if k in self.dict_element_visible_keys ]:
new_val = new_data[ key ]
old_val = self.__getattribute__( key )
if new_val == old_val:
@@ -879,9 +869,9 @@
self.group = group
self.role = role
-class Role( object, APIItem ):
- api_collection_visible_keys = ( 'id', 'name' )
- api_element_visible_keys = ( 'id', 'name', 'description', 'type' )
+class Role( object, DictifiableMixin ):
+ dict_collection_visible_keys = ( 'id', 'name' )
+ dict_element_visible_keys = ( 'id', 'name', 'description', 'type' )
private_id = None
types = Bunch(
PRIVATE = 'private',
@@ -896,21 +886,21 @@
self.type = type
self.deleted = deleted
-class UserQuotaAssociation( object, APIItem ):
- api_element_visible_keys = ( 'user', )
+class UserQuotaAssociation( object, DictifiableMixin ):
+ dict_element_visible_keys = ( 'user', )
def __init__( self, user, quota ):
self.user = user
self.quota = quota
-class GroupQuotaAssociation( object, APIItem ):
- api_element_visible_keys = ( 'group', )
+class GroupQuotaAssociation( object, DictifiableMixin ):
+ dict_element_visible_keys = ( 'group', )
def __init__( self, group, quota ):
self.group = group
self.quota = quota
-class Quota( object, APIItem ):
- api_collection_visible_keys = ( 'id', 'name' )
- api_element_visible_keys = ( 'id', 'name', 'description', 'bytes', 'operation', 'display_amount', 'default', 'users', 'groups' )
+class Quota( object, DictifiableMixin ):
+ dict_collection_visible_keys = ( 'id', 'name' )
+ dict_element_visible_keys = ( 'id', 'name', 'description', 'bytes', 'operation', 'display_amount', 'default', 'users', 'groups' )
valid_operations = ( '+', '-', '=' )
def __init__( self, name="", description="", amount=0, operation="=" ):
self.name = name
@@ -937,8 +927,8 @@
else:
return nice_size( self.bytes )
-class DefaultQuotaAssociation( Quota, APIItem ):
- api_element_visible_keys = ( 'type', )
+class DefaultQuotaAssociation( Quota, DictifiableMixin ):
+ dict_element_visible_keys = ( 'type', )
types = Bunch(
UNREGISTERED = 'unregistered',
REGISTERED = 'registered'
@@ -1518,7 +1508,7 @@
return msg
-class HistoryDatasetAssociation( DatasetInstance, UsesAnnotations ):
+class HistoryDatasetAssociation( DatasetInstance, DictifiableMixin, UsesAnnotations ):
"""
Resource class that creates a relation between a dataset and a user history.
"""
@@ -1690,7 +1680,7 @@
rval += child.get_disk_usage( user )
return rval
- def get_api_value( self, view='collection' ):
+ def dictify( self, view='collection' ):
"""
Return attributes of this HDA that are exposed using the API.
"""
@@ -1769,10 +1759,10 @@
self.subset = subset
self.location = location
-class Library( object, APIItem ):
+class Library( object, DictifiableMixin ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
- api_collection_visible_keys = ( 'id', 'name' )
- api_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
+ dict_collection_visible_keys = ( 'id', 'name' )
+ dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
@@ -1838,8 +1828,8 @@
name = unicode( name, 'utf-8' )
return name
-class LibraryFolder( object, APIItem ):
- api_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
+class LibraryFolder( object, DictifiableMixin ):
+ dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
def __init__( self, name=None, description=None, item_count=0, order_id=None ):
self.name = name or "Unnamed folder"
self.description = description
@@ -1910,8 +1900,8 @@
if isinstance( name, str ):
name = unicode( name, 'utf-8' )
return name
- def get_api_value( self, view='collection' ):
- rval = super( LibraryFolder, self ).get_api_value( view=view )
+ def dictify( self, view='collection' ):
+ rval = super( LibraryFolder, self ).dictify( view=view )
info_association, inherited = self.get_info_association()
if info_association:
if inherited:
@@ -1976,7 +1966,7 @@
name = property( get_name, set_name )
def display_name( self ):
self.library_dataset_dataset_association.display_name()
- def get_api_value( self, view='collection' ):
+ def dictify( self, view='collection' ):
# Since this class is a proxy to rather complex attributes we want to
# display in other objects, we can't use the simpler method used by
# other model classes.
@@ -2106,7 +2096,7 @@
if restrict:
return None, inherited
return self.library_dataset.folder.get_info_association( inherited=True )
- def get_api_value( self, view='collection' ):
+ def dictify( self, view='collection' ):
# Since this class is a proxy to rather complex attributes we want to
# display in other objects, we can't use the simpler method used by
# other model classes.
@@ -2333,9 +2323,9 @@
self.id = None
self.user = None
-class StoredWorkflow( object, APIItem):
- api_collection_visible_keys = ( 'id', 'name', 'published' )
- api_element_visible_keys = ( 'id', 'name', 'published' )
+class StoredWorkflow( object, DictifiableMixin):
+ dict_collection_visible_keys = ( 'id', 'name', 'published' )
+ dict_element_visible_keys = ( 'id', 'name', 'published' )
def __init__( self ):
self.id = None
self.user = None
@@ -2351,8 +2341,8 @@
new_swta.user = target_user
self.tags.append(new_swta)
- def get_api_value( self, view='collection', value_mapper = None ):
- rval = APIItem.get_api_value(self, view=view, value_mapper = value_mapper)
+ def dictify( self, view='collection', value_mapper = None ):
+ rval = DictifiableMixin.dictify(self, view=view, value_mapper = value_mapper)
tags_str_list = []
for tag in self.tags:
tag_str = tag.user_tname
@@ -2444,7 +2434,7 @@
return os.path.abspath( os.path.join( path, "metadata_%d.dat" % self.id ) )
-class FormDefinition( object, APIItem ):
+class FormDefinition( object, DictifiableMixin ):
# The following form_builder classes are supported by the FormDefinition class.
supported_field_types = [ AddressField, CheckboxField, PasswordField, SelectField, TextArea, TextField, WorkflowField, WorkflowMappingField, HistoryField ]
types = Bunch( REQUEST = 'Sequencing Request Form',
@@ -2453,8 +2443,8 @@
RUN_DETAILS_TEMPLATE = 'Sample run details template',
LIBRARY_INFO_TEMPLATE = 'Library information template',
USER_INFO = 'User Information' )
- api_collection_visible_keys = ( 'id', 'name' )
- api_element_visible_keys = ( 'id', 'name', 'desc', 'form_definition_current_id', 'fields', 'layout' )
+ dict_collection_visible_keys = ( 'id', 'name' )
+ dict_element_visible_keys = ( 'id', 'name', 'desc', 'form_definition_current_id', 'fields', 'layout' )
def __init__( self, name=None, desc=None, fields=[], form_definition_current=None, form_type=None, layout=None ):
self.name = name
self.desc = desc
@@ -2572,12 +2562,12 @@
self.form_definition = form_def
self.content = content
-class Request( object, APIItem ):
+class Request( object, DictifiableMixin ):
states = Bunch( NEW = 'New',
SUBMITTED = 'In Progress',
REJECTED = 'Rejected',
COMPLETE = 'Complete' )
- api_collection_visible_keys = ( 'id', 'name', 'state' )
+ dict_collection_visible_keys = ( 'id', 'name', 'state' )
def __init__( self, name=None, desc=None, request_type=None, user=None, form_values=None, notification=None ):
self.name = name
self.desc = desc
@@ -2763,9 +2753,9 @@
def populate_actions( self, trans, item, param_dict=None ):
return self.get_external_service_type( trans ).actions.populate( self, item, param_dict=param_dict )
-class RequestType( object, APIItem ):
- api_collection_visible_keys = ( 'id', 'name', 'desc' )
- api_element_visible_keys = ( 'id', 'name', 'desc', 'request_form_id', 'sample_form_id' )
+class RequestType( object, DictifiableMixin ):
+ dict_collection_visible_keys = ( 'id', 'name', 'desc' )
+ dict_element_visible_keys = ( 'id', 'name', 'desc', 'request_form_id', 'sample_form_id' )
rename_dataset_options = Bunch( NO = 'Do not rename',
SAMPLE_NAME = 'Preprend sample name',
EXPERIMENT_NAME = 'Prepend experiment name',
@@ -2849,12 +2839,12 @@
self.request_type = request_type
self.role = role
-class Sample( object, APIItem ):
+class Sample( object, DictifiableMixin ):
# The following form_builder classes are supported by the Sample class.
supported_field_types = [ CheckboxField, SelectField, TextField, WorkflowField, WorkflowMappingField, HistoryField ]
bulk_operations = Bunch( CHANGE_STATE = 'Change state',
SELECT_LIBRARY = 'Select data library and folder' )
- api_collection_visible_keys = ( 'id', 'name' )
+ dict_collection_visible_keys = ( 'id', 'name' )
def __init__(self, name=None, desc=None, request=None, form_values=None, bar_code=None, library=None, folder=None, workflow=None, history=None):
self.name = name
self.desc = desc
@@ -3179,9 +3169,9 @@
def __str__ ( self ):
return "Tag(id=%s, type=%i, parent_id=%s, name=%s)" % ( self.id, self.type, self.parent_id, self.name )
-class ItemTagAssociation ( object, APIItem ):
- api_collection_visible_keys = ( 'id', 'user_tname', 'user_value' )
- api_element_visible_keys = api_collection_visible_keys
+class ItemTagAssociation ( object, DictifiableMixin ):
+ dict_collection_visible_keys = ( 'id', 'user_tname', 'user_value' )
+ dict_element_visible_keys = dict_collection_visible_keys
def __init__( self, id=None, user=None, item_id=None, tag_id=None, user_tname=None, value=None ):
self.id = id
@@ -3317,9 +3307,9 @@
pass
class ToolShedRepository( object ):
- api_collection_visible_keys = ( 'id', 'tool_shed', 'name', 'owner', 'installed_changeset_revision', 'changeset_revision', 'ctx_rev', 'includes_datatypes',
+ dict_collection_visible_keys = ( 'id', 'tool_shed', 'name', 'owner', 'installed_changeset_revision', 'changeset_revision', 'ctx_rev', 'includes_datatypes',
'update_available', 'deleted', 'uninstalled', 'dist_to_shed', 'status', 'error_message' )
- api_element_visible_keys = ( 'id', 'tool_shed', 'name', 'owner', 'installed_changeset_revision', 'changeset_revision', 'ctx_rev', 'includes_datatypes',
+ dict_element_visible_keys = ( 'id', 'tool_shed', 'name', 'owner', 'installed_changeset_revision', 'changeset_revision', 'ctx_rev', 'includes_datatypes',
'update_available', 'deleted', 'uninstalled', 'dist_to_shed', 'status', 'error_message' )
installation_status = Bunch( NEW='New',
CLONING='Cloning',
@@ -3357,7 +3347,7 @@
self.status = status
self.error_message = error_message
def as_dict( self, value_mapper=None ):
- return self.get_api_value( view='element', value_mapper=value_mapper )
+ return self.dictify( view='element', value_mapper=value_mapper )
def repo_files_directory( self, app ):
repo_path = self.repo_path( app )
if repo_path:
@@ -3445,12 +3435,12 @@
if self.shed_config_filename == shed_tool_conf_dict[ 'config_filename' ]:
return shed_tool_conf_dict
return default
- def get_api_value( self, view='collection', value_mapper=None ):
+ def dictify( self, view='collection', value_mapper=None ):
if value_mapper is None:
value_mapper = {}
rval = {}
try:
- visible_keys = self.__getattribute__( 'api_' + view + '_visible_keys' )
+ visible_keys = self.__getattribute__( 'dict_' + view + '_visible_keys' )
except AttributeError:
raise Exception( 'Unknown API view: %s' % view )
for key in visible_keys:
@@ -3548,7 +3538,8 @@
for rrda in self.required_repositories:
repository_dependency = rrda.repository_dependency
required_repository = repository_dependency.repository
- required_repositories.append( required_repository )
+ if required_repository:
+ required_repositories.append( required_repository )
return required_repositories
@property
def installed_repository_dependencies( self ):
@@ -3568,6 +3559,7 @@
return missing_required_repositories
@property
def repository_dependencies_being_installed( self ):
+ """Return the repository's repository dependencies that are currently being installed."""
required_repositories_being_installed = []
for required_repository in self.repository_dependencies:
if required_repository.status == self.installation_status.INSTALLING:
@@ -3575,6 +3567,7 @@
return required_repositories_being_installed
@property
def repository_dependencies_missing_or_being_installed( self ):
+ """Return the repository's repository dependencies that are either missing or currently being installed."""
required_repositories_missing_or_being_installed = []
for required_repository in self.repository_dependencies:
if required_repository.status in [ self.installation_status.ERROR,
@@ -3585,6 +3578,7 @@
return required_repositories_missing_or_being_installed
@property
def repository_dependencies_with_installation_errors( self ):
+ """Return the repository's repository dependencies that have installation errors."""
required_repositories_with_installation_errors = []
for required_repository in self.repository_dependencies:
if required_repository.status == self.installation_status.ERROR:
@@ -3705,8 +3699,8 @@
self.tool_shed_repository.name,
self.tool_shed_repository.installed_changeset_revision )
-class ToolVersion( object, APIItem ):
- api_element_visible_keys = ( 'id', 'tool_shed_repository' )
+class ToolVersion( object, DictifiableMixin ):
+ dict_element_visible_keys = ( 'id', 'tool_shed_repository' )
def __init__( self, id=None, create_time=None, tool_id=None, tool_shed_repository=None ):
self.id = id
self.create_time = create_time
@@ -3763,8 +3757,8 @@
return version_ids
return [ tool_version.tool_id for tool_version in self.get_versions( app ) ]
- def get_api_value( self, view='element' ):
- rval = APIItem.get_api_value(self, view)
+ def dictify( self, view='element' ):
+ rval = DictifiableMixin.dictify(self, view)
rval['tool_name'] = self.tool_id
for a in self.parent_tool_association:
rval['parent_tool_id'] = a.parent_id
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 lib/galaxy/model/item_attrs.py
--- a/lib/galaxy/model/item_attrs.py
+++ b/lib/galaxy/model/item_attrs.py
@@ -158,36 +158,52 @@
class_name = '%sAnnotationAssociation' % item.__class__.__name__
return getattr( galaxy.model, class_name, None )
-class APIItem:
- """ Mixin for api representation. """
- #api_collection_visible_keys = ( 'id' )
- #api_element_visible_keys = ( 'id' )
- def get_api_value( self, view='collection', value_mapper = None ):
+class DictifiableMixin:
+ """ Mixin that enables objects to be converted to dictionaries. This is useful
+ when for sharing objects across boundaries, such as the API, tool scripts,
+ and JavaScript code. """
+
+ def dictify( self, view='collection', value_mapper=None ):
+ """
+ Return item dictionary.
+ """
+
+ if not value_mapper:
+ value_mapper = {}
+
def get_value( key, item ):
+ """
+ Recursive helper function to get item values.
+ """
+ # FIXME: why use exception here? Why not look for key in value_mapper
+ # first and then default to dictify?
try:
- return item.get_api_value( view=view, value_mapper=value_mapper )
+ return item.dictify( view=view, value_mapper=value_mapper )
except:
if key in value_mapper:
return value_mapper.get( key )( item )
return item
- if value_mapper is None:
- value_mapper = {}
- rval = {}
+
+ # Create dict to represent item.
+ rval = dict(
+ model_class=self.__class__.__name__
+ )
+
+ # Fill item dict with visible keys.
try:
- visible_keys = self.__getattribute__( 'api_' + view + '_visible_keys' )
+ visible_keys = self.__getattribute__( 'dict_' + view + '_visible_keys' )
except AttributeError:
- raise Exception( 'Unknown API view: %s' % view )
+ raise Exception( 'Unknown DictifiableMixin view: %s' % view )
for key in visible_keys:
try:
item = self.__getattribute__( key )
if type( item ) == InstrumentedList:
- rval[key] = []
+ rval[ key ] = []
for i in item:
- rval[key].append( get_value( key, i ) )
+ rval[ key ].append( get_value( key, i ) )
else:
- rval[key] = get_value( key, item )
+ rval[ key ] = get_value( key, item )
except AttributeError:
- rval[key] = None
+ rval[ key ] = None
- rval['model_class'] = self.__class__.__name__
return rval
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 lib/galaxy/model/search.py
--- a/lib/galaxy/model/search.py
+++ b/lib/galaxy/model/search.py
@@ -560,7 +560,7 @@
return self.view.get_results(True)
def item_to_api_value(self, item):
- r = item.get_api_value( view='element' )
+ r = item.dictify( view='element' )
if self.query.field_list.count("*"):
return r
o = {}
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 lib/galaxy/objectstore/__init__.py
--- a/lib/galaxy/objectstore/__init__.py
+++ b/lib/galaxy/objectstore/__init__.py
@@ -5,14 +5,11 @@
"""
import os
-import sys
import time
import random
import shutil
import logging
import threading
-import subprocess
-from datetime import datetime
from galaxy import util
from galaxy.jobs import Sleeper
@@ -21,22 +18,14 @@
from sqlalchemy.orm import object_session
-import multiprocessing
-from galaxy.objectstore.s3_multipart_upload import multipart_upload
-import boto
-from boto.s3.key import Key
-from boto.s3.connection import S3Connection
-from boto.exception import S3ResponseError
-
log = logging.getLogger( __name__ )
-logging.getLogger('boto').setLevel(logging.INFO) # Otherwise boto is quite noisy
class ObjectStore(object):
"""
ObjectStore abstract interface
"""
- def __init__(self):
+ def __init__(self, config, **kwargs):
self.running = True
self.extra_dirs = {}
@@ -206,7 +195,7 @@
>>> assert s.get_filename(obj) == file_path + '/000/dataset_1.dat'
"""
def __init__(self, config, file_path=None, extra_dirs=None):
- super(DiskObjectStore, self).__init__()
+ super(DiskObjectStore, self).__init__(config, file_path=file_path, extra_dirs=extra_dirs)
self.file_path = file_path or config.file_path
self.config = config
self.extra_dirs['job_work'] = config.job_working_directory
@@ -224,6 +213,7 @@
if not os.path.exists(path):
return self._construct_path(obj, base_dir=base_dir, dir_only=dir_only, extra_dir=extra_dir, extra_dir_at_root=extra_dir_at_root, alt_name=alt_name)
+ # TODO: rename to _disk_path or something like that to avoid conflicts with children that'll use the local_extra_dirs decorator, e.g. S3
def _construct_path(self, obj, old_style=False, base_dir=None, dir_only=False, extra_dir=None, extra_dir_at_root=False, alt_name=None, **kwargs):
""" Construct the expected absolute path for accessing the object
identified by `obj`.id.
@@ -297,7 +287,7 @@
util.umask_fix_perms(path, self.config.umask, 0666)
def empty(self, obj, **kwargs):
- return os.path.getsize(self.get_filename(obj, **kwargs)) > 0
+ return os.path.getsize(self.get_filename(obj, **kwargs)) == 0
def size(self, obj, **kwargs):
if self.exists(obj, **kwargs):
@@ -373,490 +363,6 @@
super(CachingObjectStore, self).__init__(self, path, backend)
-class S3ObjectStore(ObjectStore):
- """
- Object store that stores objects as items in an AWS S3 bucket. A local
- cache exists that is used as an intermediate location for files between
- Galaxy and S3.
- """
- def __init__(self, config):
- super(S3ObjectStore, self).__init__()
- self.config = config
- self.staging_path = self.config.file_path
- self.s3_conn = get_OS_connection(self.config)
- self.bucket = self._get_bucket(self.config.os_bucket_name)
- self.use_rr = self.config.os_use_reduced_redundancy
- self.cache_size = self.config.object_store_cache_size
- self.transfer_progress = 0
- # Clean cache only if value is set in universe_wsgi.ini
- if self.cache_size != -1:
- # Convert GBs to bytes for comparison
- self.cache_size = self.cache_size * 1073741824
- # Helper for interruptable sleep
- self.sleeper = Sleeper()
- self.cache_monitor_thread = threading.Thread(target=self.__cache_monitor)
- self.cache_monitor_thread.start()
- log.info("Cache cleaner manager started")
- # Test if 'axel' is available for parallel download and pull the key into cache
- try:
- subprocess.call('axel')
- self.use_axel = True
- except OSError:
- self.use_axel = False
-
- def __cache_monitor(self):
- time.sleep(2) # Wait for things to load before starting the monitor
- while self.running:
- total_size = 0
- # Is this going to be too expensive of an operation to be done frequently?
- file_list = []
- for dirpath, dirnames, filenames in os.walk(self.staging_path):
- for f in filenames:
- fp = os.path.join(dirpath, f)
- file_size = os.path.getsize(fp)
- total_size += file_size
- # Get the time given file was last accessed
- last_access_time = time.localtime(os.stat(fp)[7])
- # Compose a tuple of the access time and the file path
- file_tuple = last_access_time, fp, file_size
- file_list.append(file_tuple)
- # Sort the file list (based on access time)
- file_list.sort()
- # Initiate cleaning once within 10% of the defined cache size?
- cache_limit = self.cache_size * 0.9
- if total_size > cache_limit:
- log.info("Initiating cache cleaning: current cache size: %s; clean until smaller than: %s" \
- % (convert_bytes(total_size), convert_bytes(cache_limit)))
- # How much to delete? If simply deleting up to the cache-10% limit,
- # is likely to be deleting frequently and may run the risk of hitting
- # the limit - maybe delete additional #%?
- # For now, delete enough to leave at least 10% of the total cache free
- delete_this_much = total_size - cache_limit
- self.__clean_cache(file_list, delete_this_much)
- self.sleeper.sleep(30) # Test cache size every 30 seconds?
-
- def __clean_cache(self, file_list, delete_this_much):
- """ Keep deleting files from the file_list until the size of the deleted
- files is greater than the value in delete_this_much parameter.
-
- :type file_list: list
- :param file_list: List of candidate files that can be deleted. This method
- will start deleting files from the beginning of the list so the list
- should be sorted accordingly. The list must contains 3-element tuples,
- positioned as follows: position 0 holds file last accessed timestamp
- (as time.struct_time), position 1 holds file path, and position 2 has
- file size (e.g., (<access time>, /mnt/data/dataset_1.dat), 472394)
-
- :type delete_this_much: int
- :param delete_this_much: Total size of files, in bytes, that should be deleted.
- """
- # Keep deleting datasets from file_list until deleted_amount does not
- # exceed delete_this_much; start deleting from the front of the file list,
- # which assumes the oldest files come first on the list.
- deleted_amount = 0
- for i, f in enumerate(file_list):
- if deleted_amount < delete_this_much:
- deleted_amount += f[2]
- os.remove(f[1])
- # Debugging code for printing deleted files' stats
- # folder, file_name = os.path.split(f[1])
- # file_date = time.strftime("%m/%d/%y %H:%M:%S", f[0])
- # log.debug("%s. %-25s %s, size %s (deleted %s/%s)" \
- # % (i, file_name, convert_bytes(f[2]), file_date, \
- # convert_bytes(deleted_amount), convert_bytes(delete_this_much)))
- else:
- log.debug("Cache cleaning done. Total space freed: %s" % convert_bytes(deleted_amount))
- return
-
- def _get_bucket(self, bucket_name):
- """ Sometimes a handle to a bucket is not established right away so try
- it a few times. Raise error is connection is not established. """
- for i in range(5):
- try:
- bucket = self.s3_conn.get_bucket(bucket_name)
- log.debug("Using cloud object store with bucket '%s'" % bucket.name)
- return bucket
- except S3ResponseError:
- log.debug("Could not get bucket '%s', attempt %s/5" % (bucket_name, i+1))
- time.sleep(2)
- # All the attempts have been exhausted and connection was not established,
- # raise error
- raise S3ResponseError
-
- def _fix_permissions(self, rel_path):
- """ Set permissions on rel_path"""
- for basedir, dirs, files in os.walk(rel_path):
- util.umask_fix_perms(basedir, self.config.umask, 0777, self.config.gid)
- for f in files:
- path = os.path.join(basedir, f)
- # Ignore symlinks
- if os.path.islink(path):
- continue
- util.umask_fix_perms( path, self.config.umask, 0666, self.config.gid )
-
- def _construct_path(self, obj, dir_only=None, extra_dir=None, extra_dir_at_root=False, alt_name=None, **kwargs):
- rel_path = os.path.join(*directory_hash_id(obj.id))
- if extra_dir is not None:
- if extra_dir_at_root:
- rel_path = os.path.join(extra_dir, rel_path)
- else:
- rel_path = os.path.join(rel_path, extra_dir)
- # S3 folders are marked by having trailing '/' so add it now
- rel_path = '%s/' % rel_path
- if not dir_only:
- rel_path = os.path.join(rel_path, alt_name if alt_name else "dataset_%s.dat" % obj.id)
- return rel_path
-
- def _get_cache_path(self, rel_path):
- return os.path.abspath(os.path.join(self.staging_path, rel_path))
-
- def _get_transfer_progress(self):
- return self.transfer_progress
-
- def _get_size_in_s3(self, rel_path):
- try:
- key = self.bucket.get_key(rel_path)
- if key:
- return key.size
- except S3ResponseError, ex:
- log.error("Could not get size of key '%s' from S3: %s" % (rel_path, ex))
- except Exception, ex:
- log.error("Could not get reference to the key object '%s'; returning -1 for key size: %s" % (rel_path, ex))
- return -1
-
- def _key_exists(self, rel_path):
- exists = False
- try:
- # A hackish way of testing if the rel_path is a folder vs a file
- is_dir = rel_path[-1] == '/'
- if is_dir:
- rs = self.bucket.get_all_keys(prefix=rel_path)
- if len(rs) > 0:
- exists = True
- else:
- exists = False
- else:
- key = Key(self.bucket, rel_path)
- exists = key.exists()
- except S3ResponseError, ex:
- log.error("Trouble checking existence of S3 key '%s': %s" % (rel_path, ex))
- return False
- #print "Checking if '%s' exists in S3: %s" % (rel_path, exists)
- if rel_path[0] == '/':
- raise
- return exists
-
- def _in_cache(self, rel_path):
- """ Check if the given dataset is in the local cache and return True if so. """
- # log.debug("------ Checking cache for rel_path %s" % rel_path)
- cache_path = self._get_cache_path(rel_path)
- return os.path.exists(cache_path)
- # TODO: Part of checking if a file is in cache should be to ensure the
- # size of the cached file matches that on S3. Once the upload tool explicitly
- # creates, this check sould be implemented- in the mean time, it's not
- # looking likely to be implementable reliably.
- # if os.path.exists(cache_path):
- # # print "***1 %s exists" % cache_path
- # if self._key_exists(rel_path):
- # # print "***2 %s exists in S3" % rel_path
- # # Make sure the size in cache is available in its entirety
- # # print "File '%s' cache size: %s, S3 size: %s" % (cache_path, os.path.getsize(cache_path), self._get_size_in_s3(rel_path))
- # if os.path.getsize(cache_path) == self._get_size_in_s3(rel_path):
- # # print "***2.1 %s exists in S3 and the size is the same as in cache (in_cache=True)" % rel_path
- # exists = True
- # else:
- # # print "***2.2 %s exists but differs in size from cache (in_cache=False)" % cache_path
- # exists = False
- # else:
- # # Although not perfect decision making, this most likely means
- # # that the file is currently being uploaded
- # # print "***3 %s found in cache but not in S3 (in_cache=True)" % cache_path
- # exists = True
- # else:
- # return False
-
- def _pull_into_cache(self, rel_path):
- # Ensure the cache directory structure exists (e.g., dataset_#_files/)
- rel_path_dir = os.path.dirname(rel_path)
- if not os.path.exists(self._get_cache_path(rel_path_dir)):
- os.makedirs(self._get_cache_path(rel_path_dir))
- # Now pull in the file
- ok = self._download(rel_path)
- self._fix_permissions(self._get_cache_path(rel_path_dir))
- return ok
-
- def _transfer_cb(self, complete, total):
- self.transfer_progress += 10
-
- def _download(self, rel_path):
- try:
- log.debug("Pulling key '%s' into cache to %s" % (rel_path, self._get_cache_path(rel_path)))
- key = self.bucket.get_key(rel_path)
- # Test if cache is large enough to hold the new file
- if self.cache_size > 0 and key.size > self.cache_size:
- log.critical("File %s is larger (%s) than the cache size (%s). Cannot download." \
- % (rel_path, key.size, self.cache_size))
- return False
- if self.use_axel:
- log.debug("Parallel pulled key '%s' into cache to %s" % (rel_path, self._get_cache_path(rel_path)))
- ncores = multiprocessing.cpu_count()
- url = key.generate_url(7200)
- ret_code = subprocess.call("axel -a -n %s '%s'" % (ncores, url))
- if ret_code == 0:
- return True
- else:
- log.debug("Pulled key '%s' into cache to %s" % (rel_path, self._get_cache_path(rel_path)))
- self.transfer_progress = 0 # Reset transfer progress counter
- key.get_contents_to_filename(self._get_cache_path(rel_path), cb=self._transfer_cb, num_cb=10)
- return True
- except S3ResponseError, ex:
- log.error("Problem downloading key '%s' from S3 bucket '%s': %s" % (rel_path, self.bucket.name, ex))
- return False
-
- def _push_to_os(self, rel_path, source_file=None, from_string=None):
- """
- Push the file pointed to by ``rel_path`` to the object store naming the key
- ``rel_path``. If ``source_file`` is provided, push that file instead while
- still using ``rel_path`` as the key name.
- If ``from_string`` is provided, set contents of the file to the value of
- the string.
- """
- try:
- source_file = source_file if source_file else self._get_cache_path(rel_path)
- if os.path.exists(source_file):
- key = Key(self.bucket, rel_path)
- if os.path.getsize(source_file) == 0 and key.exists():
- log.debug("Wanted to push file '%s' to S3 key '%s' but its size is 0; skipping." % (source_file, rel_path))
- return True
- if from_string:
- key.set_contents_from_string(from_string, reduced_redundancy=self.use_rr)
- log.debug("Pushed data from string '%s' to key '%s'" % (from_string, rel_path))
- else:
- start_time = datetime.now()
- # print "Pushing cache file '%s' of size %s bytes to key '%s'" % (source_file, os.path.getsize(source_file), rel_path)
- # print "+ Push started at '%s'" % start_time
- mb_size = os.path.getsize(source_file) / 1e6
- if mb_size < 60 or self.config.object_store == 'swift':
- self.transfer_progress = 0 # Reset transfer progress counter
- key.set_contents_from_filename(source_file, reduced_redundancy=self.use_rr,
- cb=self._transfer_cb, num_cb=10)
- else:
- multipart_upload(self.bucket, key.name, source_file, mb_size, use_rr=self.use_rr)
- end_time = datetime.now()
- # print "+ Push ended at '%s'; %s bytes transfered in %ssec" % (end_time, os.path.getsize(source_file), end_time-start_time)
- log.debug("Pushed cache file '%s' to key '%s' (%s bytes transfered in %s sec)" % (source_file, rel_path, os.path.getsize(source_file), end_time-start_time))
- return True
- else:
- log.error("Tried updating key '%s' from source file '%s', but source file does not exist."
- % (rel_path, source_file))
- except S3ResponseError, ex:
- log.error("Trouble pushing S3 key '%s' from file '%s': %s" % (rel_path, source_file, ex))
- return False
-
- def file_ready(self, obj, **kwargs):
- """
- A helper method that checks if a file corresponding to a dataset is
- ready and available to be used. Return ``True`` if so, ``False`` otherwise.
- """
- rel_path = self._construct_path(obj, **kwargs)
- # Make sure the size in cache is available in its entirety
- if self._in_cache(rel_path):
- if os.path.getsize(self._get_cache_path(rel_path)) == self._get_size_in_s3(rel_path):
- return True
- log.debug("Waiting for dataset {0} to transfer from OS: {1}/{2}".format(rel_path,
- os.path.getsize(self._get_cache_path(rel_path)), self._get_size_in_s3(rel_path)))
- return False
-
- def exists(self, obj, **kwargs):
- in_cache = in_s3 = False
- rel_path = self._construct_path(obj, **kwargs)
- # Check cache
- if self._in_cache(rel_path):
- in_cache = True
- # Check S3
- in_s3 = self._key_exists(rel_path)
- # log.debug("~~~~~~ File '%s' exists in cache: %s; in s3: %s" % (rel_path, in_cache, in_s3))
- # dir_only does not get synced so shortcut the decision
- dir_only = kwargs.get('dir_only', False)
- if dir_only:
- if in_cache or in_s3:
- return True
- else:
- return False
- # TODO: Sync should probably not be done here. Add this to an async upload stack?
- if in_cache and not in_s3:
- self._push_to_os(rel_path, source_file=self._get_cache_path(rel_path))
- return True
- elif in_s3:
- return True
- else:
- return False
-
- def create(self, obj, **kwargs):
- if not self.exists(obj, **kwargs):
- #print "S3 OS creating a dataset with ID %s" % kwargs
- # Pull out locally used fields
- extra_dir = kwargs.get('extra_dir', None)
- extra_dir_at_root = kwargs.get('extra_dir_at_root', False)
- dir_only = kwargs.get('dir_only', False)
- alt_name = kwargs.get('alt_name', None)
- # print "---- Processing: %s; %s" % (alt_name, locals())
- # Construct hashed path
- rel_path = os.path.join(*directory_hash_id(obj.id))
-
- # Optionally append extra_dir
- if extra_dir is not None:
- if extra_dir_at_root:
- rel_path = os.path.join(extra_dir, rel_path)
- else:
- rel_path = os.path.join(rel_path, extra_dir)
- # Create given directory in cache
- cache_dir = os.path.join(self.staging_path, rel_path)
- if not os.path.exists(cache_dir):
- os.makedirs(cache_dir)
- # Although not really necessary to create S3 folders (because S3 has
- # flat namespace), do so for consistency with the regular file system
- # S3 folders are marked by having trailing '/' so add it now
- # s3_dir = '%s/' % rel_path
- # self._push_to_os(s3_dir, from_string='')
- # If instructed, create the dataset in cache & in S3
- if not dir_only:
- rel_path = os.path.join(rel_path, alt_name if alt_name else "dataset_%s.dat" % obj.id)
- open(os.path.join(self.staging_path, rel_path), 'w').close()
- self._push_to_os(rel_path, from_string='')
-
- def empty(self, obj, **kwargs):
- if self.exists(obj, **kwargs):
- return bool(self.size(obj, **kwargs) > 0)
- else:
- raise ObjectNotFound( 'objectstore.empty, object does not exist: %s, kwargs: %s'
- %( str( obj ), str( kwargs ) ) )
-
- def size(self, obj, **kwargs):
- rel_path = self._construct_path(obj, **kwargs)
- if self._in_cache(rel_path):
- try:
- return os.path.getsize(self._get_cache_path(rel_path))
- except OSError, ex:
- log.info("Could not get size of file '%s' in local cache, will try S3. Error: %s" % (rel_path, ex))
- elif self.exists(obj, **kwargs):
- return self._get_size_in_s3(rel_path)
- log.warning("Did not find dataset '%s', returning 0 for size" % rel_path)
- return 0
-
- def delete(self, obj, entire_dir=False, **kwargs):
- rel_path = self._construct_path(obj, **kwargs)
- extra_dir = kwargs.get('extra_dir', None)
- try:
- # For the case of extra_files, because we don't have a reference to
- # individual files/keys we need to remove the entire directory structure
- # with all the files in it. This is easy for the local file system,
- # but requires iterating through each individual key in S3 and deleing it.
- if entire_dir and extra_dir:
- shutil.rmtree(self._get_cache_path(rel_path))
- rs = self.bucket.get_all_keys(prefix=rel_path)
- for key in rs:
- log.debug("Deleting key %s" % key.name)
- key.delete()
- return True
- else:
- # Delete from cache first
- os.unlink(self._get_cache_path(rel_path))
- # Delete from S3 as well
- if self._key_exists(rel_path):
- key = Key(self.bucket, rel_path)
- log.debug("Deleting key %s" % key.name)
- key.delete()
- return True
- except S3ResponseError, ex:
- log.error("Could not delete key '%s' from S3: %s" % (rel_path, ex))
- except OSError, ex:
- log.error('%s delete error %s' % (self._get_filename(obj, **kwargs), ex))
- return False
-
- def get_data(self, obj, start=0, count=-1, **kwargs):
- rel_path = self._construct_path(obj, **kwargs)
- # Check cache first and get file if not there
- if not self._in_cache(rel_path):
- self._pull_into_cache(rel_path)
- #else:
- # print "(cccc) Getting '%s' from cache" % self._get_cache_path(rel_path)
- # Read the file content from cache
- data_file = open(self._get_cache_path(rel_path), 'r')
- data_file.seek(start)
- content = data_file.read(count)
- data_file.close()
- return content
-
- def get_filename(self, obj, **kwargs):
- #print "S3 get_filename for dataset: %s" % dataset_id
- dir_only = kwargs.get('dir_only', False)
- rel_path = self._construct_path(obj, **kwargs)
- cache_path = self._get_cache_path(rel_path)
- # S3 does not recognize directories as files so cannot check if those exist.
- # So, if checking dir only, ensure given dir exists in cache and return
- # the expected cache path.
- # dir_only = kwargs.get('dir_only', False)
- # if dir_only:
- # if not os.path.exists(cache_path):
- # os.makedirs(cache_path)
- # return cache_path
- # Check if the file exists in the cache first
- if self._in_cache(rel_path):
- return cache_path
- # Check if the file exists in persistent storage and, if it does, pull it into cache
- elif self.exists(obj, **kwargs):
- if dir_only: # Directories do not get pulled into cache
- return cache_path
- else:
- if self._pull_into_cache(rel_path):
- return cache_path
- # For the case of retrieving a directory only, return the expected path
- # even if it does not exist.
- # if dir_only:
- # return cache_path
- raise ObjectNotFound( 'objectstore.get_filename, no cache_path: %s, kwargs: %s'
- %( str( obj ), str( kwargs ) ) )
- # return cache_path # Until the upload tool does not explicitly create the dataset, return expected path
-
- def update_from_file(self, obj, file_name=None, create=False, **kwargs):
- if create:
- self.create(obj, **kwargs)
- if self.exists(obj, **kwargs):
- rel_path = self._construct_path(obj, **kwargs)
- # Chose whether to use the dataset file itself or an alternate file
- if file_name:
- source_file = os.path.abspath(file_name)
- # Copy into cache
- cache_file = self._get_cache_path(rel_path)
- try:
- if source_file != cache_file:
- # FIXME? Should this be a `move`?
- shutil.copy2(source_file, cache_file)
- self._fix_permissions(cache_file)
- except OSError, ex:
- log.error("Trouble copying source file '%s' to cache '%s': %s" % (source_file, cache_file, ex))
- else:
- source_file = self._get_cache_path(rel_path)
- # Update the file on S3
- self._push_to_os(rel_path, source_file)
- else:
- raise ObjectNotFound( 'objectstore.update_from_file, object does not exist: %s, kwargs: %s'
- %( str( obj ), str( kwargs ) ) )
-
- def get_object_url(self, obj, **kwargs):
- if self.exists(obj, **kwargs):
- rel_path = self._construct_path(obj, **kwargs)
- try:
- key = Key(self.bucket, rel_path)
- return key.generate_url(expires_in = 86400) # 24hrs
- except S3ResponseError, ex:
- log.warning("Trouble generating URL for dataset '%s': %s" % (rel_path, ex))
- return None
-
- def get_store_usage_percent(self):
- return 0.0
-
class DistributedObjectStore(ObjectStore):
"""
ObjectStore that defers to a list of backends, for getting objects the
@@ -1011,6 +517,7 @@
return id
return None
+
class HierarchicalObjectStore(ObjectStore):
"""
ObjectStore that defers to a list of backends, for getting objects the
@@ -1021,6 +528,7 @@
def __init__(self, backends=[]):
super(HierarchicalObjectStore, self).__init__()
+
def build_object_store_from_config(config, fsmon=False):
""" Depending on the configuration setting, invoke the appropriate object store
"""
@@ -1028,14 +536,31 @@
if store == 'disk':
return DiskObjectStore(config=config)
elif store == 's3' or store == 'swift':
+ from galaxy.objectstore.s3 import S3ObjectStore
return S3ObjectStore(config=config)
elif store == 'distributed':
return DistributedObjectStore(config=config, fsmon=fsmon)
elif store == 'hierarchical':
return HierarchicalObjectStore()
+ elif store == 'irods':
+ from galaxy.objectstore.rods import IRODSObjectStore
+ return IRODSObjectStore(config=config)
else:
log.error("Unrecognized object store definition: {0}".format(store))
+def local_extra_dirs( func ):
+ """ A decorator for non-local plugins to utilize local directories for their extra_dirs (job_working_directory and temp).
+ """
+ def wraps( self, *args, **kwargs ):
+ if kwargs.get( 'base_dir', None ) is None:
+ return func( self, *args, **kwargs )
+ else:
+ for c in self.__class__.__mro__:
+ if c.__name__ == 'DiskObjectStore':
+ return getattr( c, func.__name__ )( self, *args, **kwargs )
+ raise Exception( "Could not call DiskObjectStore's %s method, does your Object Store plugin inherit from DiskObjectStore?" % func.__name__ )
+ return wraps
+
def convert_bytes(bytes):
""" A helper function used for pretty printing disk usage """
if bytes is None:
@@ -1057,25 +582,3 @@
else:
size = '%.2fb' % bytes
return size
-
-def get_OS_connection(config):
- """
- Get a connection object for a cloud Object Store specified in the config.
- Currently, this is a ``boto`` connection object.
- """
- log.debug("Getting a connection object for '{0}' object store".format(config.object_store))
- a_key = config.os_access_key
- s_key = config.os_secret_key
- if config.object_store == 's3':
- return S3Connection(a_key, s_key)
- else:
- # Establish the connection now
- calling_format = boto.s3.connection.OrdinaryCallingFormat()
- s3_conn = boto.connect_s3(aws_access_key_id=a_key,
- aws_secret_access_key=s_key,
- is_secure=config.os_is_secure,
- host=config.os_host,
- port=int(config.os_port),
- calling_format=calling_format,
- path=config.os_conn_path)
- return s3_conn
diff -r 91bece401d51a8b9be8850208bd56fa838087a64 -r 0bdee4933794f61eec10e80d2685ae07e073a796 lib/galaxy/objectstore/rods.py
--- /dev/null
+++ b/lib/galaxy/objectstore/rods.py
@@ -0,0 +1,332 @@
+"""
+Object Store plugin for the Integrated Rule-Oriented Data Store (iRODS)
+
+The module is named rods to avoid conflicting with the PyRods module, irods
+"""
+
+import os
+import time
+import errno
+import logging
+#import traceback
+
+from posixpath import join as path_join
+from posixpath import basename as path_basename
+from posixpath import dirname as path_dirname
+
+from galaxy.objectstore import DiskObjectStore, ObjectStore, local_extra_dirs
+from galaxy.exceptions import ObjectNotFound, ObjectInvalid
+
+import galaxy.eggs
+galaxy.eggs.require( 'PyRods' )
+import irods
+
+log = logging.getLogger( __name__ )
+
+
+class IRODSObjectStore( DiskObjectStore, ObjectStore ):
+ """
+ Galaxy object store based on iRODS
+ """
+ def __init__( self, config, file_path=None, extra_dirs=None ):
+ super( IRODSObjectStore, self ).__init__( config, file_path=file_path, extra_dirs=extra_dirs )
+ self.cache_path = config.object_store_cache_path
+ self.default_resource = config.irods_default_resource or None
+
+ # Connect to iRODS (AssertionErrors will be raised if anything goes wrong)
+ self.rods_env, self.rods_conn = rods_connect()
+
+ # if the root collection path in the config is unset or relative, try to use a sensible default
+ if config.irods_root_collection_path is None or ( config.irods_root_collection_path is not None and not config.irods_root_collection_path.startswith( '/' ) ):
+ rods_home = self.rods_env.rodsHome
+ assert rods_home != '', "Unable to initialize iRODS Object Store: rodsHome cannot be determined and irods_root_collection_path in Galaxy config is unset or not absolute."
+ if config.irods_root_collection_path is None:
+ self.root_collection_path = path_join( rods_home, 'galaxy_data' )
+ else:
+ self.root_collection_path = path_join( rods_home, config.irods_root_collection_path )
+ else:
+ self.root_collection_path = config.irods_root_collection_path
+
+ # will return a collection object regardless of whether it exists
+ self.root_collection = irods.irodsCollection( self.rods_conn, self.root_collection_path )
+
+ if self.root_collection.getId() == -1:
+ log.warning( "iRODS root collection does not exist, will attempt to create: %s", self.root_collection_path )
+ self.root_collection.upCollection()
+ assert self.root_collection.createCollection( os.path.basename( self.root_collection_path ) ) == 0, "iRODS root collection creation failed: %s" % self.root_collection_path
+ self.root_collection = irods.irodsCollection( self.rods_conn, self.root_collection_path )
+ assert self.root_collection.getId() != -1, "iRODS root collection creation claimed success but still does not exist"
+
+ if self.default_resource is None:
+ self.default_resource = self.rods_env.rodsDefResource
+
+ log.info( "iRODS data for this instance will be stored in collection: %s, resource: %s", self.root_collection_path, self.default_resource )
+
+ def __get_rods_path( self, obj, base_dir=None, dir_only=False, extra_dir=None, extra_dir_at_root=False, alt_name=None, strip_dat=True, **kwargs ):
+ path = ""
+ if extra_dir is not None:
+ path = extra_dir
+
+ # extra_dir_at_root is ignored - since the iRODS plugin does not use
+ # the directory hash, there is only one level of subdirectory.
+
+ if not dir_only:
+ # the .dat extension is stripped when stored in iRODS
+ # TODO: is the strip_dat kwarg the best way to implement this?
+ if strip_dat and alt_name and alt_name.endswith( '.dat' ):
+ alt_name = os.path.splitext( alt_name )[0]
+ default_name = 'dataset_%s' % obj.id
+ if not strip_dat:
+ default_name += '.dat'
+ path = path_join( path, alt_name if alt_name else default_name )
+
+ path = path_join( self.root_collection_path, path )
+
+ #log.debug( 'iRODS path for %s %s is %s', obj.__class__.__name__, obj.id, path )
+
+ return path
+
+ def __get_cache_path( self, obj, **kwargs ):
+ # FIXME: does not handle collections
+ # FIXME: collisions could occur here
+ return os.path.join( self.cache_path, path_basename( self.__get_rods_path( obj, strip_dat=False, **kwargs ) ) )
+
+ def __clean_cache_entry( self, obj, **kwargs ):
+ # FIXME: does not handle collections
+ try:
+ os.unlink( self.__get_cache_path( obj, **kwargs ) )
+ except OSError:
+ # it is expected that we'll call this method a lot regardless of
+ # whether we think the cached file exists
+ pass
+
+ def __get_rods_handle( self, obj, mode='r', **kwargs ):
+ if kwargs.get( 'dir_only', False ):
+ return irods.irodsCollection( self.rods_conn, self.__get_rods_path( obj, **kwargs ) )
+ else:
+ return irods.irodsOpen( self.rods_conn, self.__get_rods_path( obj, **kwargs ), mode )
+
+ def __mkcolls( self, rods_path ):
+ """
+ An os.makedirs() for iRODS collections. `rods_path` is the desired collection to create.
+ """
+ assert rods_path.startswith( self.root_collection_path + '/' ), '__mkcolls(): Creating collections outside the root collection is not allowed (requested path was: %s)' % rods_path
+ mkcolls = []
+ c = irods.irodsCollection( self.rods_conn, rods_path )
+ while c.getId() == -1:
+ assert c.getCollName().startswith( self.root_collection_path + '/' ), '__mkcolls(): Attempted to move above the root collection: %s' % c.getCollName()
+ mkcolls.append( c.getCollName() )
+ c.upCollection()
+ for collname in reversed( mkcolls ):
+ log.debug( 'Creating collection %s' % collname )
+ ci = irods.collInp_t()
+ ci.collName = collname
+ status = rcCollCreate( self.rods_conn, ci )
+ assert status == 0, '__mkcolls(): Failed to create collection: %s' % collname
+
+ @local_extra_dirs
+ def exists( self, obj, **kwargs ):
+ doi = irods.dataObjInp_t()
+ doi.objPath = self.__get_rods_path( obj, **kwargs )
+ log.debug( 'exists(): checking: %s', doi.objPath )
+ return irods.rcObjStat( self.rods_conn, doi ) is not None
+
+ @local_extra_dirs
+ def create(self, obj, **kwargs):
+ if not self.exists( obj, **kwargs ):
+ rods_path = self.__get_rods_path( obj, **kwargs )
+ log.debug( 'create(): %s', rods_path )
+ dir_only = kwargs.get( 'dir_only', False )
+ # short circuit collection creation since most of the time it will
+ # be the root collection which already exists
+ collection_path = rods_path if dir_only else path_dirname( rods_path )
+ if collection_path != self.root_collection_path:
+ self.__mkcolls( collection_path )
+ if not dir_only:
+ # rcDataObjCreate is used instead of the irodsOpen wrapper so
+ # that we can prevent overwriting
+ doi = irods.dataObjInp_t()
+ doi.objPath = rods_path
+ doi.createMode = 0640
+ doi.dataSize = 0 # 0 actually means "unknown", although literally 0 would be preferable
+ irods.addKeyVal( doi.condInput, irods.DEST_RESC_NAME_KW, self.default_resource )
+ status = irods.rcDataObjCreate( self.rods_conn, doi )
+ assert status >= 0, 'create(): rcDataObjCreate() failed: %s: %s: %s' % ( rods_path, status, irods.strerror( status ) )
+
+ @local_extra_dirs
+ def empty( self, obj, **kwargs ):
+ assert 'dir_only' not in kwargs, 'empty(): `dir_only` parameter is invalid here'
+ h = self.__get_rods_handle( obj, **kwargs )
+ try:
+ return h.getSize() == 0
+ except AttributeError:
+ # h is None
+ raise ObjectNotFound()
+
+ def size( self, obj, **kwargs ):
+ assert 'dir_only' not in kwargs, 'size(): `dir_only` parameter is invalid here'
+ h = self.__get_rods_handle( obj, **kwargs )
+ try:
+ return h.getSize()
+ except AttributeError:
+ # h is None
+ return 0
+
+ @local_extra_dirs
+ def delete( self, obj, entire_dir=False, **kwargs ):
+ assert 'dir_only' not in kwargs, 'delete(): `dir_only` parameter is invalid here'
+ rods_path = self.__get_rods_path( obj, **kwargs )
+ # __get_rods_path prepends self.root_collection_path but we are going
+ # to ensure that it's valid anyway for safety's sake
+ assert rods_path.startswith( self.root_collection_path + '/' ), 'ERROR: attempt to delete object outside root collection (path was: %s)' % rods_path
+ if entire_dir:
+ # TODO
+ raise NotImplementedError()
+ h = self.__get_rods_handle( obj, **kwargs )
+ try:
+ # note: PyRods' irodsFile.delete() does not set force
+ status = h.delete()
+ assert status == 0, '%d: %s' % ( status, irods.strerror( status ) )
+ return True
+ except AttributeError:
+ log.warning( 'delete(): operation failed: object does not exist: %s', rods_path )
+ except AssertionError, e:
+ # delete() does not raise on deletion failure
+ log.error( 'delete(): operation failed: %s', e )
+ finally:
+ # remove the cached entry (finally is executed even when the try
+ # contains a return)
+ self.__clean_cache_entry( self, obj, **kwargs )
+ return False
+
+ @local_extra_dirs
+ def get_data( self, obj, start=0, count=-1, **kwargs ):
+ log.debug( 'get_data(): %s' )
+ h = self.__get_rods_handle( obj, **kwargs )
+ try:
+ h.seek( start )
+ except AttributeError:
+ raise ObjectNotFound()
+ if count == -1:
+ return h.read()
+ else:
+ return f.read( count )
+ # TODO: make sure implicit close is okay, DiskObjectStore actually
+ # reads data into a var, closes, and returns the var
+
+ @local_extra_dirs
+ def get_filename( self, obj, **kwargs ):
+ log.debug( "get_filename(): called on %s %s. For better performance, avoid this method and use get_data() instead.", obj.__class__.__name__, obj.id )
+
+ # For finding all places where get_filename is called...
+ #log.debug( ''.join( traceback.format_stack() ) )
+
+ cached_path = self.__get_cache_path( obj, **kwargs )
+
+ if not self.exists( obj, **kwargs ):
+ raise ObjectNotFound()
+
+ # TODO: implement or define whether dir_only is valid
+ if 'dir_only' in kwargs:
+ raise NotImplementedError()
+
+ # cache hit
+ if os.path.exists( cached_path ):
+ return os.path.abspath( cached_path )
+
+ # cache miss
+ # TODO: thread this
+ incoming_path = os.path.join( os.path.dirname( cached_path ), "__incoming_%s" % os.path.basename( cached_path ) )
+ doi = irods.dataObjInp_t()
+ doi.objPath = self.__get_rods_path( obj, **kwargs )
+ doi.dataSize = 0 # TODO: does this affect performance? should we get size?
+ doi.numThreads = 0
+ # TODO: might want to VERIFY_CHKSUM_KW
+ log.debug( 'get_filename(): caching %s to %s', doi.objPath, incoming_path )
+
+ # do the iget
+ status = irods.rcDataObjGet( self.rods_conn, doi, incoming_path )
+
+ # if incoming already exists, we'll wait for another process or thread
+ # to finish caching
+ if status != irods.OVERWRITE_WITHOUT_FORCE_FLAG:
+ assert status == 0, 'get_filename(): iget %s failed (%s): %s' % ( doi.objPath, status, irods.strerror( status ) )
+ # POSIX rename is atomic
+ # TODO: rename without clobbering
+ os.rename( incoming_path, cached_path )
+ log.debug( 'get_filename(): cached %s to %s', doi.objPath, cached_path )
+
+ # another process or thread is caching, wait for it
+ while not os.path.exists( cached_path ):
+ # TODO: force restart after mod time > some configurable, or
+ # otherwise deal with this potential deadlock and interrupted
+ # transfers
+ time.sleep( 5 )
+ log.debug( "get_filename(): waiting on incoming '%s' for %s %s", incoming_path, obj.__class__.__name__, obj.id )
+
+ return os.path.abspath( cached_path )
+
+ @local_extra_dirs
+ def update_from_file(self, obj, file_name=None, create=False, **kwargs):
+ assert 'dir_only' not in kwargs, 'update_from_file(): `dir_only` parameter is invalid here'
+
+ # do not create if not requested
+ if create and not self.exists( obj, **kwargs ):
+ raise ObjectNotFound()
+
+ if file_name is None:
+ file_name = self.__get_cache_path( obj, **kwargs )
+
+ # put will create if necessary
+ doi = irods.dataObjInp_t()
+ doi.objPath = self.__get_rods_path( obj, **kwargs )
+ doi.createMode = 0640
+ doi.dataSize = os.stat( file_name ).st_size
+ doi.numThreads = 0
+ irods.addKeyVal( doi.condInput, irods.DEST_RESC_NAME_KW, self.default_resource )
+ irods.addKeyVal( doi.condInput, irods.FORCE_FLAG_KW, '' )
+ # TODO: might want to VERIFY_CHKSUM_KW
+ log.debug( 'update_from_file(): updating %s to %s', file_name, doi.objPath )
+
+ # do the iput
+ status = irods.rcDataObjPut( self.rods_conn, doi, file_name )
+ assert status == 0, 'update_from_file(): iput %s failed (%s): %s' % ( doi.objPath, status, irods.strerror( status ) )
+
+ def get_object_url(self, obj, **kwargs):
+ return None
+
+ def get_store_usage_percent(self):
+ return 0.0
+
+# monkeypatch an strerror method into the irods module
+def _rods_strerror( errno ):
+ """
+ The missing `strerror` for iRODS error codes
+ """
+ if not hasattr( irods, '__rods_strerror_map' ):
+ irods.__rods_strerror_map = {}
+ for name in dir( irods ):
+ v = getattr( irods, name )
+ if type( v ) == int and v < 0:
+ irods.__rods_strerror_map[ v ] = name
+ return irods.__rods_strerror_map.get( errno, 'GALAXY_NO_ERRNO_MAPPING_FOUND' )
+
+irods.strerror = _rods_strerror
+
+def rods_connect():
+ """
+ A basic iRODS connection mechanism that connects using the current iRODS
+ environment
+ """
+ status, env = irods.getRodsEnv()
+ assert status == 0, 'connect(): getRodsEnv() failed (%s): %s' % ( status, irods.strerror( status ) )
+ conn, err = irods.rcConnect( env.rodsHost,
+ env.rodsPort,
+ env.rodsUserName,
+ env.rodsZone )
+ assert err.status == 0, 'connect(): rcConnect() failed (%s): %s' % ( err.status, err.msg )
+ status, pw = irods.obfGetPw()
+ assert status == 0, 'connect(): getting password with obfGetPw() failed (%s): %s' % ( status, irods.strerror( status ) )
+ status = irods.clientLoginWithObfPassword( conn, pw )
+ assert status == 0, 'connect(): logging in with clientLoginWithObfPassword() failed (%s): %s' % ( status, irods.strerror( status ) )
+ return env, conn
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/e9924d0e725e/
Changeset: e9924d0e725e
User: ross...(a)gmail.com
Date: 2013-10-05 01:51:18
Summary: local changes
Affected #: 6 files
diff -r 0bdee4933794f61eec10e80d2685ae07e073a796 -r e9924d0e725e0fe631cb44cef873e462a24f8fbc lib/galaxy/tools/actions/__init__.py
--- a/lib/galaxy/tools/actions/__init__.py
+++ b/lib/galaxy/tools/actions/__init__.py
@@ -200,7 +200,7 @@
if data.dbkey not in [None, '?']:
input_dbkey = data.dbkey
data_name_sane = re.sub('[^a-zA-Z0-9_]+', '', data.name)
- if trans.app.config.use_data_id_on_string:
+ if not trans.app.config.use_data_id_on_string:
# we want names in our on_strings not numbers
input_names.append(data_name_sane)
else:
diff -r 0bdee4933794f61eec10e80d2685ae07e073a796 -r e9924d0e725e0fe631cb44cef873e462a24f8fbc scripts/functional_tests.py
--- a/scripts/functional_tests.py
+++ b/scripts/functional_tests.py
@@ -454,4 +454,5 @@
return 1
if __name__ == "__main__":
+ print '\n\n\n\n#### SGE_ROOT=', os.environ.get('SGE_ROOT','##### no SGE_ROOT!'),'\n\n\n'
sys.exit( main() )
diff -r 0bdee4933794f61eec10e80d2685ae07e073a796 -r e9924d0e725e0fe631cb44cef873e462a24f8fbc tool_conf.xml.sample
--- a/tool_conf.xml.sample
+++ b/tool_conf.xml.sample
@@ -1,6 +1,7 @@
<?xml version="1.0"?><toolbox><section name="Get Data" id="getext">
+ <tool file="rlGAT/rlGAT.xml"/><tool file="data_source/upload.xml"/><tool file="data_source/ucsc_tablebrowser.xml" /><tool file="data_source/ucsc_tablebrowser_test.xml" />
diff -r 0bdee4933794f61eec10e80d2685ae07e073a796 -r e9924d0e725e0fe631cb44cef873e462a24f8fbc tools/rgedgeR/rgedgeRpaired.xml
--- a/tools/rgedgeR/rgedgeRpaired.xml
+++ b/tools/rgedgeR/rgedgeRpaired.xml
@@ -682,11 +682,12 @@
# DESeq2
require('DESeq2')
library('RColorBrewer')
- pdata = data.frame(Name=colnames(workCM),Rx=group,subjects=subjects,row.names=colnames(workCM))
if (length(subjects) == 0)
{
+ pdata = data.frame(Name=colnames(workCM),Rx=group,row.names=colnames(workCM))
deSEQds = DESeqDataSetFromMatrix(countData = workCM, colData = pdata, design = formula(~ Rx))
} else {
+ pdata = data.frame(Name=colnames(workCM),Rx=group,subjects=subjects,row.names=colnames(workCM))
deSEQds = DESeqDataSetFromMatrix(countData = workCM, colData = pdata, design = formula(~ subjects + Rx))
}
#DESeq2 = DESeq(deSEQds,fitType='local',pAdjustMethod=fdrtype)
diff -r 0bdee4933794f61eec10e80d2685ae07e073a796 -r e9924d0e725e0fe631cb44cef873e462a24f8fbc tools/rgenetics/bams2mx.py
--- a/tools/rgenetics/bams2mx.py
+++ b/tools/rgenetics/bams2mx.py
@@ -296,6 +296,8 @@
bcolname = [x.split(',')[2].replace("'",'').replace('"','') for x in bamdat]
assert len(bamf) == len(baif) == len(bcolname), '##ERROR bams2mx: Count of bam/bai/cname not consistent - %d/%d/%d' % (len(bamf),len(baif),len(bcolname))
for i,b in enumerate(bamf):
+ if b.lower() == "none":
+ continue
assert os.path.isfile(b),'## Supplied input bam file "%s" not found' % b
bn = os.path.basename(b)
tf,tbam = tempfile.mkstemp(suffix='%s.bam' % bn,dir=opts.tmpdir)
diff -r 0bdee4933794f61eec10e80d2685ae07e073a796 -r e9924d0e725e0fe631cb44cef873e462a24f8fbc tools/rgenetics/bams2mx.xml
--- a/tools/rgenetics/bams2mx.xml
+++ b/tools/rgenetics/bams2mx.xml
@@ -27,7 +27,7 @@
truevalue="true" falsevalue="false" checked="no" help="Controls counting of optical/pcr duplicates if flagged by upstream processing - leave alone unless you know what you are doing" /><param name="firstbamf" type="data" label="BAM file from your history to count reads overlapping BED regions" format="bam" /><repeat name="bamfiles" title="Additional BAM files from your history to count reads overlapping BED regions" min="10">
- <param name="bamf" type="data" label="Additional BAM file from your history" format="bam" size="100"/>
+ <param name="bamf" type="data" label="Additional BAM file from your history" format="bam" size="100" optional="true"/></repeat></inputs><outputs>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: natefoo: Retrying operations that can encounter transient failures when running jobs.
by commits-noreply@bitbucket.org 04 Oct '13
by commits-noreply@bitbucket.org 04 Oct '13
04 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/384240b8cd29/
Changeset: 384240b8cd29
User: natefoo
Date: 2013-10-04 22:11:45
Summary: Retrying operations that can encounter transient failures when running jobs.
Affected #: 2 files
diff -r e8dcf9b9fa2914623435997c567b415b1ad416f1 -r 384240b8cd29963f302a0349476cf83734cfb5df lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -2,6 +2,7 @@
Support for running a tool in Galaxy via an internal job management system
"""
+import time
import copy
import datetime
import galaxy
@@ -17,7 +18,7 @@
import traceback
from galaxy import model, util
from galaxy.datatypes import metadata
-from galaxy.exceptions import ObjectInvalid
+from galaxy.exceptions import ObjectInvalid, ObjectNotFound
from galaxy.jobs.actions.post import ActionBox
from galaxy.jobs.mapper import JobRunnerMapper
from galaxy.jobs.runners import BaseJobRunner
@@ -926,6 +927,17 @@
context = self.get_dataset_finish_context( job_context, dataset_assoc.dataset.dataset )
#should this also be checking library associations? - can a library item be added from a history before the job has ended? - lets not allow this to occur
for dataset in dataset_assoc.dataset.dataset.history_associations + dataset_assoc.dataset.dataset.library_associations: #need to update all associated output hdas, i.e. history was shared with job running
+ trynum = 0
+ while trynum < self.app.config.retry_job_output_collection:
+ try:
+ # Attempt to short circuit NFS attribute caching
+ os.stat( dataset.dataset.file_name )
+ os.chown( dataset.dataset.file_name, os.getuid(), -1 )
+ trynum = self.app.config.retry_job_output_collection
+ except ( OSError, ObjectNotFound ), e:
+ trynum += 1
+ log.warning( 'Error accessing %s, will retry: %s', dataset.dataset.file_name, e )
+ time.sleep( 2 )
dataset.blurb = 'done'
dataset.peek = 'no peek'
dataset.info = (dataset.info or '')
diff -r e8dcf9b9fa2914623435997c567b415b1ad416f1 -r 384240b8cd29963f302a0349476cf83734cfb5df lib/galaxy/jobs/runners/drmaa.py
--- a/lib/galaxy/jobs/runners/drmaa.py
+++ b/lib/galaxy/jobs/runners/drmaa.py
@@ -166,7 +166,17 @@
# runJob will raise if there's a submit problem
if self.external_runJob_script is None:
- external_job_id = self.ds.runJob(jt)
+ # TODO: create a queue for retrying submission indefinitely
+ # TODO: configurable max tries and sleep
+ trynum = 0
+ external_job_id = None
+ while external_job_id is None and trynum < 5:
+ try:
+ external_job_id = self.ds.runJob(jt)
+ except drmaa.InternalException, e:
+ trynum += 1
+ log.warning( '(%s) drmaa.Session.runJob() failed, will retry: %s', galaxy_id_tag, e )
+ time.sleep( 5 )
else:
job_wrapper.change_ownership_for_run()
log.debug( '(%s) submitting with credentials: %s [uid: %s]' % ( galaxy_id_tag, job_wrapper.user_system_pwent[0], job_wrapper.user_system_pwent[2] ) )
@@ -202,16 +212,9 @@
try:
assert external_job_id not in ( None, 'None' ), '(%s/%s) Invalid job id' % ( galaxy_id_tag, external_job_id )
state = self.ds.jobStatus( external_job_id )
- # InternalException was reported to be necessary on some DRMs, but
- # this could cause failures to be detected as completion! Please
- # report if you experience problems with this.
- except ( drmaa.InvalidJobException, drmaa.InternalException ), e:
- # we should only get here if an orphaned job was put into the queue at app startup
- log.info( "(%s/%s) job left DRM queue with following message: %s" % ( galaxy_id_tag, external_job_id, e ) )
- self.work_queue.put( ( self.finish_job, ajs ) )
- continue
- except drmaa.DrmCommunicationException, e:
- log.warning( "(%s/%s) unable to communicate with DRM: %s" % ( galaxy_id_tag, external_job_id, e ))
+ # TODO: probably need to keep track of InvalidJobException count and remove after it exceeds some configurable
+ except ( drmaa.DrmCommunicationException, drmaa.InternalException, drmaa.InvalidJobException ), e:
+ log.warning( "(%s/%s) job check resulted in %s: %s", galaxy_id_tag, external_job_id, e.__class__.name, e )
new_watched.append( ajs )
continue
except Exception, e:
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: final tweaks to activation emails
by commits-noreply@bitbucket.org 04 Oct '13
by commits-noreply@bitbucket.org 04 Oct '13
04 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e8dcf9b9fa29/
Changeset: e8dcf9b9fa29
User: martenson
Date: 2013-10-04 21:40:15
Summary: final tweaks to activation emails
Affected #: 2 files
diff -r 850ea902e067feed6dcb73a8cc188158ec7a167d -r e8dcf9b9fa2914623435997c567b415b1ad416f1 lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -757,7 +757,7 @@
"More about the Galaxy Project can be found at galaxyproject.org\n\n"
"Your Galaxy Team" % ( trans.user.username, email, datetime.utcnow().strftime( "%D" ), trans.request.host, activation_link,trans.app.config.terms_url, trans.app.config.error_email_to, trans.app.config.instance_resource_url ))
to = email
- frm = "Galaxy Project <" + str (trans.app.config.activation_email) + ">"
+ frm = trans.app.config.activation_email
subject = 'Galaxy Account Activation'
try:
util.send_mail( frm, to, subject, body, trans.app.config )
diff -r 850ea902e067feed6dcb73a8cc188158ec7a167d -r e8dcf9b9fa2914623435997c567b415b1ad416f1 universe_wsgi.ini.sample
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -273,11 +273,9 @@
#error_email_to = None
# Activation email is used as a sender ('from' field) for the account activation mail.
+# We recommend using string in the following format: Galaxy Project <galaxy-no-reply(a)example.com>
#activation_email = None
-# URL of the terms and conditions of the service. Used in activation emails.
-#terms_url = https://main.g2.bx.psu.edu/static/terms.html
-
# URL of the support resource for the galaxy instance. Used in activation emails.
#instance_resource_url = http://wiki.galaxyproject.org/
@@ -379,7 +377,7 @@
#citation_url = http://wiki.g2.bx.psu.edu/Citing%20Galaxy
# The URL linked by the "Terms and Conditions" link in the "Help" menu, as well
-# as on the user registration and login forms.
+# as on the user registration and login forms and in the activation emails.
#terms_url = None
# The URL linked by the "Galaxy Q&A" link in the "Help" menu
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: made activation email even better
by commits-noreply@bitbucket.org 04 Oct '13
by commits-noreply@bitbucket.org 04 Oct '13
04 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/850ea902e067/
Changeset: 850ea902e067
User: martenson
Date: 2013-10-04 21:30:38
Summary: made activation email even better
Affected #: 3 files
diff -r 363b639c2b4816b566c38dde4dccd352a9e4f0ff -r 850ea902e067feed6dcb73a8cc188158ec7a167d lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -136,6 +136,8 @@
self.user_activation_on = string_as_bool( kwargs.get( 'user_activation_on', False ) )
self.activation_grace_period = kwargs.get( 'activation_grace_period', None )
self.inactivity_box_content = kwargs.get( 'inactivity_box_content', None )
+ self.terms_url = kwargs.get( 'terms_url', None )
+ self.instance_resource_url = kwargs.get( 'instance_resource_url', None )
self.registration_warning_message = kwargs.get( 'registration_warning_message', None )
# Get the disposable email domains blacklist file and its contents
self.blacklist_location = kwargs.get( 'blacklist_file', None )
diff -r 363b639c2b4816b566c38dde4dccd352a9e4f0ff -r 850ea902e067feed6dcb73a8cc188158ec7a167d lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -752,12 +752,12 @@
body = ("Hello %s,\n\n"
"In order to complete the activation process for %s begun on %s at %s, please click on the following link to verify your account:\n\n"
"%s \n\n"
- "By clicking on the above link and opening a Galaxy account you are also confirming that you have read and agreed to Galaxy's Terms and Conditions for use of this service (https://main.g2.bx.psu.edu/static/terms.html) This includes a quota limit of one account per user. Attempts to subvert this limit by creating multiple accounts or through any other method may result in termination of all associated accounts and data.\n\n"
- "Please contact us if you need help with your account at: galaxy-bugs(a)bx.psu.edu\n"
- "More about Galaxy can be found on our wiki: galaxyproject.org\n\n"
- "Your Galaxy Team" % ( trans.user.username, email, datetime.utcnow().strftime( "%D" ), trans.request.host, activation_link ))
+ "By clicking on the above link and opening a Galaxy account you are also confirming that you have read and agreed to Galaxy's Terms and Conditions for use of this service (%s). This includes a quota limit of one account per user. Attempts to subvert this limit by creating multiple accounts or through any other method may result in termination of all associated accounts and data.\n\n"
+ "Please contact us if you need help with your account at: %s. You can also browse resources available at: %s. \n\n"
+ "More about the Galaxy Project can be found at galaxyproject.org\n\n"
+ "Your Galaxy Team" % ( trans.user.username, email, datetime.utcnow().strftime( "%D" ), trans.request.host, activation_link,trans.app.config.terms_url, trans.app.config.error_email_to, trans.app.config.instance_resource_url ))
to = email
- frm = trans.app.config.admin_email
+ frm = "Galaxy Project <" + str (trans.app.config.activation_email) + ">"
subject = 'Galaxy Account Activation'
try:
util.send_mail( frm, to, subject, body, trans.app.config )
diff -r 363b639c2b4816b566c38dde4dccd352a9e4f0ff -r 850ea902e067feed6dcb73a8cc188158ec7a167d universe_wsgi.ini.sample
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -275,6 +275,12 @@
# Activation email is used as a sender ('from' field) for the account activation mail.
#activation_email = None
+# URL of the terms and conditions of the service. Used in activation emails.
+#terms_url = https://main.g2.bx.psu.edu/static/terms.html
+
+# URL of the support resource for the galaxy instance. Used in activation emails.
+#instance_resource_url = http://wiki.galaxyproject.org/
+
# E-mail domains blacklist is used for filtering out users that are using disposable email address
# during the registration. If their address domain matches any domain in the BL they are refused the registration.
#blacklist_file = config/disposable_email_blacklist.conf
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: Override the default Galaxy style for unordered lists in the Tool Shed so bullets points will display, and enhance tool config and readme file image declarations to allow for image files to be contained in a repository directory named something starting with the string 'http'.
by commits-noreply@bitbucket.org 04 Oct '13
by commits-noreply@bitbucket.org 04 Oct '13
04 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/363b639c2b48/
Changeset: 363b639c2b48
User: greg
Date: 2013-10-04 20:51:42
Summary: Override the default Galaxy style for unordered lists in the Tool Shed so bullets points will display, and enhance tool config and readme file image declarations to allow for image files to be contained in a repository directory named something starting with the string 'http'.
Affected #: 3 files
diff -r 3cabb73f554e53064f6fb88234e2f535caa39bb4 -r 363b639c2b4816b566c38dde4dccd352a9e4f0ff lib/tool_shed/util/readme_util.py
--- a/lib/tool_shed/util/readme_util.py
+++ b/lib/tool_shed/util/readme_util.py
@@ -68,6 +68,7 @@
encoding_errors='replace' )
text_of_reasonable_length = text_of_reasonable_length.render( static_path=web.url_for( '/static' ),
host_url=web.url_for( '/', qualified=True ) )
+ text_of_reasonable_length = unicodify( text_of_reasonable_length )
else:
text_of_reasonable_length = suc.to_html_string( text_of_reasonable_length )
readme_files_dict[ readme_file_name ] = text_of_reasonable_length
diff -r 3cabb73f554e53064f6fb88234e2f535caa39bb4 -r 363b639c2b4816b566c38dde4dccd352a9e4f0ff lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -1523,10 +1523,11 @@
text = text.replace( './static/images', '' )
# Eliminate the default setting of /static/images since the routes will properly display images contained in that directory.
text = text.replace( '/static/images', '' )
- # Use regex to instantiate routes into the defined image paths.
+ # Use regex to instantiate routes into the defined image paths, but replace paths that start with neither http:// nor https://,
+ # which will allow for settings like .. images:: http_files/images/help.png
for match in re.findall( '.. image:: (?!http)/?(.+)', text ):
text = text.replace( match, match.replace( '/', '%2F' ) )
- text = re.sub( '.. image:: (?!http)/?(.+)', '.. image:: %s/\\1' % route_to_images, text )
+ text = re.sub( r'\.\. image:: (?!https?://)/?(.+)', r'.. image:: %s/\1' % route_to_images, text )
return text
def set_only_if_compiling_contained_td( repository, required_repository ):
diff -r 3cabb73f554e53064f6fb88234e2f535caa39bb4 -r 363b639c2b4816b566c38dde4dccd352a9e4f0ff templates/webapps/tool_shed/repository/common.mako
--- a/templates/webapps/tool_shed/repository/common.mako
+++ b/templates/webapps/tool_shed/repository/common.mako
@@ -584,10 +584,7 @@
</%def><%def name="render_readme( readme, pad, parent, row_counter )">
- <%
- encoded_id = trans.security.encode_id( readme.id )
- from galaxy.util import unicodify
- %>
+ <% encoded_id = trans.security.encode_id( readme.id ) %><tr class="datasetRow"
%if parent is not None:
parent="${parent}"
@@ -595,15 +592,7 @@
id="libraryItem-rr-${encoded_id}"><td style="padding-left: ${pad+20}px;"><table id="readme_files">
- <tr>
- <td>
- % if readme.name.endswith( '.rst' ):
- ${ unicodify( readme.text ) }
- %else:
- ${ readme.text }
- %endif
- </td>
- </tr>
+ <tr><td>${ readme.text }</td></tr></table></td></tr>
@@ -689,6 +678,8 @@
word-break:keep-all;
word-wrap:break-word;
line-break:strict; }
+ ul{ list-style-type: disc;
+ padding-left: 20px; }
</style></%def>
@@ -1163,10 +1154,6 @@
</div>
%endif
%if tool_test_results_root_folder:
- ##${render_table_wrap_style( "failed_test_table" )}
- ##${render_table_wrap_style( "missing_table" )}
- ##${render_table_wrap_style( "not_tested_table" )}
- ##${render_table_wrap_style( "passed_tests_table" )}
${render_table_wrap_style( "test_environment" )}
<p/><div class="toolForm">
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: natefoo: Fix history rename_async under uwsgi.
by commits-noreply@bitbucket.org 04 Oct '13
by commits-noreply@bitbucket.org 04 Oct '13
04 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3cabb73f554e/
Changeset: 3cabb73f554e
User: natefoo
Date: 2013-10-04 20:50:39
Summary: Fix history rename_async under uwsgi.
Affected #: 1 file
diff -r 2dcd992a28f36431bf5a76c5662896256b172175 -r 3cabb73f554e53064f6fb88234e2f535caa39bb4 lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -627,10 +627,11 @@
else:
assert history.user == trans.user
# Rename
- history.name = sanitize_html( new_name )
+ new_name = sanitize_html( new_name )
+ history.name = new_name
trans.sa_session.add( history )
trans.sa_session.flush()
- return history.name
+ return new_name
@web.expose
@web.require_login( "use Galaxy histories" )
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: modified activation email body, now it should not end up in spam folders, hopefully
by commits-noreply@bitbucket.org 04 Oct '13
by commits-noreply@bitbucket.org 04 Oct '13
04 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/2dcd992a28f3/
Changeset: 2dcd992a28f3
User: martenson
Date: 2013-10-04 20:24:35
Summary: modified activation email body, now it should not end up in spam folders, hopefully
Affected #: 1 file
diff -r 6e063ca1960f9fd0539ad974f4e98520e31fc0a9 -r 2dcd992a28f36431bf5a76c5662896256b172175 lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -749,13 +749,16 @@
"""
activation_link = self.prepare_activation_link( trans, email )
- body = ("Hi %s,\n\n"
- "Please click the activation link below in order to activate your account.\n\n"
- "Activation link: %s \n\n"
- "Your Galaxy Team" % ( email, activation_link ))
+ body = ("Hello %s,\n\n"
+ "In order to complete the activation process for %s begun on %s at %s, please click on the following link to verify your account:\n\n"
+ "%s \n\n"
+ "By clicking on the above link and opening a Galaxy account you are also confirming that you have read and agreed to Galaxy's Terms and Conditions for use of this service (https://main.g2.bx.psu.edu/static/terms.html) This includes a quota limit of one account per user. Attempts to subvert this limit by creating multiple accounts or through any other method may result in termination of all associated accounts and data.\n\n"
+ "Please contact us if you need help with your account at: galaxy-bugs(a)bx.psu.edu\n"
+ "More about Galaxy can be found on our wiki: galaxyproject.org\n\n"
+ "Your Galaxy Team" % ( trans.user.username, email, datetime.utcnow().strftime( "%D" ), trans.request.host, activation_link ))
to = email
- frm = trans.app.config.activation_email
- subject = 'How to activate your Galaxy account'
+ frm = trans.app.config.admin_email
+ subject = 'Galaxy Account Activation'
try:
util.send_mail( frm, to, subject, body, trans.app.config )
return True
@@ -767,9 +770,6 @@
Prepares the account activation link for the user.
"""
activation_token = self.get_activation_token( trans, email )
- host = trans.request.host.split( ':' )[ 0 ]
- if host == 'localhost':
- host = socket.getfqdn()
activation_link = str( trans.request.host ) + url_for( controller='user', action='activate' ) + "?activation_token=" + str( activation_token ) + "&email=" + urllib.quote( email )
return activation_link
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: Dave Bouvier: Fix for the wrong extraction path with certain archives' internal structures.
by commits-noreply@bitbucket.org 04 Oct '13
by commits-noreply@bitbucket.org 04 Oct '13
04 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/6e063ca1960f/
Changeset: 6e063ca1960f
User: Dave Bouvier
Date: 2013-10-04 18:53:50
Summary: Fix for the wrong extraction path with certain archives' internal structures.
Affected #: 1 file
diff -r e2839b5b365966a09b6ad9fd4140b0f067621e93 -r 6e063ca1960f9fd0539ad974f4e98520e31fc0a9 lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
@@ -20,7 +20,7 @@
self.file_type = 'tar'
elif iszip( file_path ) and not isjar( file_path ):
self.file_type = 'zip'
- self.file_name = os.path.splitext( file_path )[ 0 ]
+ self.file_name = os.path.splitext( os.path.basename( file_path ) )[ 0 ]
if self.file_name.endswith( '.tar' ):
self.file_name = os.path.splitext( self.file_name )[ 0 ]
self.type = self.file_type
@@ -31,11 +31,6 @@
raise NameError( 'File type %s specified, no open method found.' % self.file_type )
def extract( self, path ):
- full_path = self.extraction_path( path )
- self.archive.extractall( full_path )
- return full_path
-
- def extraction_path( self, path ):
'''Determine the path to which the archive should be extracted.'''
contents = self.getmembers()
extraction_path = path
@@ -43,6 +38,9 @@
# The archive contains a single file, return the extraction path.
if self.isfile( contents[ 0 ] ):
extraction_path = os.path.join( path, self.file_name )
+ if not os.path.exists( extraction_path ):
+ os.makedirs( extraction_path )
+ self.archive.extractall( extraction_path )
else:
# Sort filenames within the archive by length, because if the shortest path entry in the archive is a directory,
# and the next entry has the directory prepended, then everything following that entry must be within that directory.
@@ -63,11 +61,17 @@
parent = self.getmember( parent_name )
first_child = filenames[ 1 ]
if first_child.startswith( parent_name ) and parent is not None and self.isdir( parent ):
- extraction_path = os.path.join( path, self.getname( parent ) )
+ if self.getname( parent ) == self.file_name:
+ self.archive.extractall( os.path.join( path ) )
+ extraction_path = os.path.join( path, self.file_name )
+ else:
+ self.archive.extractall( os.path.join( path ) )
+ extraction_path = os.path.join( path, self.getname( parent ) )
else:
extraction_path = os.path.join( path, self.file_name )
- if not os.path.exists( extraction_path ):
- os.makedirs( extraction_path )
+ if not os.path.exists( extraction_path ):
+ os.makedirs( extraction_path )
+ self.archive.extractall( os.path.join( extraction_path ) )
return os.path.abspath( extraction_path )
def getmembers_tar( self ):
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
6 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/1a76c4302896/
Changeset: 1a76c4302896
User: dannon
Date: 2013-10-02 01:58:31
Summary: Backout re-encoding from 10711, since it's done in send_redirect now.
Affected #: 1 file
diff -r e416f8c710b63d7fc9f682539929e9823c26f041 -r 1a76c43028967403168b7750879dc21c25c53919 lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -708,7 +708,6 @@
redirect_url = kwd['redirect_url'] % urllib.quote_plus( kwd['display_url'] )
except:
redirect_url = kwd['redirect_url'] # not all will need custom text
- redirect_url = redirect_url.encode( 'utf-8' )
current_user_roles = trans.get_current_user_roles()
if trans.app.security_agent.dataset_is_public( data.dataset ):
return trans.response.send_redirect( redirect_url ) # anon access already permitted by rbac
https://bitbucket.org/galaxy/galaxy-central/commits/2172bff6e3bb/
Changeset: 2172bff6e3bb
User: dannon
Date: 2013-10-04 17:42:21
Summary: Fix imports in datasets controller, removing *s
Affected #: 1 file
diff -r 1a76c43028967403168b7750879dc21c25c53919 -r 2172bff6e3bbafed3749d8d3d1bd558903d00a5b lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -1,23 +1,20 @@
import logging
-import mimetypes
import os
import string
-import sys
import tempfile
import urllib
import zipfile
-from galaxy.web.base.controller import *
-from galaxy.web.framework.helpers import time_ago, iff, grids
-from galaxy import util, datatypes, web, model
-from galaxy.datatypes.display_applications.util import encode_dataset_user, decode_dataset_user
+from galaxy import datatypes, eggs, model, util, web
+from galaxy.datatypes.display_applications.util import decode_dataset_user, encode_dataset_user
+from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings
+from galaxy.util import inflector
from galaxy.util.sanitize_html import sanitize_html
-from galaxy.util import inflector
-from galaxy.model.item_attrs import *
+from galaxy.web.base.controller import BaseUIController, ERROR, SUCCESS, url_for, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin
+from galaxy.web.framework.helpers import grids, iff, time_ago
from galaxy.web.framework.helpers import to_unicode
-import pkg_resources;
-pkg_resources.require( "Paste" )
+eggs.require( "Paste" )
import paste.httpexceptions
tmpd = tempfile.mkdtemp()
https://bitbucket.org/galaxy/galaxy-central/commits/2aa0bfdf1534/
Changeset: 2aa0bfdf1534
User: dannon
Date: 2013-10-04 17:43:33
Summary: Dataset controller: log needed to exist just post import; this was previously using log from one of the * imports...
Affected #: 1 file
diff -r 2172bff6e3bbafed3749d8d3d1bd558903d00a5b -r 2aa0bfdf15340da4cf203713066bcd71cd5b3f2e lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -17,6 +17,8 @@
eggs.require( "Paste" )
import paste.httpexceptions
+log = logging.getLogger( __name__ )
+
tmpd = tempfile.mkdtemp()
comptypes=[]
ziptype = '32'
@@ -37,7 +39,6 @@
pass
os.rmdir( tmpd )
-log = logging.getLogger( __name__ )
error_report_template = """
GALAXY TOOL ERROR REPORT
https://bitbucket.org/galaxy/galaxy-central/commits/0b51e7ba3ac5/
Changeset: 0b51e7ba3ac5
User: dannon
Date: 2013-10-04 17:48:07
Summary: Datasets controller, fix misc invalid references and function calls.
Affected #: 1 file
diff -r 2aa0bfdf15340da4cf203713066bcd71cd5b3f2e -r 0b51e7ba3ac525b00577f6c67a36f4bdc61d8e2f lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -202,7 +202,7 @@
trans.response.set_content_type( 'text/plain' )
exit_code = ""
try:
- job = _get_job_for_dataset( dataset_id )
+ job = self._get_job_for_dataset( dataset_id )
exit_code = job.exit_code
except:
exit_code = "Invalid dataset ID or you are not allowed to access this dataset"
@@ -613,7 +613,7 @@
return trans.show_error_message( "The specified dataset does not exist." )
# Rate dataset.
- dataset_rating = self.rate_item( rate_item, trans.get_user(), dataset, rating )
+ dataset_rating = self.rate_item( trans.sa_session, trans.get_user(), dataset, rating )
return self.get_ave_item_rating_data( trans.sa_session, dataset )
https://bitbucket.org/galaxy/galaxy-central/commits/8e6d9ee6bb64/
Changeset: 8e6d9ee6bb64
User: dannon
Date: 2013-10-04 17:56:40
Summary: Strip unused code/vars from datasets controller
Affected #: 1 file
diff -r 0b51e7ba3ac525b00577f6c67a36f4bdc61d8e2f -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -561,19 +561,15 @@
@web.expose
def imp( self, trans, dataset_id=None, **kwd ):
""" Import another user's dataset via a shared URL; dataset is added to user's current history. """
- msg = ""
-
# Set referer message.
referer = trans.request.referer
if referer is not "":
referer_message = "<a href='%s'>return to the previous page</a>" % referer
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
-
# Error checking.
if not dataset_id:
return trans.show_error_message( "You must specify a dataset to import. You can %s." % referer_message, use_panels=True )
-
# Do import.
cur_history = trans.get_history( create=True )
status, message = self._copy_datasets( trans, [ dataset_id ], [ cur_history ], imported=True )
@@ -613,7 +609,7 @@
return trans.show_error_message( "The specified dataset does not exist." )
# Rate dataset.
- dataset_rating = self.rate_item( trans.sa_session, trans.get_user(), dataset, rating )
+ self.rate_item( trans.sa_session, trans.get_user(), dataset, rating )
return self.get_ave_item_rating_data( trans.sa_session, dataset )
@@ -706,7 +702,6 @@
redirect_url = kwd['redirect_url'] % urllib.quote_plus( kwd['display_url'] )
except:
redirect_url = kwd['redirect_url'] # not all will need custom text
- current_user_roles = trans.get_current_user_roles()
if trans.app.security_agent.dataset_is_public( data.dataset ):
return trans.response.send_redirect( redirect_url ) # anon access already permitted by rbac
if self._can_access_dataset( trans, data ):
@@ -807,7 +802,6 @@
id = None
try:
id = trans.app.security.decode_id( dataset_id )
- history = trans.get_history()
hda = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id )
assert hda, 'Invalid HDA: %s' % id
# Walk up parent datasets to find the containing history
@@ -854,7 +848,7 @@
hda.mark_undeleted()
trans.sa_session.flush()
trans.log_event( "Dataset id %s has been undeleted" % str(id) )
- except Exception, e:
+ except Exception:
msg = 'HDA undeletion failed (encoded: %s, decoded: %s)' % ( dataset_id, id )
log.exception( msg )
trans.log_event( msg )
@@ -925,7 +919,7 @@
except:
log.exception( 'Unable to purge dataset (%s) on purge of HDA (%s):' % ( hda.dataset.id, hda.id ) )
trans.sa_session.flush()
- except Exception, e:
+ except Exception:
msg = 'HDA purge failed (encoded: %s, decoded: %s)' % ( dataset_id, id )
log.exception( msg )
trans.log_event( msg )
@@ -1036,7 +1030,6 @@
@web.expose
def copy_datasets( self, trans, source_history=None, source_dataset_ids="", target_history_id=None, target_history_ids="", new_history_name="", do_copy=False, **kwd ):
- params = util.Params( kwd )
user = trans.get_user()
if source_history is not None:
history = self.get_history(trans, source_history)
https://bitbucket.org/galaxy/galaxy-central/commits/e2839b5b3659/
Changeset: e2839b5b3659
User: dannon
Date: 2013-10-04 17:56:52
Summary: merge
Affected #: 33 files
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -132,8 +132,8 @@
self.admin_users = kwargs.get( "admin_users", "" )
self.mailing_join_addr = kwargs.get('mailing_join_addr',"galaxy-announce-join(a)bx.psu.edu")
self.error_email_to = kwargs.get( 'error_email_to', None )
- self.admin_email = kwargs.get( 'admin_email', None )
- self.user_activation_on = kwargs.get( 'user_activation_on', None )
+ self.activation_email = kwargs.get( 'activation_email', None )
+ self.user_activation_on = string_as_bool( kwargs.get( 'user_activation_on', False ) )
self.activation_grace_period = kwargs.get( 'activation_grace_period', None )
self.inactivity_box_content = kwargs.get( 'inactivity_box_content', None )
self.registration_warning_message = kwargs.get( 'registration_warning_message', None )
@@ -143,10 +143,10 @@
if self.blacklist_location is not None:
self.blacklist_file = resolve_path( kwargs.get( 'blacklist_file', None ), self.root )
try:
- with open(self.blacklist_file) as blacklist:
- self.blacklist_content = [ line.rstrip() for line in blacklist.readlines() ]
+ with open( self.blacklist_file ) as blacklist:
+ self.blacklist_content = [ line.rstrip() for line in blacklist.readlines() ]
except IOError:
- print ( "CONFIGURATION ERROR: Can't open supplied blacklist file from path: " + str( self.blacklist_file ) )
+ print ( "CONFIGURATION ERROR: Can't open supplied blacklist file from path: " + str( self.blacklist_file ) )
self.smtp_server = kwargs.get( 'smtp_server', None )
self.smtp_username = kwargs.get( 'smtp_username', None )
self.smtp_password = kwargs.get( 'smtp_password', None )
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -2351,7 +2351,7 @@
self.tags.append(new_swta)
def to_dict( self, view='collection', value_mapper = None ):
- rval = super( StoredWorkflow, self ).to_dict(self, view=view, value_mapper = value_mapper)
+ rval = super( StoredWorkflow, self ).to_dict( view=view, value_mapper = value_mapper )
tags_str_list = []
for tag in self.tags:
tag_str = tag.user_tname
@@ -3912,7 +3912,7 @@
return [ tool_version.tool_id for tool_version in self.get_versions( app ) ]
def to_dict( self, view='element' ):
- rval = super( ToolVersion, self ).to_dict( self, view )
+ rval = super( ToolVersion, self ).to_dict( view=view )
rval['tool_name'] = self.tool_id
for a in self.parent_tool_association:
rval['parent_tool_id'] = a.parent_id
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -11,6 +11,7 @@
import shutil
import sys
import tempfile
+import threading
import traceback
import types
import urllib
@@ -1333,19 +1334,16 @@
help_header = ""
help_footer = ""
if self.help is not None:
- # Handle tool help image display for tools that are contained in repositories that are in the tool shed or installed into Galaxy.
- # When tool config files use the special string $PATH_TO_IMAGES, the following code will replace that string with the path on disk.
- if self.repository_id and self.help.text.find( '$PATH_TO_IMAGES' ) >= 0:
- if self.app.name == 'galaxy':
- repository = self.sa_session.query( self.app.model.ToolShedRepository ).get( self.app.security.decode_id( self.repository_id ) )
- if repository:
- path_to_images = '/tool_runner/static/images/%s' % self.repository_id
- self.help.text = self.help.text.replace( '$PATH_TO_IMAGES', path_to_images )
- elif self.app.name == 'tool_shed':
- repository = self.sa_session.query( self.app.model.Repository ).get( self.app.security.decode_id( self.repository_id ) )
- if repository:
- path_to_images = '/repository/static/images/%s' % self.repository_id
- self.help.text = self.help.text.replace( '$PATH_TO_IMAGES', path_to_images )
+ if self.repository_id and self.help.text.find( '.. image:: ' ) >= 0:
+ # Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy.
+ lock = threading.Lock()
+ lock.acquire( True )
+ try:
+ self.help.text = shed_util_common.set_image_paths( self.app, self.repository_id, self.help.text )
+ except Exception, e:
+ log.exception( "Exception in parse_help, so images may not be properly displayed:\n%s" % str( e ) )
+ finally:
+ lock.release()
help_pages = self.help.findall( "page" )
help_header = self.help.text
try:
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/web/form_builder.py
--- a/lib/galaxy/web/form_builder.py
+++ b/lib/galaxy/web/form_builder.py
@@ -756,13 +756,6 @@
refresh_on_change=refresh_on_change,
refresh_on_change_values=refresh_on_change_values,
size=size )
- if display is None and initial_value == 'none':
- # Only insert an initial "Select one" option if we are not displaying check boxes
- # or radio buttons and we have not received an initial_value other than 'none'.
- if selected_value == initial_value:
- select_field.add_option( 'Select one', initial_value, selected=True )
- else:
- select_field.add_option( 'Select one', initial_value )
for obj in objs:
if label_attr == 'self':
# Each obj is a string
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,8 +46,12 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # The following route will handle displaying tool shelp images for tools contained in repositories installed from the tool shed.
- webapp.add_route( '/tool_runner/static/images/:repository_id/:image_file', controller='tool_runner', action='display_tool_help_image_in_repository', repository_id=None, image_file=None )
+ # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
+ webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
+ controller='admin_toolshed',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -285,6 +285,30 @@
status=status )
@web.expose
+ def display_image_in_repository( self, trans, **kwd ):
+ """
+ Open an image file that is contained in an installed tool shed repository or that is referenced by a URL for display. The
+ image can be defined in either a README.rst file contained in the repository or the help section of a Galaxy tool config that
+ is contained in the repository. The following image definitions are all supported. The former $PATH_TO_IMAGES is no longer
+ required, and is now ignored.
+ .. image:: https://raw.github.com/galaxy/some_image.png
+ .. image:: $PATH_TO_IMAGES/some_image.png
+ .. image:: /static/images/some_image.gif
+ .. image:: some_image.jpg
+ .. image:: /deep/some_image.png
+ """
+ repository_id = kwd.get( 'repository_id', None )
+ relative_path_to_image_file = kwd.get( 'image_file', None )
+ if repository_id and relative_path_to_image_file:
+ repository = suc.get_tool_shed_repository_by_id( trans, repository_id )
+ if repository:
+ repo_files_dir = repository.repo_files_directory( trans.app )
+ path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
+ if os.path.exists( path_to_file ):
+ return open( path_to_file, 'r' )
+ return None
+
+ @web.expose
@web.require_admin
def find_tools_in_tool_shed( self, trans, **kwd ):
tool_shed_url = kwd[ 'tool_shed_url' ]
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
@@ -98,22 +98,6 @@
**vars )
@web.expose
- def display_tool_help_image_in_repository( self, trans, **kwd ):
- repository_id = kwd.get( 'repository_id', None )
- image_file = kwd.get( 'image_file', None )
- if repository_id and image_file:
- repository = suc.get_tool_shed_repository_by_id( trans, repository_id )
- repo_files_dir = os.path.join( repository.repo_files_directory( trans.app ) )
- default_path = os.path.abspath( os.path.join( repo_files_dir, 'static', 'images', image_file ) )
- if os.path.exists( default_path ):
- return open( default_path, 'r' )
- else:
- path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, image_file )
- if os.path.exists( path_to_file ):
- return open( path_to_file, 'r' )
- return None
-
- @web.expose
def rerun( self, trans, id=None, from_noframe=None, **kwd ):
"""
Given a HistoryDatasetAssociation id, find the job and that created
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -491,12 +491,12 @@
message = "No such user (please note that login is case sensitive)"
elif user.deleted:
message = "This account has been marked deleted, contact your local Galaxy administrator to restore the account."
- if trans.app.config.admin_email is not None:
- message += 'Contact: %s' % trans.app.config.admin_email
+ if trans.app.config.error_email_to is not None:
+ message += ' Contact: %s' % trans.app.config.error_email_to
elif user.external:
message = "This account was created for use with an external authentication method, contact your local Galaxy administrator to activate it."
- if trans.app.config.admin_email is not None:
- message += 'Contact: %s' % trans.app.config.admin_email
+ if trans.app.config.error_email_to is not None:
+ message += ' Contact: %s' % trans.app.config.error_email_to
elif not user.check_password( password ):
message = "Invalid password"
elif trans.app.config.user_activation_on and not user.active: # activation is ON and the user is INACTIVE
@@ -536,8 +536,8 @@
message = 'This account has not been activated yet. The activation link has been sent again. Please check your email address %s.<br>' % email
else:
message = 'This account has not been activated yet but we are unable to send the activation link. Please contact your local Galaxy administrator.'
- if trans.app.config.admin_email is not None:
- message += 'Contact: %s' % trans.app.config.admin_email
+ if trans.app.config.error_email_to is not None:
+ message += ' Contact: %s' % trans.app.config.error_email_to
return message
def is_outside_grace_period ( self, trans, create_time ):
@@ -591,8 +591,8 @@
is_admin = cntrller == 'admin' and trans.user_is_admin
if not trans.app.config.allow_user_creation and not trans.user_is_admin():
message = 'User registration is disabled. Please contact your local Galaxy administrator for an account.'
- if trans.app.config.admin_email is not None:
- message += 'Contact: %s' % trans.app.config.admin_email
+ if trans.app.config.error_email_to is not None:
+ message += ' Contact: %s' % trans.app.config.error_email_to
status = 'error'
else:
if not refresh_frames:
@@ -670,8 +670,10 @@
user = trans.app.model.User( email=email )
user.set_password_cleartext( password )
user.username = username
- if trans.app.config.user_activation_on: # Do not set the active flag in case activation is OFF.
+ if trans.app.config.user_activation_on:
user.active = False
+ else:
+ user.active = True # Activation is off, every new user is active by default.
trans.sa_session.add( user )
trans.sa_session.flush()
trans.app.security_agent.create_private_user_role( user )
@@ -733,8 +735,8 @@
success = True
else:
message = 'Unable to send activation email, please contact your local Galaxy administrator.'
- if trans.app.config.admin_email is not None:
- message += 'Contact: %s' % trans.app.config.admin_email
+ if trans.app.config.error_email_to is not None:
+ message += ' Contact: %s' % trans.app.config.error_email_to
success = False
else: # User activation is OFF, proceed without sending the activation email.
message = 'Now logged in as %s.<br><a target="_top" href="%s">Return to the home page.</a>' % ( user.email, url_for( '/' ) )
@@ -752,7 +754,7 @@
"Activation link: %s \n\n"
"Your Galaxy Team" % ( email, activation_link ))
to = email
- frm = trans.app.config.admin_email
+ frm = trans.app.config.activation_email
subject = 'How to activate your Galaxy account'
try:
util.send_mail( frm, to, subject, body, trans.app.config )
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/webapps/tool_shed/buildapp.py
--- a/lib/galaxy/webapps/tool_shed/buildapp.py
+++ b/lib/galaxy/webapps/tool_shed/buildapp.py
@@ -69,8 +69,12 @@
webapp.add_route( '/view/{owner}', controller='repository', action='sharable_owner' )
webapp.add_route( '/view/{owner}/{name}', controller='repository', action='sharable_repository' )
webapp.add_route( '/view/{owner}/{name}/{changeset_revision}', controller='repository', action='sharable_repository_revision' )
- # The following route will handle displaying tool shelp images for tools contained in repositories.
- webapp.add_route( '/repository/static/images/:repository_id/:image_file', controller='repository', action='display_tool_help_image_in_repository', repository_id=None, image_file=None )
+ # Handle displaying tool help images and README file images for tools contained in repositories.
+ webapp.add_route( '/repository/static/images/:repository_id/:image_file',
+ controller='repository',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
webapp.add_route( '/:controller/:action', action='index' )
webapp.add_route( '/:action', controller='repository', action='index' )
webapp.add_route( '/repos/*path_info', controller='hg', action='handle_request', path_info='/' )
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -666,7 +666,7 @@
# Update repository files for browsing.
suc.update_repository( repo )
changeset_revision = repository.tip( trans.app )
- metadata = self.get_metadata( trans, id, changeset_revision )
+ metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision, metadata_only=True )
repository_type_select_field = rt_util.build_repository_type_select_field( trans, repository=repository )
return trans.fill_template( '/webapps/tool_shed/repository/browse_repository.mako',
repository=repository,
@@ -897,7 +897,10 @@
message = kwd.get( 'message', '' )
status = kwd.get( 'status', 'done' )
repository = suc.get_repository_in_tool_shed( trans, id )
- metadata = self.get_metadata( trans, id, repository.tip( trans.app ) )
+ metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
+ id,
+ repository.tip( trans.app ),
+ metadata_only=True )
if trans.user and trans.user.email:
return trans.fill_template( "/webapps/tool_shed/repository/contact_owner.mako",
repository=repository,
@@ -1015,6 +1018,29 @@
status=status ) )
@web.expose
+ def display_image_in_repository( self, trans, **kwd ):
+ """
+ Open an image file that is contained in repository or that is referenced by a URL for display. The image can be defined in
+ either a README.rst file contained in the repository or the help section of a Galaxy tool config that is contained in the repository.
+ The following image definitions are all supported. The former $PATH_TO_IMAGES is no longer required, and is now ignored.
+ .. image:: https://raw.github.com/galaxy/some_image.png
+ .. image:: $PATH_TO_IMAGES/some_image.png
+ .. image:: /static/images/some_image.gif
+ .. image:: some_image.jpg
+ .. image:: /deep/some_image.png
+ """
+ repository_id = kwd.get( 'repository_id', None )
+ relative_path_to_image_file = kwd.get( 'image_file', None )
+ if repository_id and relative_path_to_image_file:
+ repository = suc.get_repository_in_tool_shed( trans, repository_id )
+ if repository:
+ repo_files_dir = repository.repo_path( trans.app )
+ path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
+ if os.path.exists( path_to_file ):
+ return open( path_to_file, 'r' )
+ return None
+
+ @web.expose
def display_tool( self, trans, repository_id, tool_config, changeset_revision, **kwd ):
message = kwd.get( 'message', '' )
status = kwd.get( 'status', 'done' )
@@ -1023,7 +1049,10 @@
if message:
status = 'error'
tool_state = tool_util.new_state( trans, tool, invalid=False )
- metadata = self.get_metadata( trans, repository_id, changeset_revision )
+ metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
+ repository_id,
+ changeset_revision,
+ metadata_only=True )
try:
return trans.fill_template( "/webapps/tool_shed/repository/tool_form.mako",
repository=repository,
@@ -1052,22 +1081,6 @@
status='error' ) )
@web.expose
- def display_tool_help_image_in_repository( self, trans, **kwd ):
- repository_id = kwd.get( 'repository_id', None )
- image_file = kwd.get( 'image_file', None )
- if repository_id and image_file:
- repository = suc.get_repository_in_tool_shed( trans, repository_id )
- repo_files_dir = os.path.join( repository.repo_path( trans.app ), repository.name )
- default_path = os.path.abspath( os.path.join( repo_files_dir, 'static', 'images', image_file ) )
- if os.path.exists( default_path ):
- return open( default_path, 'r' )
- else:
- path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, image_file )
- if os.path.exists( path_to_file ):
- return open( path_to_file, 'r' )
- return None
-
- @web.expose
def download( self, trans, repository_id, changeset_revision, file_type, **kwd ):
"""Download an archive of the repository files compressed as zip, gz or bz2."""
# FIXME: thgis will currently only download the repository tip, no matter which installable changeset_revision is being viewed.
@@ -1571,12 +1584,6 @@
pubdate=strftime( '%a, %d %b %Y %H:%M:%S UT', gmtime() ),
items=functional_test_results )
- def get_metadata( self, trans, repository_id, changeset_revision ):
- repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, repository_id, changeset_revision )
- if repository_metadata and repository_metadata.metadata:
- return repository_metadata.metadata
- return None
-
@web.json
def get_readme_files( self, trans, **kwd ):
"""
@@ -2409,7 +2416,10 @@
avg_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, repository, webapp_model=trans.model )
display_reviews = util.string_as_bool( kwd.get( 'display_reviews', False ) )
rra = self.get_user_item_rating( trans.sa_session, trans.user, repository, webapp_model=trans.model )
- metadata = self.get_metadata( trans, id, repository.tip( trans.app ) )
+ metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
+ id,
+ repository.tip( trans.app ),
+ metadata_only=True )
repository_type_select_field = rt_util.build_repository_type_select_field( trans, repository=repository )
return trans.fill_template( '/webapps/tool_shed/repository/rate_repository.mako',
repository=repository,
@@ -2806,7 +2816,10 @@
'has_metadata' : has_metadata }
# Make sure we'll view latest changeset first.
changesets.insert( 0, change_dict )
- metadata = self.get_metadata( trans, id, repository.tip( trans.app ) )
+ metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
+ id,
+ repository.tip( trans.app ),
+ metadata_only=True )
return trans.fill_template( '/webapps/tool_shed/repository/view_changelog.mako',
repository=repository,
metadata=metadata,
@@ -2839,7 +2852,7 @@
diffs = []
for diff in patch.diff( repo, node1=ctx_parent.node(), node2=ctx.node() ):
diffs.append( suc.to_html_string( diff ) )
- metadata = self.get_metadata( trans, id, ctx_str )
+ metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, id, ctx_str, metadata_only=True )
# For rendering the prev button.
if ctx_parent:
ctx_parent_rev = ctx_parent.rev()
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
@@ -284,7 +284,8 @@
elif action_type == 'move_file':
td_common_util.move_file( current_dir=current_dir,
source=os.path.join( action_dict[ 'source' ] ),
- destination_dir=os.path.join( action_dict[ 'destination' ] ) )
+ destination=os.path.join( action_dict[ 'destination' ] ),
+ rename_to=action_dict[ 'rename_to' ] )
elif action_type == 'set_environment':
# Currently the only action supported in this category is "environment_variable".
# Build a command line from the prior_installation_required, in case an environment variable is referenced
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
@@ -464,7 +464,7 @@
is_binary_download = True
else:
is_binary_download = False
- elif action_elem:
+ elif action_elem is not None:
# We were provided with a single <action> element to perform certain actions after a platform-specific tarball was downloaded.
elems = [ action_elem ]
else:
@@ -550,11 +550,7 @@
action_dict[ 'directory' ] = action_elem.text
else:
continue
- elif action_type in [ 'move_directory_files', 'move_file' ]:
- # <action type="move_file">
- # <source>misc/some_file</source>
- # <destination>$INSTALL_DIR/bin</destination>
- # </action>
+ elif action_type == 'move_directory_files':
# <action type="move_directory_files">
# <source_directory>bin</source_directory>
# <destination_directory>$INSTALL_DIR/bin</destination_directory>
@@ -563,6 +559,14 @@
move_elem_text = evaluate_template( move_elem.text )
if move_elem_text:
action_dict[ move_elem.tag ] = move_elem_text
+ elif action_type == 'move_file':
+ # <action type="move_file" rename_to="new_file_name">
+ # <source>misc/some_file</source>
+ # <destination>$INSTALL_DIR/bin</destination>
+ # </action>
+ action_dict[ 'source' ] = evaluate_template( action_elem.find( 'source' ).text )
+ action_dict[ 'destination' ] = evaluate_template( action_elem.find( 'destination' ).text )
+ action_dict[ 'rename_to' ] = action_elem.get( 'rename_to' )
elif action_type == 'set_environment':
# <action type="set_environment">
# <environment_variable name="PYTHONPATH" action="append_to">$INSTALL_DIR/lib/python</environment_variable>
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
@@ -12,6 +12,109 @@
log = logging.getLogger( __name__ )
+
+class CompressedFile( object ):
+
+ def __init__( self, file_path, mode='r' ):
+ if istar( file_path ):
+ self.file_type = 'tar'
+ elif iszip( file_path ) and not isjar( file_path ):
+ self.file_type = 'zip'
+ self.file_name = os.path.splitext( file_path )[ 0 ]
+ if self.file_name.endswith( '.tar' ):
+ self.file_name = os.path.splitext( self.file_name )[ 0 ]
+ self.type = self.file_type
+ method = 'open_%s' % self.file_type
+ if hasattr( self, method ):
+ self.archive = getattr( self, method )( file_path, mode )
+ else:
+ raise NameError( 'File type %s specified, no open method found.' % self.file_type )
+
+ def extract( self, path ):
+ full_path = self.extraction_path( path )
+ self.archive.extractall( full_path )
+ return full_path
+
+ def extraction_path( self, path ):
+ '''Determine the path to which the archive should be extracted.'''
+ contents = self.getmembers()
+ extraction_path = path
+ if len( contents ) == 1:
+ # The archive contains a single file, return the extraction path.
+ if self.isfile( contents[ 0 ] ):
+ extraction_path = os.path.join( path, self.file_name )
+ else:
+ # Sort filenames within the archive by length, because if the shortest path entry in the archive is a directory,
+ # and the next entry has the directory prepended, then everything following that entry must be within that directory.
+ # For example, consider tarball for BWA 0.5.9:
+ # bwa-0.5.9.tar.bz2:
+ # bwa-0.5.9/
+ # bwa-0.5.9/bwt.c
+ # bwa-0.5.9/bwt.h
+ # bwa-0.5.9/Makefile
+ # bwa-0.5.9/bwt_gen/
+ # bwa-0.5.9/bwt_gen/Makefile
+ # bwa-0.5.9/bwt_gen/bwt_gen.c
+ # bwa-0.5.9/bwt_gen/bwt_gen.h
+ # When sorted by length, one sees a directory at the root of the tarball, and all other tarball contents as
+ # children of that directory.
+ filenames = sorted( [ self.getname( item ) for item in contents ], cmp=lambda a,b: cmp( len( a ), len( b ) ) )
+ parent_name = filenames[ 0 ]
+ parent = self.getmember( parent_name )
+ first_child = filenames[ 1 ]
+ if first_child.startswith( parent_name ) and parent is not None and self.isdir( parent ):
+ extraction_path = os.path.join( path, self.getname( parent ) )
+ else:
+ extraction_path = os.path.join( path, self.file_name )
+ if not os.path.exists( extraction_path ):
+ os.makedirs( extraction_path )
+ return os.path.abspath( extraction_path )
+
+ def getmembers_tar( self ):
+ return self.archive.getmembers()
+
+ def getmembers_zip( self ):
+ return self.archive.infolist()
+
+ def getname_tar( self, item ):
+ return item.name
+
+ def getname_zip( self, item ):
+ return item.filename
+
+ def getmember( self, name ):
+ for member in self.getmembers():
+ if self.getname( member ) == name:
+ return member
+
+ def getmembers( self ):
+ return getattr( self, 'getmembers_%s' % self.type )()
+
+ def getname( self, member ):
+ return getattr( self, 'getname_%s' % self.type )( member )
+
+ def isdir( self, member ):
+ return getattr( self, 'isdir_%s' % self.type )( member )
+
+ def isdir_tar( self, member ):
+ return member.isdir()
+
+ def isdir_zip( self, member ):
+ if member.filename.endswith( os.sep ):
+ return True
+ return False
+
+ def isfile( self, member ):
+ if not self.isdir( member ):
+ return True
+ return False
+
+ def open_tar( self, filepath, mode ):
+ return tarfile.open( filepath, mode )
+
+ def open_zip( self, filepath, mode ):
+ return zipfile.ZipFile( filepath, mode )
+
def clean_tool_shed_url( base_url ):
if base_url:
if base_url.find( '://' ) > -1:
@@ -92,30 +195,6 @@
dir = url_download( work_dir, downloaded_filename, url, extract=False )
return downloaded_filename
-def extract_tar( file_name, file_path ):
- if isgzip( file_name ) or isbz2( file_name ):
- # Open for reading with transparent compression.
- tar = tarfile.open( file_name, 'r:*', errorlevel=0 )
- else:
- tar = tarfile.open( file_name, errorlevel=0 )
- tar.extractall( path=file_path )
- tar.close()
-
-def extract_zip( archive_path, extraction_path ):
- # TODO: change this method to use zipfile.Zipfile.extractall() when we stop supporting Python 2.5.
- if not zipfile_ok( archive_path ):
- return False
- zip_archive = zipfile.ZipFile( archive_path, 'r' )
- for name in zip_archive.namelist():
- uncompressed_path = os.path.join( extraction_path, name )
- if uncompressed_path.endswith( '/' ):
- if not os.path.isdir( uncompressed_path ):
- os.makedirs( uncompressed_path )
- else:
- file( uncompressed_path, 'wb' ).write( zip_archive.read( name ) )
- zip_archive.close()
- return True
-
def format_traceback():
ex_type, ex, tb = sys.exc_info()
return ''.join( traceback.format_tb( tb ) )
@@ -232,12 +311,18 @@
destination_file = os.path.join( destination_directory, file_name )
shutil.move( source_file, destination_file )
-def move_file( current_dir, source, destination_dir ):
+def move_file( current_dir, source, destination, rename_to=None ):
source_file = os.path.abspath( os.path.join( current_dir, source ) )
- destination_directory = os.path.join( destination_dir )
- if not os.path.isdir( destination_directory ):
+ if rename_to is not None:
+ destination_file = rename_to
+ destination_directory = os.path.join( destination )
+ destination_path = os.path.join( destination_directory, destination_file )
+ else:
+ destination_directory = os.path.join( destination )
+ destination_path = os.path.join( destination_directory, source_file )
+ if not os.path.exists( destination_directory ):
os.makedirs( destination_directory )
- shutil.move( source_file, destination_directory )
+ shutil.move( source_file, destination_path )
def parse_package_elem( package_elem, platform_info_dict=None, include_after_install_actions=True ):
"""
@@ -340,19 +425,6 @@
continue
return actions_elem_tuples
-def tar_extraction_directory( file_path, file_name ):
- """Try to return the correct extraction directory."""
- file_name = file_name.strip()
- extensions = [ '.tar.gz', '.tgz', '.tar.bz2', '.tar', '.zip' ]
- for extension in extensions:
- if file_name.find( extension ) > 0:
- dir_name = file_name[ :-len( extension ) ]
- if os.path.exists( os.path.abspath( os.path.join( file_path, dir_name ) ) ):
- return dir_name
- if os.path.exists( os.path.abspath( os.path.join( file_path, file_name ) ) ):
- return os.path.abspath( file_path )
- raise ValueError( 'Could not find path to file %s' % os.path.abspath( os.path.join( file_path, file_name ) ) )
-
def url_download( install_dir, downloaded_file_name, download_url, extract=True ):
file_path = os.path.join( install_dir, downloaded_file_name )
src = None
@@ -374,32 +446,14 @@
if dst:
dst.close()
if extract:
- if istar( file_path ):
- # <action type="download_by_url">http://sourceforge.net/projects/samtools/files/samtools/0.1.18/samtools-0.1…</action>
- extract_tar( file_path, install_dir )
- dir = tar_extraction_directory( install_dir, downloaded_file_name )
- elif isjar( file_path ):
- dir = os.path.curdir
- elif iszip( file_path ):
- # <action type="download_by_url">http://downloads.sourceforge.net/project/picard/picard-tools/1.56/picard-to…</action>
- zip_archive_extracted = extract_zip( file_path, install_dir )
- dir = zip_extraction_directory( install_dir, downloaded_file_name )
+ if istar( file_path ) or iszip( file_path ):
+ archive = CompressedFile( file_path )
+ extraction_path = archive.extract( install_dir )
else:
- dir = os.path.abspath( install_dir )
+ extraction_path = os.path.abspath( install_dir )
else:
- dir = os.path.abspath( install_dir )
- return dir
-
-def zip_extraction_directory( file_path, file_name ):
- """Try to return the correct extraction directory."""
- files = [ filename for filename in os.listdir( file_path ) if not filename.endswith( '.zip' ) ]
- if len( files ) > 1:
- return os.path.abspath( file_path )
- elif len( files ) == 1:
- # If there is only on file it should be a directory.
- if os.path.isdir( os.path.join( file_path, files[ 0 ] ) ):
- return os.path.abspath( os.path.join( file_path, files[ 0 ] ) )
- raise ValueError( 'Could not find directory for the extracted file %s' % os.path.abspath( os.path.join( file_path, file_name ) ) )
+ extraction_path = os.path.abspath( install_dir )
+ return extraction_path
def zipfile_ok( path_to_archive ):
"""
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/tool_shed/util/metadata_util.py
--- a/lib/tool_shed/util/metadata_util.py
+++ b/lib/tool_shed/util/metadata_util.py
@@ -710,8 +710,16 @@
fp = open( relative_path, 'rb' )
workflow_text = fp.read()
fp.close()
- exported_workflow_dict = json.from_json_string( workflow_text )
- if 'a_galaxy_workflow' in exported_workflow_dict and exported_workflow_dict[ 'a_galaxy_workflow' ] == 'true':
+ if workflow_text:
+ valid_exported_galaxy_workflow = True
+ try:
+ exported_workflow_dict = json.from_json_string( workflow_text )
+ except Exception, e:
+ log.exception( "Skipping file %s since it does not seem to be a valid exported Galaxy workflow: %s" \
+ % str( relative_path ), str( e ) )
+ valid_exported_galaxy_workflow = False
+ if valid_exported_galaxy_workflow and \
+ 'a_galaxy_workflow' in exported_workflow_dict and exported_workflow_dict[ 'a_galaxy_workflow' ] == 'true':
metadata_dict = generate_workflow_metadata( relative_path, exported_workflow_dict, metadata_dict )
# Handle any data manager entries
metadata_dict = generate_data_manager_metadata( app,
@@ -1064,13 +1072,15 @@
"""Get repository metadata from the database"""
return trans.sa_session.query( trans.model.RepositoryMetadata ).get( trans.security.decode_id( id ) )
-def get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision ):
- """Get a specified metadata record for a specified repository."""
- return trans.sa_session.query( trans.model.RepositoryMetadata ) \
- .filter( and_( trans.model.RepositoryMetadata.table.c.repository_id == trans.security.decode_id( id ),
- trans.model.RepositoryMetadata.table.c.changeset_revision == changeset_revision ) ) \
- .first()
-
+def get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision, metadata_only=False ):
+ """Get a specified metadata record for a specified repository in the tool shed."""
+ if metadata_only:
+ repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, id, changeset_revision )
+ if repository_metadata and repository_metadata.metadata:
+ return repository_metadata.metadata
+ return None
+ return suc.get_repository_metadata_by_changeset_revision( trans, id, changeset_revision )
+
def get_repository_metadata_revisions_for_review( repository, reviewed=True ):
repository_metadata_revisions = []
metadata_changeset_revision_hashes = []
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/tool_shed/util/readme_util.py
--- a/lib/tool_shed/util/readme_util.py
+++ b/lib/tool_shed/util/readme_util.py
@@ -1,7 +1,11 @@
import logging
import os
+import threading
+from mako.template import Template
from galaxy import eggs
+from galaxy import web
from galaxy.util import json
+from galaxy.util import rst_to_html
from galaxy.util import unicodify
import tool_shed.util.shed_util_common as suc
from tool_shed.util import common_util
@@ -34,13 +38,39 @@
full_path_to_readme_file = os.path.abspath( os.path.join( tool_path, relative_path_to_readme_file ) )
else:
full_path_to_readme_file = os.path.abspath( relative_path_to_readme_file )
+ text = None
try:
f = open( full_path_to_readme_file, 'r' )
text = unicodify( f.read() )
f.close()
- readme_files_dict[ readme_file_name ] = suc.size_string( text )
except Exception, e:
log.exception( "Error reading README file '%s' from disk: %s" % ( str( relative_path_to_readme_file ), str( e ) ) )
+ text = None
+ if text:
+ text_of_reasonable_length = suc.size_string( text )
+ if text_of_reasonable_length.find( '.. image:: ' ) >= 0:
+ # Handle image display for README files that are contained in repositories in the tool shed or installed into Galaxy.
+ lock = threading.Lock()
+ lock.acquire( True )
+ try:
+ text_of_reasonable_length = suc.set_image_paths( trans.app,
+ trans.security.encode_id( repository.id ),
+ text_of_reasonable_length )
+ except Exception, e:
+ log.exception( "Exception in build_readme_files_dict, so images may not be properly displayed:\n%s" % str( e ) )
+ finally:
+ lock.release()
+ if readme_file_name.endswith( '.rst' ):
+ text_of_reasonable_length = Template( rst_to_html( text_of_reasonable_length ),
+ input_encoding='utf-8',
+ output_encoding='utf-8',
+ default_filters=[ 'decode.utf8' ],
+ encoding_errors='replace' )
+ text_of_reasonable_length = text_of_reasonable_length.render( static_path=web.url_for( '/static' ),
+ host_url=web.url_for( '/', qualified=True ) )
+ else:
+ text_of_reasonable_length = suc.to_html_string( text_of_reasonable_length )
+ readme_files_dict[ readme_file_name ] = text_of_reasonable_length
else:
# We must be in the tool shed and have an old changeset_revision, so we need to retrieve the file contents from the repository manifest.
ctx = suc.get_changectx_for_changeset( repo, changeset_revision )
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -1,5 +1,6 @@
import logging
import os
+import re
import shutil
import string
import tempfile
@@ -1504,6 +1505,30 @@
"""Return a reversed list of changesets in the repository changelog up to and including the included_upper_bounds_changeset_revision."""
return reversed_lower_upper_bounded_changelog( repo, INITIAL_CHANGELOG_HASH, included_upper_bounds_changeset_revision )
+def set_image_paths( app, encoded_repository_id, text ):
+ """
+ Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy as well as image
+ display in repository README files. This method will determine the location of the image file and return the path to it that will enable
+ the caller to open the file.
+ """
+ if text:
+ if app.name == 'galaxy':
+ route_to_images = '/admin_toolshed/static/images/%s' % encoded_repository_id
+ else:
+ # We're in the tool shed.
+ route_to_images = '/repository/static/images/%s' % encoded_repository_id
+ # We used to require $PATH_TO_IMAGES, but we now eliminate it if it's used.
+ text = text.replace( '$PATH_TO_IMAGES', '' )
+ # Eliminate the invalid setting of ./static/images since the routes will properly display images contained in that directory.
+ text = text.replace( './static/images', '' )
+ # Eliminate the default setting of /static/images since the routes will properly display images contained in that directory.
+ text = text.replace( '/static/images', '' )
+ # Use regex to instantiate routes into the defined image paths.
+ for match in re.findall( '.. image:: (?!http)/?(.+)', text ):
+ text = text.replace( match, match.replace( '/', '%2F' ) )
+ text = re.sub( '.. image:: (?!http)/?(.+)', '.. image:: %s/\\1' % route_to_images, text )
+ return text
+
def set_only_if_compiling_contained_td( repository, required_repository ):
"""Return True if the received required_repository is only needed to compile a tool dependency defined for the received repository."""
# This method is called only from Galaxy when rendering repository dependencies for an installed tool shed repository.
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/galaxy.base.js
--- a/static/scripts/galaxy.base.js
+++ b/static/scripts/galaxy.base.js
@@ -175,18 +175,14 @@
// if theres confirm text, send the dialog
if ( !confirmtext || confirm( confirmtext ) ) {
- var f;
- // Http request target is a window named demolocal on the local box
- if ( target === "demo" ) {
- if ( f === undefined || f.closed ) {
- f = window.open( href,target );
- f.creator = self;
- }
-
+ // link.click() doesn't use target for some reason,
+ // so manually do it here.
+ if (target) {
+ window.open(href, target);
}
// For all other links, do the default action.
else {
- link.trigger('click');
+ link.click();
}
}
}
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/galaxy.grids.js
--- a/static/scripts/galaxy.grids.js
+++ b/static/scripts/galaxy.grids.js
@@ -255,7 +255,8 @@
});
// Initialize ratings.
- $('.community_rating_star').rating({});
+ if ($('.community_rating_star').length !== 0)
+ $('.community_rating_star').rating({});
// Initialize item menu operations.
make_popup_menus();
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/mvc/data.js
--- a/static/scripts/mvc/data.js
+++ b/static/scripts/mvc/data.js
@@ -179,17 +179,17 @@
// -- Helper functions. --
_renderCell: function(cell_contents, index, colspan) {
+ var $cell = $('<td>').text(cell_contents);
var column_types = this.model.get_metadata('column_types');
if (colspan !== undefined) {
- return $('<td>').attr('colspan', colspan).addClass('stringalign').text(cell_contents);
+ $cell.attr('colspan', colspan).addClass('stringalign');
+ } else if (column_types !== undefined && index < column_types.length) {
+ if (column_types[index] === 'str' || column_types === 'list') {
+ /* Left align all str columns, right align the rest */
+ $cell.addClass('stringalign');
+ }
}
- else if (column_types[index] === 'str' || column_types === 'list') {
- /* Left align all str columns, right align the rest */
- return $('<td>').addClass('stringalign').text(cell_contents);
- }
- else {
- return $('<td>').text(cell_contents);
- }
+ return $cell;
},
_renderRow: function(line) {
@@ -416,49 +416,54 @@
error: function() { alert( ( "Could not add this dataset to browser" ) + '.' ); },
success: function(table_html) {
var parent = window.parent;
+ parent.Galaxy.modal.show({
+ title : "View Data in a New or Saved Visualization",
+ buttons :{
+ "Cancel": function(){
+ parent.Galaxy.modal.hide();
+ },
+ "View in saved visualization": function(){
+ // Show new modal with saved visualizations.
+ parent.Galaxy.modal.show(
+ {
+ title: "Add Data to Saved Visualization",
+ body: table_html,
+ buttons :{
+ "Cancel": function(){
+ parent.Galaxy.modal.hide();
+ },
+ "Add to visualization": function(){
+ $(parent.document).find('input[name=id]:checked').each(function(){
+ // hide
+ parent.Galaxy.modal.hide();
+
+ var vis_id = $(this).val();
+ dataset_params.id = vis_id;
+
+ // add widget
+ parent.Galaxy.frame_manager.frame_new({
+ title : "Trackster",
+ type : "url",
+ content : vis_url + "/trackster?" + $.param(dataset_params)
+ });
+ });
+ }
+ }
+ });
+ },
+ "View in new visualization": function(){
+ // hide
+ parent.Galaxy.modal.hide();
+
+ var url = vis_url + "/trackster?" + $.param(dataset_params);
- parent.show_modal( ( "View Data in a New or Saved Visualization" ), "", {
- "Cancel": function() {
- parent.hide_modal();
- },
- "View in saved visualization": function() {
- // Show new modal with saved visualizations.
- parent.show_modal( ( "Add Data to Saved Visualization" ), table_html, {
- "Cancel": function() {
- parent.hide_modal();
- },
- "Add to visualization": function() {
- $(parent.document).find('input[name=id]:checked').each(function() {
- var vis_id = $(this).val();
- dataset_params.id = vis_id;
-
- // add widget
- parent.Galaxy.frame_manager.frame_new(
- {
- title : "Trackster",
- type : "url",
- content : vis_url + "/trackster?" + $.param(dataset_params)
- });
-
- // hide
- parent.hide_modal();
- });
- }
- });
- },
- "View in new visualization": function() {
- var url = vis_url + "/trackster?" + $.param(dataset_params);
-
- // add widget
- parent.Galaxy.frame_manager.frame_new(
- {
- title : "Trackster",
- type : "url",
- content : url
- });
-
- // hide
- parent.hide_modal();
+ // add widget
+ parent.Galaxy.frame_manager.frame_new({
+ title : "Trackster",
+ type : "url",
+ content : url
+ });
+ }
}
});
}
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/mvc/dataset/hda-edit.js
--- a/static/scripts/mvc/dataset/hda-edit.js
+++ b/static/scripts/mvc/dataset/hda-edit.js
@@ -635,67 +635,77 @@
* @returns function that displays modal, loads trackster
*/
//TODO: should be imported from trackster.js
-function create_trackster_action_fn(vis_url, dataset_params, dbkey) {
- return function() {
- var listTracksParams = {};
- if (dbkey){
- // list_tracks seems to use 'f-dbkey' (??)
- listTracksParams[ 'f-dbkey' ] = dbkey;
- }
- $.ajax({
- url: vis_url + '/list_tracks?' + $.param( listTracksParams ),
- dataType: "html",
- error: function() { alert( _l( "Could not add this dataset to browser" ) + '.' ); },
- success: function(table_html) {
- var parent = window.parent;
+//TODO: This function is redundant and also exists in data.js
+ // create action
+ function create_trackster_action_fn (vis_url, dataset_params, dbkey) {
+ return function() {
+ var listTracksParams = {};
+ if (dbkey){
+ // list_tracks seems to use 'f-dbkey' (??)
+ listTracksParams[ 'f-dbkey' ] = dbkey;
+ }
+ $.ajax({
+ url: vis_url + '/list_tracks?' + $.param( listTracksParams ),
+ dataType: "html",
+ error: function() { alert( ( "Could not add this dataset to browser" ) + '.' ); },
+ success: function(table_html) {
+ var parent = window.parent;
+ parent.Galaxy.modal.show({
+ title : "View Data in a New or Saved Visualization",
+ buttons :{
+ "Cancel": function(){
+ parent.Galaxy.modal.hide();
+ },
+ "View in saved visualization": function(){
+ // Show new modal with saved visualizations.
+ parent.Galaxy.modal.show(
+ {
+ title: "Add Data to Saved Visualization",
+ body: table_html,
+ buttons :{
+ "Cancel": function(){
+ parent.Galaxy.modal.hide();
+ },
+ "Add to visualization": function(){
+ $(parent.document).find('input[name=id]:checked').each(function(){
+ // hide
+ parent.Galaxy.modal.hide();
+
+ var vis_id = $(this).val();
+ dataset_params.id = vis_id;
+
+ // add widget
+ parent.Galaxy.frame_manager.frame_new({
+ title : "Trackster",
+ type : "url",
+ content : vis_url + "/trackster?" + $.param(dataset_params)
+ });
+ });
+ }
+ }
+ });
+ },
+ "View in new visualization": function(){
+ // hide
+ parent.Galaxy.modal.hide();
+
+ var url = vis_url + "/trackster?" + $.param(dataset_params);
- parent.show_modal( _l( "View Data in a New or Saved Visualization" ), "", {
- "Cancel": function() {
- parent.hide_modal();
- },
- "View in saved visualization": function() {
- // Show new modal with saved visualizations.
- parent.show_modal( _l( "Add Data to Saved Visualization" ), table_html, {
- "Cancel": function() {
- parent.hide_modal();
- },
- "Add to visualization": function() {
- $(parent.document).find('input[name=id]:checked').each(function() {
- var vis_id = $(this).val();
- dataset_params.id = vis_id;
-
- // hide modal
- parent.hide_modal();
-
- // add widget
- Galaxy.frame_manager.frame_new(
- {
- title : "Trackster",
- type : "url",
- content : vis_url + "/trackster?" + $.param(dataset_params)
- });
+ // add widget
+ parent.Galaxy.frame_manager.frame_new({
+ title : "Trackster",
+ type : "url",
+ content : url
});
}
- });
- },
- "View in new visualization": function() {
- // hide modal
- parent.hide_modal();
+ }
+ });
+ }
+ });
+ return false;
+ };
+ }
- // add widget
- Galaxy.frame_manager.frame_new(
- {
- title : "Trackster",
- type : "url",
- content : vis_url + "/trackster?" + $.param(dataset_params)
- });
- }
- });
- }
- });
- return false;
- };
-}
//==============================================================================
//return {
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/utils/config.js
--- a/static/scripts/utils/config.js
+++ b/static/scripts/utils/config.js
@@ -245,9 +245,13 @@
if (this.$el.children().length === 0) {
this.render();
}
- show_modal("Configure", drawable.config.build_form(), {
- "Cancel": cancel_fn,
- "OK": ok_fn
+ Galaxy.modal.show({
+ title: "Configure",
+ body: drawable.config.build_form(),
+ buttons: {
+ "Cancel": cancel_fn,
+ "OK": ok_fn
+ }
});
},
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/viz/trackster/filters.js
--- a/static/scripts/viz/trackster/filters.js
+++ b/static/scripts/viz/trackster/filters.js
@@ -596,15 +596,19 @@
$.getJSON(run_tool_url, url_params, function(response) {
if (response.error) {
// General error.
- show_modal("Filter Dataset",
- "Error running tool " + tool_id,
- { "Close" : hide_modal } );
+ Galaxy.modal.show({
+ title: "Filter Dataset",
+ body : "Error running tool " + tool_id,
+ buttons : { "Close" : Galaxy.modal.hide() }
+ });
}
else if (filters.length === 0) {
// No more filters to run.
- show_modal("Filtering Dataset",
- "Filter(s) are running on the complete dataset. Outputs are in dataset's history.",
- { "Close" : hide_modal } );
+ Galaxy.modal.show({
+ title: "Filtering Dataset",
+ body: "Filter(s) are running on the complete dataset. Outputs are in dataset's history.",
+ buttons: { "Close" : Galaxy.modal.hide() }
+ });
}
else {
// More filters to run.
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/base/base_panels.mako
--- a/templates/base/base_panels.mako
+++ b/templates/base/base_panels.mako
@@ -102,10 +102,11 @@
});
## load galaxy js-modules
- require(['galaxy.master', 'galaxy.frame', 'galaxy.upload'], function(master, frame, upload)
+ require(['galaxy.master', 'galaxy.frame', 'galaxy.modal', 'galaxy.upload'], function(master, frame, modal, upload)
{
Galaxy.master = new master.GalaxyMaster();
Galaxy.frame_manager = new frame.GalaxyFrameManager();
+ Galaxy.modal = new modal.GalaxyModal();
##Galaxy.upload = new upload.GalaxyUpload();
});
</script>
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/grid_base.mako
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -86,7 +86,7 @@
// Create grid.
var grid = new Grid({
- url_base: location.pathname,
+ url_base: '${trans.request.path_url}',
async: is_true('${grid.use_async}'),
async_ops: async_ops,
categorical_filters: categorical_filters,
@@ -102,6 +102,12 @@
// Initialize grid objects on load.
// FIXME: use a grid view object eventually.
$(document).ready(function() {
+
+ // strip protocol and domain
+ var url = grid.get('url_base');
+ url = url.replace(/^.*\/\/[^\/]+/, '');
+ grid.set('url_base', url);
+
init_grid_elements();
init_grid_controls();
// Initialize text filters to select text on click and use normal font when user is typing.
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/webapps/galaxy/base_panels.mako
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -63,7 +63,7 @@
<ul class="nav navbar-nav" border="0" cellspacing="0">
- <%def name="tab( id, display, href, target='_parent', visible=True, extra_class='', menu_options=None )">
+ <%def name="tab( id, display, href, onclick=False, target='_parent', visible=True, extra_class='', menu_options=None )">
## Create a tab at the top of the panels. menu_options is a list of 2-elements lists of [name, link]
## that are options in the menu.
@@ -100,7 +100,11 @@
${menu_item[0]}
%elif len ( menu_item ) == 2:
<% name, link = menu_item %>
- <a href="${link}">${name}</a>
+ %if onclick:
+ <a href="${link}" onclick="Galaxy.frame_manager.frame_new({title: '${name}', type: 'url', content: '${link}'}); return false;">${name}</a>
+ %else:
+ <a href="${link}">${name}</a>
+ %endif
%else:
<% name, link, target = menu_item %><a target="${target}" href="${link}">${name}</a>
@@ -117,7 +121,7 @@
${tab( "analysis", _("Analyze Data"), h.url_for( controller='/root', action='index' ) )}
## Workflow tab.
- ${tab( "workflow", _("Workflow"), "javascript:Galaxy.frame_manager.frame_new({title: 'Workflow', type: 'url', content: '" + h.url_for( controller='/workflow', action='index' ) + "'});")}
+ ${tab( "workflow", _("Workflow"), h.url_for( controller='/workflow', action='index' ) )}
## 'Shared Items' or Libraries tab.
<%
@@ -147,10 +151,10 @@
## Visualization menu.
<%
menu_options = [
- [_('New Track Browser'), "javascript:Galaxy.frame_manager.frame_new({title: 'Trackster', type: 'url', content: '" + h.url_for( controller='/visualization', action='trackster' ) + "'});"],
- [_('Saved Visualizations'), "javascript:Galaxy.frame_manager.frame_new({ type: 'url', content : '" + h.url_for( controller='/visualization', action='list' ) + "'});" ]
+ [_('New Track Browser'), h.url_for( controller='/visualization', action='trackster' )],
+ [_('Saved Visualizations'), h.url_for( controller='/visualization', action='list' )]
]
- tab( "visualization", _("Visualization"), "javascript:Galaxy.frame_manager.frame_new({title: 'Trackster', type: 'url', content: '" + h.url_for( controller='/visualization', action='list' ) + "'});", menu_options=menu_options )
+ tab( "visualization", _("Visualization"), h.url_for( controller='/visualization', action='list' ), menu_options=menu_options, onclick=True )
%>
## Cloud menu.
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/webapps/galaxy/galaxy.masthead.mako
--- a/templates/webapps/galaxy/galaxy.masthead.mako
+++ b/templates/webapps/galaxy/galaxy.masthead.mako
@@ -51,7 +51,7 @@
<ul class="nav navbar-nav" border="0" cellspacing="0">
- <%def name="tab( id, display, href, target='_parent', visible=True, extra_class='', menu_options=None )">
+ <%def name="tab( id, display, href, onclick=False, target='_parent', visible=True, extra_class='', menu_options=None )">
## Create a tab at the top of the panels. menu_options is a list of 2-elements lists of [name, link]
## that are options in the menu.
@@ -61,8 +61,6 @@
extra = ""
if extra_class:
cls += " " + extra_class
- ##if self.active_view == id:
- ## cls += " active"
if menu_options:
cls += " dropdown"
a_cls += " dropdown-toggle"
@@ -88,7 +86,11 @@
${menu_item[0]}
%elif len ( menu_item ) == 2:
<% name, link = menu_item %>
- <a href="${link}">${name}</a>
+ %if onclick:
+ <a href="${link}" onclick="Galaxy.frame_manager.frame_new({title: '${name}', type: 'url', content: '${link}'}); return false;">${name}</a>
+ %else:
+ <a href="${link}">${name}</a>
+ %endif
%else:
<% name, link, target = menu_item %><a target="${target}" href="${link}">${name}</a>
@@ -105,7 +107,7 @@
${tab( "analysis", _("Analyze Data"), h.url_for( controller='/root', action='index' ) )}
## Workflow tab.
- ${tab( "workflow", _("Workflow"), "javascript:Galaxy.frame_manager.frame_new({title: 'Workflow', type: 'url', content: '" + h.url_for( controller='/workflow', action='index' ) + "'});")}
+ ${tab( "workflow", _("Workflow"), h.url_for( controller='/workflow', action='index' ) )}
## 'Shared Items' or Libraries tab.
<%
@@ -135,10 +137,10 @@
## Visualization menu.
<%
menu_options = [
- [_('New Track Browser'), "javascript:Galaxy.frame_manager.frame_new({title: 'Trackster', type: 'url', content: '" + h.url_for( controller='/visualization', action='trackster' ) + "'});"],
- [_('Saved Visualizations'), "javascript:Galaxy.frame_manager.frame_new({ type: 'url', content : '" + h.url_for( controller='/visualization', action='list' ) + "'});" ]
+ [_('New Track Browser'), h.url_for( controller='/visualization', action='trackster' )],
+ [_('Saved Visualizations'), h.url_for( controller='/visualization', action='list' )]
]
- tab( "visualization", _("Visualization"), "javascript:Galaxy.frame_manager.frame_new({title: 'Trackster', type: 'url', content: '" + h.url_for( controller='/visualization', action='list' ) + "'});", menu_options=menu_options )
+ tab( "visualization", _("Visualization"), h.url_for( controller='/visualization', action='list' ), menu_options=menu_options, onclick=True )
%>
## Cloud menu.
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/webapps/galaxy/galaxy.panels.mako
--- a/templates/webapps/galaxy/galaxy.panels.mako
+++ b/templates/webapps/galaxy/galaxy.panels.mako
@@ -182,7 +182,6 @@
%if self.galaxy_config['message_box']:
<div id="messagebox" class="panel-message"></div>
%endif
-
## left panel
%if self.galaxy_config['left_panel']:
<div id="left">
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/webapps/tool_shed/repository/common.mako
--- a/templates/webapps/tool_shed/repository/common.mako
+++ b/templates/webapps/tool_shed/repository/common.mako
@@ -585,10 +585,8 @@
<%def name="render_readme( readme, pad, parent, row_counter )"><%
- from tool_shed.util.shed_util_common import to_html_string
- from galaxy.util import rst_to_html
encoded_id = trans.security.encode_id( readme.id )
- render_rst = readme.name.endswith( '.rst' )
+ from galaxy.util import unicodify
%><tr class="datasetRow"
%if parent is not None:
@@ -597,11 +595,15 @@
id="libraryItem-rr-${encoded_id}"><td style="padding-left: ${pad+20}px;"><table id="readme_files">
- %if render_rst:
- <tr><td>${ rst_to_html( readme.text ) }</td></tr>
- %else:
- <tr><td>${ to_html_string( readme.text ) }</td></tr>
- %endif
+ <tr>
+ <td>
+ % if readme.name.endswith( '.rst' ):
+ ${ unicodify( readme.text ) }
+ %else:
+ ${ readme.text }
+ %endif
+ </td>
+ </tr></table></td></tr>
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 test/tool_shed/base/twilltestcase.py
--- a/test/tool_shed/base/twilltestcase.py
+++ b/test/tool_shed/base/twilltestcase.py
@@ -723,7 +723,6 @@
post_submit_strings_not_displayed=[] ):
self.display_manage_repository_page( repository )
self.check_for_strings( strings_displayed, strings_not_displayed )
- tc.fv( "user_access", "allow_push", '-Select one' )
for username in usernames:
tc.fv( "user_access", "allow_push", '+%s' % username )
tc.submit( 'user_access_button' )
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 test/tool_shed/functional/test_1140_tool_help_images.py
--- a/test/tool_shed/functional/test_1140_tool_help_images.py
+++ b/test/tool_shed/functional/test_1140_tool_help_images.py
@@ -109,10 +109,10 @@
In the center tool panel, look for the image string similar to the following string where the
encoded_repository_id is previously determined as the installed ToolShedRepository id:
- src="/tool_runner/static/images/<id>/count_modes.png"
+ src="/admin_toolshed/static/images/<id>/count_modes.png"
'''
repository = test_db_util.get_installed_repository_by_name_owner( repository_name, common.test_user_1_name )
- image_path = 'src="/tool_runner/static/images/%s/count_modes.png"' % self.security.encode_id( repository.id )
+ image_path = 'src="/admin_toolshed/static/images/%s/count_modes.png"' % self.security.encode_id( repository.id )
# The repository uploaded in this test should only define one tool, which should be the tool that contains a link to an image.
repository_metadata = repository.metadata
tool_id = repository_metadata[ 'tools' ][ 0 ][ 'guid' ]
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 test/tool_shed/functional/test_1400_review_migration_stages.py
--- a/test/tool_shed/functional/test_1400_review_migration_stages.py
+++ b/test/tool_shed/functional/test_1400_review_migration_stages.py
@@ -20,14 +20,26 @@
def test_0005_load_migration_stages_page( self ):
'''Load the migration page and check for the appropriate migration stages.'''
- stages = [
- 'emboss_5', 'emboss_datatypes', 'emboss', '5.0.0', '0002_tools.sh',
- 'freebayes', '0.9.4_9696d0ce8a962f7bb61c4791be5ce44312b81cf8', 'samtools', '0.1.18', 'FreeBayes requires g++',
- 'ncurses', 'zlib', '0003_tools.sh',
- 'ncbi_blast_plus', 'blast_datatypes', 'blast+', '2.2.26+', 'blast.ncbi.nlm.nih.gov', 'NCBI BLAST+ tools', '0004_tools.sh',
- 'bwa_wrappers', '0.5.9', 'zlib and libpthread', 'Map with BWA for Illumina', '0005_tools.sh',
- 'picard', '1.56.0', 'FASTQ to BAM', '0006_tools.sh',
- 'lastz', '1.02.00', 'bowtie', '0.12.7', 'Map with Bowtie for Illumina', 'Bowtie requires libpthread', '0007_tools.sh'
- ]
+ stages = []
+ migration_message_strings = [ 'The Emboss 5.0.0 tools have been eliminated',
+ 'The freebayes tool has been eliminated',
+ 'The NCBI BLAST+ tools',
+ 'The tools "Map with BWA for Illumina"',
+ 'FASTQ to BAM, SAM to FASTQ, BAM ',
+ 'Map with Bowtie for Illumina, ',
+ 'BAM-to-SAM converts BAM format' ]
+ migrated_repository_names = [ 'emboss_5', 'emboss_datatypes', 'freebayes', 'ncbi_blast_plus',
+ 'blast_datatypes', 'bwa_wrappers', 'picard', 'lastz',
+ 'lastz_paired_reads', 'bowtie_color_wrappers', 'bowtie_wrappers',
+ 'xy_plot', 'bam_to_sam' ]
+ migrated_tool_dependencies = [ 'emboss', '5.0.0', 'freebayes', '0.9.4_9696d0ce8a962f7bb61c4791be5ce44312b81cf8',
+ 'samtools', '0.1.18', 'blast+', '2.2.26+', 'bwa', '0.5.9', 'picard', '1.56.0',
+ 'lastz', '1.02.00', 'bowtie', '0.12.7', 'FreeBayes requires g++', 'ncurses', 'zlib',
+ 'zlib and libpthread', 'blast.ncbi.nlm.nih.gov', 'fastx_toolkit', '0.0.13',
+ 'samtools', '0.1.16', 'cufflinks', '2.1.1', 'R', '2.11.0' ]
+ migration_scripts = [ '0002_tools.sh', '0003_tools.sh', '0004_tools.sh', '0005_tools.sh', '0006_tools.sh',
+ '0007_tools.sh', '0008_tools.sh' ]
+ stages.extend( migration_scripts + migrated_tool_dependencies + migrated_repository_names )
+ stages.extend( migration_message_strings )
self.load_galaxy_tool_migrations_page( strings_displayed=stages )
diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 universe_wsgi.ini.sample
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -269,11 +269,11 @@
# Datasets in an error state include a link to report the error. Those reports
# will be sent to this address. Error reports are disabled if no address is set.
+# Also this email is shown as a contact to user in case of Galaxy misconfiguration and other events user may encounter.
#error_email_to = None
-# Administrator's email is shown to user in case of Galaxy misconfiguration or a generic error.
-# It is also used as a sender for the account activation mail.
-#admin_email = None
+# Activation email is used as a sender ('from' field) for the account activation mail.
+#activation_email = None
# E-mail domains blacklist is used for filtering out users that are using disposable email address
# during the registration. If their address domain matches any domain in the BL they are refused the registration.
@@ -299,7 +299,7 @@
# Used for warning box for inactive accounts (unable to run jobs).
# In use only if activation_grace_period is set.
-#inactivity_box_content = Your account has not been activated yet. Please activate your account by verifying your email address. For now you can access everything at Galaxy but your jobs won't run.
+#inactivity_box_content = Your account has not been activated yet. Feel free to browse around and see what's available, but you won't be able to upload data or run jobs until you have verified your email address.
# -- Display sites
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jgoecks: Bug fixes and simplification for popup menu links.
by commits-noreply@bitbucket.org 04 Oct '13
by commits-noreply@bitbucket.org 04 Oct '13
04 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/6965d7da4ba5/
Changeset: 6965d7da4ba5
User: jgoecks
Date: 2013-10-04 17:50:44
Summary: Bug fixes and simplification for popup menu links.
Affected #: 1 file
diff -r 380fdaad0f938fda743593bf682de7f2fdb95a94 -r 6965d7da4ba5c84b19ade3ab89577d827edd5dc1 static/scripts/galaxy.base.js
--- a/static/scripts/galaxy.base.js
+++ b/static/scripts/galaxy.base.js
@@ -175,18 +175,14 @@
// if theres confirm text, send the dialog
if ( !confirmtext || confirm( confirmtext ) ) {
- var f;
- // Http request target is a window named demolocal on the local box
- if ( target === "demo" ) {
- if ( f === undefined || f.closed ) {
- f = window.open( href,target );
- f.creator = self;
- }
-
+ // link.click() doesn't use target for some reason,
+ // so manually do it here.
+ if (target) {
+ window.open(href, target);
}
// For all other links, do the default action.
else {
- link.trigger('click');
+ link.click();
}
}
}
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: Fix for rendering rst files in tool shed repositories.
by commits-noreply@bitbucket.org 04 Oct '13
by commits-noreply@bitbucket.org 04 Oct '13
04 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/380fdaad0f93/
Changeset: 380fdaad0f93
User: greg
Date: 2013-10-04 17:46:48
Summary: Fix for rendering rst files in tool shed repositories.
Affected #: 1 file
diff -r 0ee4e7ac77e25a814e02157e3d5c77c43f4b224c -r 380fdaad0f938fda743593bf682de7f2fdb95a94 templates/webapps/tool_shed/repository/common.mako
--- a/templates/webapps/tool_shed/repository/common.mako
+++ b/templates/webapps/tool_shed/repository/common.mako
@@ -584,7 +584,10 @@
</%def><%def name="render_readme( readme, pad, parent, row_counter )">
- <% encoded_id = trans.security.encode_id( readme.id ) %>
+ <%
+ encoded_id = trans.security.encode_id( readme.id )
+ from galaxy.util import unicodify
+ %><tr class="datasetRow"
%if parent is not None:
parent="${parent}"
@@ -592,7 +595,15 @@
id="libraryItem-rr-${encoded_id}"><td style="padding-left: ${pad+20}px;"><table id="readme_files">
- <tr><td>${ readme.text }</td></tr>
+ <tr>
+ <td>
+ % if readme.name.endswith( '.rst' ):
+ ${ unicodify( readme.text ) }
+ %else:
+ ${ readme.text }
+ %endif
+ </td>
+ </tr></table></td></tr>
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: Add additional check and slice to _sniffnfix_pg9_hex(). Fixes issue seen when attempting to view saved visualizations. Further investigation may be needed.
by commits-noreply@bitbucket.org 04 Oct '13
by commits-noreply@bitbucket.org 04 Oct '13
04 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/1f0e7ae9e324/
Changeset: 1f0e7ae9e324
Branch: stable
User: dan
Date: 2013-09-29 08:04:31
Summary: Add additional check and slice to _sniffnfix_pg9_hex(). Fixes issue seen when attempting to view saved visualizations. Further investigation may be needed.
Affected #: 1 file
diff -r a477486bf18eafdd14dd7ba1e91e17f1b05e8121 -r 1f0e7ae9e324fcc1e12105651269016f2edb925f lib/galaxy/model/custom_types.py
--- a/lib/galaxy/model/custom_types.py
+++ b/lib/galaxy/model/custom_types.py
@@ -29,6 +29,8 @@
try:
if value[0] == 'x':
return binascii.unhexlify(value[1:])
+ elif value.startswith( '\\x' ):
+ return binascii.unhexlify( value[2:] )
else:
return value
except Exception, ex:
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 errors when defining paths to images on disk for display in tool shed repositories.
by commits-noreply@bitbucket.org 04 Oct '13
by commits-noreply@bitbucket.org 04 Oct '13
04 Oct '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/0ee4e7ac77e2/
Changeset: 0ee4e7ac77e2
User: greg
Date: 2013-10-04 16:30:55
Summary: Handle errors when defining paths to images on disk for display in tool shed repositories.
Affected #: 2 files
diff -r 836b6a16e5a9e499c0a07b23b69c25a3f17eba98 -r 0ee4e7ac77e25a814e02157e3d5c77c43f4b224c lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -301,9 +301,11 @@
relative_path_to_image_file = kwd.get( 'image_file', None )
if repository_id and relative_path_to_image_file:
repository = suc.get_tool_shed_repository_by_id( trans, repository_id )
- repo_files_dir = repository.repo_files_directory( trans.app )
- path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
- return open( path_to_file, 'r' )
+ if repository:
+ repo_files_dir = repository.repo_files_directory( trans.app )
+ path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
+ if os.path.exists( path_to_file ):
+ return open( path_to_file, 'r' )
return None
@web.expose
diff -r 836b6a16e5a9e499c0a07b23b69c25a3f17eba98 -r 0ee4e7ac77e25a814e02157e3d5c77c43f4b224c lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -1033,9 +1033,11 @@
relative_path_to_image_file = kwd.get( 'image_file', None )
if repository_id and relative_path_to_image_file:
repository = suc.get_repository_in_tool_shed( trans, repository_id )
- repo_files_dir = os.path.join( repository.repo_path( trans.app ), repository.name )
- path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
- return open( path_to_file, 'r' )
+ if repository:
+ repo_files_dir = repository.repo_path( trans.app )
+ path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file )
+ if os.path.exists( path_to_file ):
+ return open( path_to_file, 'r' )
return None
@web.expose
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