changeset 8:220885b2d7ee

End to end test works. Add tests next
author ross lazarus ross.lazarus@gmail.com
date Sat, 02 Jun 2012 20:02:11 +1000
parents 7221619caefa
children e09c76551bed
files rgToolFactory.py rgToolFactory.xml
diffstat 2 files changed, 186 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/rgToolFactory.py	Sat Jun 02 10:43:08 2012 +1000
+++ b/rgToolFactory.py	Sat Jun 02 20:02:11 2012 +1000
@@ -30,26 +30,14 @@
 import optparse
 import tarfile
 import re
+import shutil
+
 progname = os.path.split(sys.argv[0])[1] 
 myversion = 'V000.1 May 2012' 
 verbose = False 
 debug = False
 
 
-galhtmlprefix = """<?xml version="1.0" encoding="utf-8" ?> 
-<!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="document"> 
-""" 
-galhtmlattr = """<b><a href="http://rgenetics.org">Galaxy Rgenetics Base Script Wrapper based </a> tool output %s run at %s</b><br/>""" 
-galhtmlpostfix = """</div></body></html>\n"""
-
 def timenow():
     """return current time as a string
     """
@@ -97,19 +85,22 @@
         __cr____cn__sessionInfo()
         __cr____cn__
         """
-        self.myname = sys.argv[0] # get our name because we write ourselves out as a tool later
+        if opts.output_dir: # simplify for the tool tarball
+            os.chdir(opts.output_dir)
         self.thumbformat = 'jpg'
         self.opts = opts
         self.toolname = re.sub('[^a-zA-Z0-9_]+', '', opts.tool_name)
+        self.toolid = self.toolname
         s = open(self.opts.script_path,'r').read()
         self.script = restore_text(s)
-        self.pyfile = self.myname
-        self.xmlfile = '%s.xml' % os.path.splitext(self.pyfile)[0] # punt
+        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
         self.sfile = '%s.%s' % (self.toolname,opts.interpreter)
         localscript = open(self.sfile,'w')
         localscript.write(self.script)
         localscript.close()
-        if opts.output_dir or self.opts.makeTool: # may not want these complexities if a simple script
+        if opts.output_dir or self.opts.make_Tool: # may not want these complexities if a simple script
             self.tlog = os.path.join(opts.output_dir,"%s_runner.log" % self.toolname)
             artifactpath = os.path.join(opts.output_dir,'%s_run.script' % self.toolname) 
             artifact = open(artifactpath,'w')
@@ -123,6 +114,77 @@
         a('-') # use 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
+
+
+    def makeXML(self):
+        """
+        Create a Galaxy xml tool wrapper for the new script as a string to write out
+        """    
+        newXML="""<tool id="%(toolid)s" name="%(toolname)s" version="0.01">
+            %(tooldesc)s
+            %(command)s
+            <inputs>
+            %(inputs)s
+            </inputs>
+            <outputs>
+            %(outputs)s
+            </outputs>
+            <help>
+            %(help)s
+            </help>
+            <configfiles>
+            <configfile name="runMe">
+            %(script)s
+            </configfile>
+            </configfiles>
+            </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
+            
+        xdict = {}
+        xdict['script'] = self.script # we pass this as a configfile because it's less painful that galaxy_tool_data_dir
+                                      # embed script in tool - remove dependence on something else outside in the wilds
+        if self.opts.help_text:
+            h = open(self.opts.help_text,'r').read()
+            xdict['help'] = restore_text(h)
+        else:
+            xdict['help'] = 'Please ask the tool author for help as none was supplied at tool generation'
+        if self.opts.tool_desc:
+            xdict['tooldesc'] = '<description>%s</description>' % self.opts.tool_desc
+        else:
+            xdict['tooldesc'] = ''
+        xdict['command_outputs'] = '' # will probably be some!
+        xdict['outputs'] = '' # will probably be some!
+        if self.opts.input_tab:
+            xdict['command_inputs'] = '--input_tab "$input1"'
+            xdict['inputs'] = '<param name="input1"  type="data" format="%s" label="Select a suitable input file from your history"/>' % 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"'
+            xdict['outputs'] +=  '<data format="html" name="html_file" label="${job_name}.html"/>\n'
+        if self.opts.output_tab:
+            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):
         """
@@ -133,13 +195,19 @@
         if retval:
             print >> sys.stderr,'## Run failed. Cannot build yet. Please fix and retry'
             sys.exit(1)
-        tarpath = os.path.join(self.opts.output_dir,"%s.gz" % self.toolname)
+        self.makeXML()
+        tdir = self.toolname
+        os.mkdir(tdir)
+        shutil.copyfile(self.xmlfile,os.path.join(tdir,self.xmlfile))
+        shutil.copyfile(self.pyfile,os.path.join(tdir,'%s.py' % self.toolname))
+        shutil.copyfile(self.sfile,os.path.join(tdir,self.sfile))
+        tarpath = "%s.gz" % self.toolname
         tar = tarfile.open(tarpath, "w:gz")
-        tar.add(self.xmlfile,arcname='%s.xml' % self.toolname)
-        tar.add(self.pyfile,arcname=os.path.basename(self.pyfile))
-        tar.add(self.sfile,arcname=self.sfile)
+        tar.add(tdir,arcname=self.toolname)
         tar.close()
-        self.makeHtml()
+        shutil.rmtree(tdir)
+        self.makeHtml() # call this to return the new gzip inside the autogenerated html file
+        ## TODO: replace with optional direct upload to local toolshed?
         return retval
 
     def compressPDF(self,inpdf=None,thumbformat='png'):
@@ -181,17 +249,32 @@
         return size
 
     def makeHtml(self):
-       """
-       """
-       flist = os.listdir(self.opts.output_dir)
-       flist = [x for x in flist if x <> 'Rplots.pdf']
-       flist.sort()
-       html = [galhtmlprefix % progname,]
-       html.append('<h2>Galaxy %s outputs run at %s</h2><br/>\n' % (self.toolname,timenow()))
-       fhtml = []
-       if len(flist) > 0:
-           html.append('<table cellpadding="3" cellspacing="3">\n')
-           for fname in flist:
+        """ Create an HTML file content to list all the artefacts found in the output_dir
+        """
+
+        galhtmlprefix = """<?xml version="1.0" encoding="utf-8" ?> 
+        <!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="document"> 
+        """ 
+        galhtmlattr = """<hr/><b><a href="http://rgenetics.org">Galaxy Tool Factory Script Wrapper</a> tool output %s run at %s</b><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 = [galhtmlprefix % progname,]
+        html.append('<h2>Galaxy %s outputs run at %s</h2><br/>\n' % (self.toolname,timenow()))
+        fhtml = []
+        if len(flist) > 0:
+            html.append('<table cellpadding="3" cellspacing="3">\n')
+            for fname in flist:
                 dname,e = os.path.splitext(fname)
                 sfsize = self.getfSize(fname,self.opts.output_dir)
                 if e.lower() == '.pdf' : # compress and make a thumbnail
