comparison Downloads/CloudMapzfhommapping/CloudMapzfhommapping.py @ 0:d866ab420947 draft

Uploaded
author richjp
date Wed, 08 Oct 2014 13:28:47 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:d866ab420947
1 # CloudMapzfhommapping/CloudMapzfhommapping.py - a self annotated version of rgToolFactory.py generated by running rgToolFactory.py
2 # to make a new Galaxy tool called CloudMapzfhommapping
3 # User r.poole@ucl.ac.uk at 08/10/2014 18:05:06
4 # rgToolFactory.py
5 # see https://bitbucket.org/fubar/galaxytoolfactory/wiki/Home
6 #
7 # copyright ross lazarus (ross stop lazarus at gmail stop com) May 2012
8 #
9 # all rights reserved
10 # Licensed under the LGPL
11 # suggestions for improvement and bug fixes welcome at https://bitbucket.org/fubar/galaxytoolfactory/wiki/Home
12 #
13 # August 2014
14 # merged John Chilton's citation addition and ideas from Marius van den Beek to enable arbitrary
15 # data types for input and output - thanks!
16 #
17 # march 2014
18 # had to remove dependencies because cross toolshed dependencies are not possible - can't pre-specify a toolshed url for graphicsmagick and ghostscript
19 # grrrrr - night before a demo
20 # added dependencies to a tool_dependencies.xml if html page generated so generated tool is properly portable
21 #
22 # added ghostscript and graphicsmagick as dependencies
23 # fixed a wierd problem where gs was trying to use the new_files_path from universe (database/tmp) as ./database/tmp
24 # errors ensued
25 #
26 # august 2013
27 # found a problem with GS if $TMP or $TEMP missing - now inject /tmp and warn
28 #
29 # july 2013
30 # added ability to combine images and individual log files into html output
31 # just make sure there's a log file foo.log and it will be output
32 # together with all images named like "foo_*.pdf
33 # otherwise old format for html
34 #
35 # January 2013
36 # problem pointed out by Carlos Borroto
37 # added escaping for <>$ - thought I did that ages ago...
38 #
39 # August 11 2012
40 # changed to use shell=False and cl as a sequence
41
42 # This is a Galaxy tool factory for simple scripts in python, R or whatever ails ye.
43 # It also serves as the wrapper for the new tool.
44 #
45 # you paste and run your script
46 # Only works for simple scripts that read one input from the history.
47 # Optionally can write one new history dataset,
48 # and optionally collect any number of outputs into links on an autogenerated HTML page.
49
50 # DO NOT install on a public or important site - please.
51
52 # installed generated tools are fine if the script is safe.
53 # They just run normally and their user cannot do anything unusually insecure
54 # but please, practice safe toolshed.
55 # Read the fucking code before you install any tool
56 # especially this one
57
58 # After you get the script working on some test data, you can
59 # optionally generate a toolshed compatible gzip file
60 # containing your script safely wrapped as an ordinary Galaxy script in your local toolshed for
61 # safe and largely automated installation in a production Galaxy.
62
63 # If you opt for an HTML output, you get all the script outputs arranged
64 # as a single Html history item - all output files are linked, thumbnails for all the pdfs.
65 # Ugly but really inexpensive.
66 #
67 # Patches appreciated please.
68 #
69 #
70 # long route to June 2012 product
71 # Behold the awesome power of Galaxy and the toolshed with the tool factory to bind them
72 # derived from an integrated script model
73 # called rgBaseScriptWrapper.py
74 # Note to the unwary:
75 # This tool allows arbitrary scripting on your Galaxy as the Galaxy user
76 # There is nothing stopping a malicious user doing whatever they choose
77 # Extremely dangerous!!
78 # Totally insecure. So, trusted users only
79 #
80 # preferred model is a developer using their throw away workstation instance - ie a private site.
81 # no real risk. The universe_wsgi.ini admin_users string is checked - only admin users are permitted to run this tool.
82 #
83
84 import sys
85 import shutil
86 import subprocess
87 import os
88 import time
89 import tempfile
90 import optparse
91 import tarfile
92 import re
93 import shutil
94 import math
95
96 progname = os.path.split(sys.argv[0])[1]
97 myversion = 'V001.1 March 2014'
98 verbose = False
99 debug = False
100 toolFactoryURL = 'https://bitbucket.org/fubar/galaxytoolfactory'
101
102 # if we do html we need these dependencies specified in a tool_dependencies.xml file and referred to in the generated
103 # tool xml
104 toolhtmldepskel = """<?xml version="1.0"?>
105 <tool_dependency>
106 <package name="ghostscript" version="9.10">
107 <repository name="package_ghostscript_9_10" owner="devteam" prior_installation_required="True" />
108 </package>
109 <package name="graphicsmagick" version="1.3.18">
110 <repository name="package_graphicsmagick_1_3" owner="iuc" prior_installation_required="True" />
111 </package>
112 <readme>
113 %s
114 </readme>
115 </tool_dependency>
116 """
117
118 protorequirements = """<requirements>
119 <requirement type="package" version="9.10">ghostscript</requirement>
120 <requirement type="package" version="1.3.18">graphicsmagick</requirement>
121 </requirements>"""
122
123 def timenow():
124 """return current time as a string
125 """
126 return time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(time.time()))
127
128 html_escape_table = {
129 "&": "&amp;",
130 ">": "&gt;",
131 "<": "&lt;",
132 "$": "\$"
133 }
134
135 def html_escape(text):
136 """Produce entities within text."""
137 return "".join(html_escape_table.get(c,c) for c in text)
138
139 def cmd_exists(cmd):
140 return subprocess.call("type " + cmd, shell=True,
141 stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
142
143
144 def parse_citations(citations_text):
145 """
146 """
147 citations = [c for c in citations_text.split("**ENTRY**") if c.strip()]
148 citation_tuples = []
149 for citation in citations:
150 if citation.startswith("doi"):
151 citation_tuples.append( ("doi", citation[len("doi"):].strip() ) )
152 else:
153 citation_tuples.append( ("bibtex", citation[len("bibtex"):].strip() ) )
154 return citation_tuples
155
156
157 class ScriptRunner:
158 """class is a wrapper for an arbitrary script
159 """
160
161 def __init__(self,opts=None,treatbashSpecial=True):
162 """
163 cleanup inputs, setup some outputs
164
165 """
166 self.useGM = cmd_exists('gm')
167 self.useIM = cmd_exists('convert')
168 self.useGS = cmd_exists('gs')
169 self.temp_warned = False # we want only one warning if $TMP not set
170 self.treatbashSpecial = treatbashSpecial
171 if opts.output_dir: # simplify for the tool tarball
172 os.chdir(opts.output_dir)
173 self.thumbformat = 'png'
174 self.opts = opts
175 self.toolname = re.sub('[^a-zA-Z0-9_]+', '', opts.tool_name) # a sanitizer now does this but..
176 self.toolid = self.toolname
177 self.myname = sys.argv[0] # get our name because we write ourselves out as a tool later
178 self.pyfile = self.myname # crude but efficient - the cruft won't hurt much
179 self.xmlfile = '%s.xml' % self.toolname
180 s = open(self.opts.script_path,'r').readlines()
181 s = [x.rstrip() for x in s] # remove pesky dos line endings if needed
182 self.script = '\n'.join(s)
183 fhandle,self.sfile = tempfile.mkstemp(prefix=self.toolname,suffix=".%s" % (opts.interpreter))
184 tscript = open(self.sfile,'w') # use self.sfile as script source for Popen
185 tscript.write(self.script)
186 tscript.close()
187 self.indentedScript = '\n'.join([' %s' % html_escape(x) for x in s]) # for restructured text in help
188 self.escapedScript = '\n'.join([html_escape(x) for x in s])
189 self.elog = os.path.join(self.opts.output_dir,"%s_error.log" % self.toolname)
190 if opts.output_dir: # may not want these complexities
191 self.tlog = os.path.join(self.opts.output_dir,"%s_runner.log" % self.toolname)
192 art = '%s.%s' % (self.toolname,opts.interpreter)
193 artpath = os.path.join(self.opts.output_dir,art) # need full path
194 artifact = open(artpath,'w') # use self.sfile as script source for Popen
195 artifact.write(self.script)
196 artifact.close()
197 self.cl = []
198 self.html = []
199 a = self.cl.append
200 a(opts.interpreter)
201 if self.treatbashSpecial and opts.interpreter in ['bash','sh']:
202 a(self.sfile)
203 else:
204 a('-') # stdin
205 a(opts.input_tab)
206 a(opts.output_tab)
207 self.outputFormat = self.opts.output_format
208 self.inputFormats = self.opts.input_formats
209 self.test1Input = '%s_test1_input.xls' % self.toolname
210 self.test1Output = '%s_test1_output.xls' % self.toolname
211 self.test1HTML = '%s_test1_output.html' % self.toolname
212
213 def makeXML(self):
214 """
215 Create a Galaxy xml tool wrapper for the new script as a string to write out
216 fixme - use templating or something less fugly than this example of what we produce
217
218 <tool id="reverse" name="reverse" version="0.01">
219 <description>a tabular file</description>
220 <command interpreter="python">
221 reverse.py --script_path "$runMe" --interpreter "python"
222 --tool_name "reverse" --input_tab "$input1" --output_tab "$tab_file"
223 </command>
224 <inputs>
225 <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"/>
226
227 </inputs>
228 <outputs>
229 <data format="tabular" name="tab_file" label="${job_name}"/>
230
231 </outputs>
232 <help>
233
234 **What it Does**
235
236 Reverse the columns in a tabular file
237
238 </help>
239 <configfiles>
240 <configfile name="runMe">
241
242 # reverse order of columns in a tabular file
243 import sys
244 inp = sys.argv[1]
245 outp = sys.argv[2]
246 i = open(inp,'r')
247 o = open(outp,'w')
248 for row in i:
249 rs = row.rstrip().split('\t')
250 rs.reverse()
251 o.write('\t'.join(rs))
252 o.write('\n')
253 i.close()
254 o.close()
255
256
257 </configfile>
258 </configfiles>
259 </tool>
260
261 """
262 newXML="""<tool id="%(toolid)s" name="%(toolname)s" version="%(tool_version)s">
263 %(tooldesc)s
264 %(requirements)s
265 <command interpreter="python">
266 %(command)s
267 </command>
268 <inputs>
269 %(inputs)s
270 </inputs>
271 <outputs>
272 %(outputs)s
273 </outputs>
274 <configfiles>
275 <configfile name="runMe">
276 %(script)s
277 </configfile>
278 </configfiles>
279
280 %(tooltests)s
281
282 <help>
283
284 %(help)s
285
286 </help>
287 <citations>
288 %(citations)s
289 <citation type="doi">10.1093/bioinformatics/bts573</citation>
290 </citations>
291 </tool>""" # needs a dict with toolname, toolid, interpreter, scriptname, command, inputs as a multi line string ready to write, outputs ditto, help ditto
292
293 newCommand="""
294 %(toolname)s.py --script_path "$runMe" --interpreter "%(interpreter)s"
295 --tool_name "%(toolname)s" %(command_inputs)s %(command_outputs)s """
296 # may NOT be an input or htmlout - appended later
297 tooltestsTabOnly = """
298 <tests>
299 <test>
300 <param name="input1" value="%(test1Input)s" ftype="%(inputFormats)s"/>
301 <param name="job_name" value="test1"/>
302 <param name="runMe" value="$runMe"/>
303 <output name="tab_file" file="%(test1Output)s" ftype="%(outputFormat)s"/>
304 </test>
305 </tests>
306 """
307 tooltestsHTMLOnly = """
308 <tests>
309 <test>
310 <param name="input1" value="%(test1Input)s" ftype="%(inputFormats)s"/>
311 <param name="job_name" value="test1"/>
312 <param name="runMe" value="$runMe"/>
313 <output name="html_file" file="%(test1HTML)s" ftype="html" lines_diff="5"/>
314 </test>
315 </tests>
316 """
317 tooltestsBoth = """<tests>
318 <test>
319 <param name="input1" value="%(test1Input)s" ftype="%(inputFormats)s"/>
320 <param name="job_name" value="test1"/>
321 <param name="runMe" value="$runMe"/>
322 <output name="tab_file" file="%(test1Output)s" ftype="%(outputFormat)s" />
323 <output name="html_file" file="%(test1HTML)s" ftype="html" lines_diff="10"/>
324 </test>
325 </tests>
326 """
327 xdict = {}
328 xdict['outputFormat'] = self.outputFormat
329 xdict['inputFormats'] = self.inputFormats
330 xdict['requirements'] = ''
331 if self.opts.make_HTML:
332 if self.opts.include_dependencies == "yes":
333 xdict['requirements'] = protorequirements
334 xdict['tool_version'] = self.opts.tool_version
335 xdict['test1Input'] = self.test1Input
336 xdict['test1HTML'] = self.test1HTML
337 xdict['test1Output'] = self.test1Output
338 if self.opts.make_HTML and self.opts.output_tab <> 'None':
339 xdict['tooltests'] = tooltestsBoth % xdict
340 elif self.opts.make_HTML:
341 xdict['tooltests'] = tooltestsHTMLOnly % xdict
342 else:
343 xdict['tooltests'] = tooltestsTabOnly % xdict
344 xdict['script'] = self.escapedScript
345 # configfile is least painful way to embed script to avoid external dependencies
346 # but requires escaping of <, > and $ to avoid Mako parsing
347 if self.opts.help_text:
348 helptext = open(self.opts.help_text,'r').readlines()
349 helptext = [html_escape(x) for x in helptext] # must html escape here too - thanks to Marius van den Beek
350 xdict['help'] = ''.join([x for x in helptext])
351 else:
352 xdict['help'] = 'Please ask the tool author (%s) for help as none was supplied at tool generation\n' % (self.opts.user_email)
353 if self.opts.citations:
354 citationstext = open(self.opts.citations,'r').read()
355 citation_tuples = parse_citations(citationstext)
356 citations_xml = ""
357 for citation_type, citation_content in citation_tuples:
358 citation_xml = """<citation type="%s">%s</citation>""" % (citation_type, html_escape(citation_content))
359 citations_xml += citation_xml
360 xdict['citations'] = citations_xml
361 else:
362 xdict['citations'] = ""
363 coda = ['**Script**','Pressing execute will run the following code over your input file and generate some outputs in your history::']
364 coda.append('\n')
365 coda.append(self.indentedScript)
366 coda.append('\n**Attribution**\nThis Galaxy tool was created by %s at %s\nusing the Galaxy Tool Factory.\n' % (self.opts.user_email,timenow()))
367 coda.append('See %s for details of that project' % (toolFactoryURL))
368 coda.append('Please cite: Creating re-usable tools from scripts: The Galaxy Tool Factory. Ross Lazarus; Antony Kaspi; Mark Ziemann; The Galaxy Team. ')
369 coda.append('Bioinformatics 2012; doi: 10.1093/bioinformatics/bts573\n')
370 xdict['help'] = '%s\n%s' % (xdict['help'],'\n'.join(coda))
371 if self.opts.tool_desc:
372 xdict['tooldesc'] = '<description>%s</description>' % self.opts.tool_desc
373 else:
374 xdict['tooldesc'] = ''
375 xdict['command_outputs'] = ''
376 xdict['outputs'] = ''
377 if self.opts.input_tab <> 'None':
378 xdict['command_inputs'] = '--input_tab "$input1" ' # the space may matter a lot if we append something
379 xdict['inputs'] = '<param name="input1" type="data" format="%s" label="Select a suitable input file from your history"/> \n' % self.inputFormats
380 else:
381 xdict['command_inputs'] = '' # assume no input - eg a random data generator
382 xdict['inputs'] = ''
383 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
384 xdict['toolname'] = self.toolname
385 xdict['toolid'] = self.toolid
386 xdict['interpreter'] = self.opts.interpreter
387 xdict['scriptname'] = self.sfile
388 if self.opts.make_HTML:
389 xdict['command_outputs'] += ' --output_dir "$html_file.files_path" --output_html "$html_file" --make_HTML "yes"'
390 xdict['outputs'] += ' <data format="html" name="html_file" label="${job_name}.html"/>\n'
391 else:
392 xdict['command_outputs'] += ' --output_dir "./"'
393 if self.opts.output_tab <> 'None':
394 xdict['command_outputs'] += ' --output_tab "$tab_file"'
395 xdict['outputs'] += ' <data format="%s" name="tab_file" label="${job_name}"/>\n' % self.outputFormat
396 xdict['command'] = newCommand % xdict
397 xmls = newXML % xdict
398 xf = open(self.xmlfile,'w')
399 xf.write(xmls)
400 xf.write('\n')
401 xf.close()
402 # ready for the tarball
403
404
405 def makeTooltar(self):
406 """
407 a tool is a gz tarball with eg
408 /toolname/tool.xml /toolname/tool.py /toolname/test-data/test1_in.foo ...
409 """
410 retval = self.run()
411 if retval:
412 print >> sys.stderr,'## Run failed. Cannot build yet. Please fix and retry'
413 sys.exit(1)
414 tdir = self.toolname
415 os.mkdir(tdir)
416 self.makeXML()
417 if self.opts.make_HTML:
418 if self.opts.help_text:
419 hlp = open(self.opts.help_text,'r').read()
420 else:
421 hlp = 'Please ask the tool author for help as none was supplied at tool generation\n'
422 if self.opts.include_dependencies:
423 tooldepcontent = toolhtmldepskel % hlp
424 depf = open(os.path.join(tdir,'tool_dependencies.xml'),'w')
425 depf.write(tooldepcontent)
426 depf.write('\n')
427 depf.close()
428 if self.opts.input_tab <> 'None': # no reproducible test otherwise? TODO: maybe..
429 testdir = os.path.join(tdir,'test-data')
430 os.mkdir(testdir) # make tests directory
431 shutil.copyfile(self.opts.input_tab,os.path.join(testdir,self.test1Input))
432 if self.opts.output_tab <> 'None':
433 shutil.copyfile(self.opts.output_tab,os.path.join(testdir,self.test1Output))
434 if self.opts.make_HTML:
435 shutil.copyfile(self.opts.output_html,os.path.join(testdir,self.test1HTML))
436 if self.opts.output_dir:
437 shutil.copyfile(self.tlog,os.path.join(testdir,'test1_out.log'))
438 outpif = '%s.py' % self.toolname # new name
439 outpiname = os.path.join(tdir,outpif) # path for the tool tarball
440 pyin = os.path.basename(self.pyfile) # our name - we rewrite ourselves (TM)
441 notes = ['# %s - a self annotated version of %s generated by running %s\n' % (outpiname,pyin,pyin),]
442 notes.append('# to make a new Galaxy tool called %s\n' % self.toolname)
443 notes.append('# User %s at %s\n' % (self.opts.user_email,timenow()))
444 pi = open(self.pyfile,'r').readlines() # our code becomes new tool wrapper (!) - first Galaxy worm
445 notes += pi
446 outpi = open(outpiname,'w')
447 outpi.write(''.join(notes))
448 outpi.write('\n')
449 outpi.close()
450 stname = os.path.join(tdir,self.sfile)
451 if not os.path.exists(stname):
452 shutil.copyfile(self.sfile, stname)
453 xtname = os.path.join(tdir,self.xmlfile)
454 if not os.path.exists(xtname):
455 shutil.copyfile(self.xmlfile,xtname)
456 tarpath = "%s.gz" % self.toolname
457 tar = tarfile.open(tarpath, "w:gz")
458 tar.add(tdir,arcname=self.toolname)
459 tar.close()
460 shutil.copyfile(tarpath,self.opts.new_tool)
461 shutil.rmtree(tdir)
462 ## TODO: replace with optional direct upload to local toolshed?
463 return retval
464
465
466 def compressPDF(self,inpdf=None,thumbformat='png'):
467 """need absolute path to pdf
468 note that GS gets confoozled if no $TMP or $TEMP
469 so we set it
470 """
471 assert os.path.isfile(inpdf), "## Input %s supplied to %s compressPDF not found" % (inpdf,self.myName)
472 hlog = os.path.join(self.opts.output_dir,"compress_%s.txt" % os.path.basename(inpdf))
473 sto = open(hlog,'a')
474 our_env = os.environ.copy()
475 our_tmp = our_env.get('TMP',None)
476 if not our_tmp:
477 our_tmp = our_env.get('TEMP',None)
478 if not (our_tmp and os.path.exists(our_tmp)):
479 newtmp = os.path.join(self.opts.output_dir,'tmp')
480 try:
481 os.mkdir(newtmp)
482 except:
483 sto.write('## WARNING - cannot make %s - it may exist or permissions need fixing\n' % newtmp)
484 our_env['TEMP'] = newtmp
485 if not self.temp_warned:
486 sto.write('## WARNING - no $TMP or $TEMP!!! Please fix - using %s temporarily\n' % newtmp)
487 self.temp_warned = True
488 outpdf = '%s_compressed' % inpdf
489 cl = ["gs", "-sDEVICE=pdfwrite", "-dNOPAUSE", "-dUseCIEColor", "-dBATCH","-dPDFSETTINGS=/printer", "-sOutputFile=%s" % outpdf,inpdf]
490 x = subprocess.Popen(cl,stdout=sto,stderr=sto,cwd=self.opts.output_dir,env=our_env)
491 retval1 = x.wait()
492 sto.close()
493 if retval1 == 0:
494 os.unlink(inpdf)
495 shutil.move(outpdf,inpdf)
496 os.unlink(hlog)
497 hlog = os.path.join(self.opts.output_dir,"thumbnail_%s.txt" % os.path.basename(inpdf))
498 sto = open(hlog,'w')
499 outpng = '%s.%s' % (os.path.splitext(inpdf)[0],thumbformat)
500 if self.useGM:
501 cl2 = ['gm', 'convert', inpdf, outpng]
502 else: # assume imagemagick
503 cl2 = ['convert', inpdf, outpng]
504 x = subprocess.Popen(cl2,stdout=sto,stderr=sto,cwd=self.opts.output_dir,env=our_env)
505 retval2 = x.wait()
506 sto.close()
507 if retval2 == 0:
508 os.unlink(hlog)
509 retval = retval1 or retval2
510 return retval
511
512
513 def getfSize(self,fpath,outpath):
514 """
515 format a nice file size string
516 """
517 size = ''
518 fp = os.path.join(outpath,fpath)
519 if os.path.isfile(fp):
520 size = '0 B'
521 n = float(os.path.getsize(fp))
522 if n > 2**20:
523 size = '%1.1f MB' % (n/2**20)
524 elif n > 2**10:
525 size = '%1.1f KB' % (n/2**10)
526 elif n > 0:
527 size = '%d B' % (int(n))
528 return size
529
530 def makeHtml(self):
531 """ Create an HTML file content to list all the artifacts found in the output_dir
532 """
533
534 galhtmlprefix = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
535 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
536 <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
537 <meta name="generator" content="Galaxy %s tool output - see http://g2.trac.bx.psu.edu/" />
538 <title></title>
539 <link rel="stylesheet" href="/static/style/base.css" type="text/css" />
540 </head>
541 <body>
542 <div class="toolFormBody">
543 """
544 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/>"""
545 galhtmlpostfix = """</div></body></html>\n"""
546
547 flist = os.listdir(self.opts.output_dir)
548 flist = [x for x in flist if x <> 'Rplots.pdf']
549 flist.sort()
550 html = []
551 html.append(galhtmlprefix % progname)
552 html.append('<div class="infomessage">Galaxy Tool "%s" run at %s</div><br/>' % (self.toolname,timenow()))
553 fhtml = []
554 if len(flist) > 0:
555 logfiles = [x for x in flist if x.lower().endswith('.log')] # log file names determine sections
556 logfiles.sort()
557 logfiles = [x for x in logfiles if os.path.abspath(x) <> os.path.abspath(self.tlog)]
558 logfiles.append(os.path.abspath(self.tlog)) # make it the last one
559 pdflist = []
560 npdf = len([x for x in flist if os.path.splitext(x)[-1].lower() == '.pdf'])
561 for rownum,fname in enumerate(flist):
562 dname,e = os.path.splitext(fname)
563 sfsize = self.getfSize(fname,self.opts.output_dir)
564 if e.lower() == '.pdf' : # compress and make a thumbnail
565 thumb = '%s.%s' % (dname,self.thumbformat)
566 pdff = os.path.join(self.opts.output_dir,fname)
567 retval = self.compressPDF(inpdf=pdff,thumbformat=self.thumbformat)
568 if retval == 0:
569 pdflist.append((fname,thumb))
570 else:
571 pdflist.append((fname,fname))
572 if (rownum+1) % 2 == 0:
573 fhtml.append('<tr class="odd_row"><td><a href="%s">%s</a></td><td>%s</td></tr>' % (fname,fname,sfsize))
574 else:
575 fhtml.append('<tr><td><a href="%s">%s</a></td><td>%s</td></tr>' % (fname,fname,sfsize))
576 for logfname in logfiles: # expect at least tlog - if more
577 if os.path.abspath(logfname) == os.path.abspath(self.tlog): # handled later
578 sectionname = 'All tool run'
579 if (len(logfiles) > 1):
580 sectionname = 'Other'
581 ourpdfs = pdflist
582 else:
583 realname = os.path.basename(logfname)
584 sectionname = os.path.splitext(realname)[0].split('_')[0] # break in case _ added to log
585 ourpdfs = [x for x in pdflist if os.path.basename(x[0]).split('_')[0] == sectionname]
586 pdflist = [x for x in pdflist if os.path.basename(x[0]).split('_')[0] <> sectionname] # remove
587 nacross = 1
588 npdf = len(ourpdfs)
589
590 if npdf > 0:
591 nacross = math.sqrt(npdf) ## int(round(math.log(npdf,2)))
592 if int(nacross)**2 != npdf:
593 nacross += 1
594 nacross = int(nacross)
595 width = min(400,int(1200/nacross))
596 html.append('<div class="toolFormTitle">%s images and outputs</div>' % sectionname)
597 html.append('(Click on a thumbnail image to download the corresponding original PDF image)<br/>')
598 ntogo = nacross # counter for table row padding with empty cells
599 html.append('<div><table class="simple" cellpadding="2" cellspacing="2">\n<tr>')
600 for i,paths in enumerate(ourpdfs):
601 fname,thumb = paths
602 s= """<td><a href="%s"><img src="%s" title="Click to download a PDF of %s" hspace="5" width="%d"
603 alt="Image called %s"/></a></td>\n""" % (fname,thumb,fname,width,fname)
604 if ((i+1) % nacross == 0):
605 s += '</tr>\n'
606 ntogo = 0
607 if i < (npdf - 1): # more to come
608 s += '<tr>'
609 ntogo = nacross
610 else:
611 ntogo -= 1
612 html.append(s)
613 if html[-1].strip().endswith('</tr>'):
614 html.append('</table></div>\n')
615 else:
616 if ntogo > 0: # pad
617 html.append('<td>&nbsp;</td>'*ntogo)
618 html.append('</tr></table></div>\n')
619 logt = open(logfname,'r').readlines()
620 logtext = [x for x in logt if x.strip() > '']
621 html.append('<div class="toolFormTitle">%s log output</div>' % sectionname)
622 if len(logtext) > 1:
623 html.append('\n<pre>\n')
624 html += logtext
625 html.append('\n</pre>\n')
626 else:
627 html.append('%s is empty<br/>' % logfname)
628 if len(fhtml) > 0:
629 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')
630 fhtml.append('</table></div><br/>')
631 html.append('<div class="toolFormTitle">All output files available for downloading</div>\n')
632 html += fhtml # add all non-pdf files to the end of the display
633 else:
634 html.append('<div class="warningmessagelarge">### Error - %s returned no files - please confirm that parameters are sane</div>' % self.opts.interpreter)
635 html.append(galhtmlpostfix)
636 htmlf = file(self.opts.output_html,'w')
637 htmlf.write('\n'.join(html))
638 htmlf.write('\n')
639 htmlf.close()
640 self.html = html
641
642
643 def run(self):
644 """
645 scripts must be small enough not to fill the pipe!
646 """
647 if self.treatbashSpecial and self.opts.interpreter in ['bash','sh']:
648 retval = self.runBash()
649 else:
650 if self.opts.output_dir:
651 ste = open(self.elog,'w')
652 sto = open(self.tlog,'w')
653 sto.write('## Toolfactory generated command line = %s\n' % ' '.join(self.cl))
654 sto.flush()
655 p = subprocess.Popen(self.cl,shell=False,stdout=sto,stderr=ste,stdin=subprocess.PIPE,cwd=self.opts.output_dir)
656 else:
657 p = subprocess.Popen(self.cl,shell=False,stdin=subprocess.PIPE)
658 p.stdin.write(self.script)
659 p.stdin.close()
660 retval = p.wait()
661 if self.opts.output_dir:
662 sto.close()
663 ste.close()
664 err = open(self.elog,'r').readlines()
665 if retval <> 0 and err: # problem
666 print >> sys.stderr,err
667 if self.opts.make_HTML:
668 self.makeHtml()
669 return retval
670
671 def runBash(self):
672 """
673 cannot use - for bash so use self.sfile
674 """
675 if self.opts.output_dir:
676 s = '## Toolfactory generated command line = %s\n' % ' '.join(self.cl)
677 sto = open(self.tlog,'w')
678 sto.write(s)
679 sto.flush()
680 p = subprocess.Popen(self.cl,shell=False,stdout=sto,stderr=sto,cwd=self.opts.output_dir)
681 else:
682 p = subprocess.Popen(self.cl,shell=False)
683 retval = p.wait()
684 if self.opts.output_dir:
685 sto.close()
686 if self.opts.make_HTML:
687 self.makeHtml()
688 return retval
689
690
691 def main():
692 u = """
693 This is a Galaxy wrapper. It expects to be called by a special purpose tool.xml as:
694 <command interpreter="python">rgBaseScriptWrapper.py --script_path "$scriptPath" --tool_name "foo" --interpreter "Rscript"
695 </command>
696 """
697 op = optparse.OptionParser()
698 a = op.add_option
699 a('--script_path',default=None)
700 a('--tool_name',default=None)
701 a('--interpreter',default=None)
702 a('--output_dir',default='./')
703 a('--output_html',default=None)
704 a('--input_tab',default="None")
705 a('--input_formats',default="tabular,text")
706 a('--output_tab',default="None")
707 a('--output_format',default="tabular")
708 a('--user_email',default='Unknown')
709 a('--bad_user',default=None)
710 a('--make_Tool',default=None)
711 a('--make_HTML',default=None)
712 a('--help_text',default=None)
713 a('--citations',default=None)
714 a('--tool_desc',default=None)
715 a('--new_tool',default=None)
716 a('--tool_version',default=None)
717 a('--include_dependencies',default=None)
718 opts, args = op.parse_args()
719 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)
720 assert opts.tool_name,'## Tool Factory expects a tool name - eg --tool_name=DESeq'
721 assert opts.interpreter,'## Tool Factory wrapper expects an interpreter - eg --interpreter=Rscript'
722 assert os.path.isfile(opts.script_path),'## Tool Factory wrapper expects a script path - eg --script_path=foo.R'
723 if opts.output_dir:
724 try:
725 os.makedirs(opts.output_dir)
726 except:
727 pass
728 r = ScriptRunner(opts)
729 if opts.make_Tool:
730 retcode = r.makeTooltar()
731 else:
732 retcode = r.run()
733 os.unlink(r.sfile)
734 if retcode:
735 sys.exit(retcode) # indicate failure to job runner
736
737
738 if __name__ == "__main__":
739 main()
740
741
742