@@ -204,32 +287,31 @@
                     fhtml.append('<li><a href="%s">%s %s</a></li>' % (fname,fname,sfsize))
                 else:
                     fhtml.append('<li><a href="%s">%s %s</a></li>' % (fname,fname,sfsize))
-           html.append('</table>\n')
-           if len(fhtml) > 0:
-               fhtml.insert(0,'<ul>')
-               fhtml.append('</ul>')
-               html += fhtml # add all non-pdf files to the end of the display
-       else:
-           html.append('<h2>### Error - %s returned no files - please confirm that parameters are sane</h1>' % self.opts.interpreter)
-           html.append('<h3>%s log follows below</h3><hr><pre>\n' % self.opts.interpreter)
-       rlog = open(self.tlog,'r').readlines()
-       html += rlog
-       html.append('%s CL = %s</br>\n' % (self.toolname,' '.join(sys.argv)))
-       html.append('CL = %s</br>\n' % (' '.join(self.cl)))
-       html.append('</pre>\n')
-       html.append(galhtmlattr % (progname,timenow()))
-       html.append(galhtmlpostfix)
-       htmlf = file(self.opts.output_html,'w')
-       htmlf.write('\n'.join(html))
-       htmlf.write('\n')
-       htmlf.close()
-       self.html = html
+            html.append('</table>\n')
+        if len(fhtml) > 0:
+           fhtml.insert(0,'<ul>')
+           fhtml.append('</ul>')
+           html += fhtml # add all non-pdf files to the end of the display
+        else:
+            html.append('<h2>### Error - %s returned no files - please confirm that parameters are sane</h1>' % self.opts.interpreter)
+            html.append('<h3>%s log follows below</h3><hr/><pre>\n' % self.opts.interpreter)
+        rlog = open(self.tlog,'r').readlines()
+        html += rlog
+        html.append('%s CL = %s<br/>\n' % (self.toolname,' '.join(sys.argv)))
+        html.append('</pre>\n')
+        html.append(galhtmlattr % (progname,timenow()))
+        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):
         """
         """
-        if self.opts.output_dir or self.opts.makeTool:
+        if self.opts.output_dir or self.opts.make_Tool:
             sto = open(self.tlog,'w')
             p = subprocess.Popen(' '.join(self.cl),shell=True,stdout=sto,stderr=sto,stdin=subprocess.PIPE,cwd=self.opts.output_dir)
         else:
@@ -237,7 +319,7 @@
         p.stdin.write(self.script)
         p.stdin.close()
         retval = p.wait()
-        if self.opts.output_dir or self.opts.makeTool:
+        if self.opts.make_HTML or self.opts.make_Tool:
             sto.close()
             self.makeHtml()
         return retval
@@ -256,11 +338,14 @@
     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('--input_tab',default=None)
+    a('--output_tab',default=None)
     a('--user_email',default=None)
     a('--bad_user',default=None)
-    a('--makeTool',default=None)
+    a('--make_Tool',default=None)
+    a('--make_HTML',default=None)
+    a('--help_text',default=None)
+    a('--tool_desc',default=None)
     opts, args = op.parse_args()
     assert not opts.bad_user,'%s is NOT authorized to use this tool. Please ask your friendly admin' % opts.bad_user
     assert opts.tool_name,'## Tool Factory expects a tool name - eg --tool_name=DESeq'
@@ -272,7 +357,7 @@
         except:
             pass
     r = ScriptRunner(opts)
-    if opts.makeTool:
+    if opts.make_Tool:
         retcode = r.makeTooltar()
     else:
         retcode = r.run()
--- a/rgToolFactory.xml	Sat Jun 02 10:43:08 2012 +1000
+++ b/rgToolFactory.xml	Sat Jun 02 20:02:11 2012 +1000
@@ -5,15 +5,20 @@
      rgToolFactory.py --bad_user $__user_email__
   #else:
     rgToolFactory.py --script_path "$runme" --interpreter "$interpreter" 
-     --tool_name "$tool_name"  --input_tab "$input1" --user_email "${__user_email__}"
-    #if $makeHTML.value=="yes" or $makeTool.value=="yes":
+     --tool_name "$tool_name"  --input_tab "$input1" 
+    #if $make_HTML.value=="yes" or $factory.make_Tool=="yes":
       --output_dir "$html_file.files_path" --output_html "$html_file"
     #end if
-    #if $makeTAB.value=="yes":
+    #if $make_HTML.value=="yes":
+    --make_HTML "yes"
+    #end if
+    #if $make_TAB.value=="yes":
       --output_tab "$tab_file"
     #end if
-    #if $makeTool.value=="yes":
-      --makeTool "$makeTool"
+    #if $factory.make_Tool=="yes":
+      --make_Tool "$factory.make_Tool"
+      --help_text "$helpme"
+      --tool_desc "$factory.tool_desc"
     #end if
 #end if 
   </command>
@@ -21,17 +26,28 @@
     <param name="input1"  type="data" format="tabular" label="Select an optional input tabular file from your history" optional="true"
        help="Your script probably needs an input - but if not, this can be left unassigned"/>
     <param name="tool_name" type="text" value="My dynamic script" size="80" label="Title for job outputs" help="Supply a meaningful name here to remind you what the outputs contain"/>
-        <param name="makeTool" type="select" label="Create a tar.gz file ready for local toolshed entry" 
-         help="Ready to deploy securely! with tests eventually">
+    <conditional name="factory">
+        <param name="make_Tool" type="select" label="Create a tar.gz file ready for local toolshed entry" help="Ready to deploy securely!">
         <option value="yes">Yes</option>
         <option value="" selected="true">Not yet, it's still broken</option>
-    </param>   
-    <param name="makeHTML" type="select" label="Create an HTML output with all script outputs collected together, with thumbnails of new PDF images, the script and a run log file" 
+        </param>
+        <when value = "yes">
+            <param name="tool_desc" type="text" width="120" value="" help="Supply a brief tool description for the Galaxy tool menu entry (optional - appears after the tool name)" />
+            <param name="help_text" type="text" area="true" size="8x80" width="120" value="**What it Does**" help="Supply the brief user documentation to appear on the new tool form as reStructured text" >           
+                <sanitizer>
+                    <valid initial="string.printable">
+                    </valid>
+                    <mapping initial="none"/>
+                </sanitizer>
+            </param>
+        </when>
+    </conditional> 
+    <param name="make_HTML" type="select" label="Create an HTML output with all script outputs collected together, with thumbnails of new PDF images, the script and a run log file" 
          help="This is useful for presenting complex outputs and is not needed if your script doesn't create anything other than a single tabular output">
         <option value="yes">Yes</option>
         <option value="" selected="true">No</option>
     </param>   
-    <param name="makeTAB" type="select" label="Create a new tabular history output" 
+    <param name="make_TAB" type="select" label="Create a new tabular history output" 
          help="This is useful if your script creates a single new tabular file you want to appear in the history after the tool executes">
         <option value="yes" selected="true">Yes</option>
         <option value="">No</option>
@@ -42,20 +58,32 @@
         <option value="perl (ugh)">perl</option>
     </param>   
     <param name="dynScript" label="Your Script Goes Here" type="text" value="" area="True" size="8x80" width="80" 
-      help="Expect FIRST CL parameter = the optional input tabular file path (or NONE if not specified). Ensure your script writes tabular output to the path in the SECOND command line parameter it gets."/>
+      help="Expect FIRST CL parameter = the optional input tabular file path (or NONE if not specified). Ensure your script writes tabular output to the path in the SECOND command line parameter it gets.">
+      <sanitizer>
+         <valid initial="string.printable">
+         </valid>
+         <mapping initial="none"/>
+      </sanitizer>
+     </param>
   </inputs>
   <outputs>
     <data format="tabular" name="tab_file" label="${tool_name}.xls">
-        <filter> makeTAB=="yes" </filter>
+        <filter> make_TAB=="yes" </filter>
     </data>
     <data format="html" name="html_file" label="${tool_name}.html">
-        <filter> makeHTML=="yes" or makeTool=="yes"</filter>
+        <filter> make_HTML=="yes" or make_Tool=="yes"</filter>
     </data>
   </outputs>
 <configfiles>
 <configfile name="runme">
 ${dynScript}
 </configfile>
+#if $factory.make_Tool == "yes":
+<configfile name="helpme">
+$factory.help_text
+</configfile>
+#end if
+
 </configfiles>
 <help>
 **What it does**