Mercurial > repos > fubar > brokenandnotdeletablebyowneroradmin
comparison rgToolFactory.py @ 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 |
comparison
equal
deleted
inserted
replaced
7:7221619caefa | 8:220885b2d7ee |
---|---|
28 import time | 28 import time |
29 import tempfile | 29 import tempfile |
30 import optparse | 30 import optparse |
31 import tarfile | 31 import tarfile |
32 import re | 32 import re |
33 import shutil | |
34 | |
33 progname = os.path.split(sys.argv[0])[1] | 35 progname = os.path.split(sys.argv[0])[1] |
34 myversion = 'V000.1 May 2012' | 36 myversion = 'V000.1 May 2012' |
35 verbose = False | 37 verbose = False |
36 debug = False | 38 debug = False |
37 | 39 |
38 | |
39 galhtmlprefix = """<?xml version="1.0" encoding="utf-8" ?> | |
40 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
41 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
42 <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
43 <meta name="generator" content="Galaxy %s tool output - see http://g2.trac.bx.psu.edu/" /> | |
44 <title></title> | |
45 <link rel="stylesheet" href="/static/style/base.css" type="text/css" /> | |
46 </head> | |
47 <body> | |
48 <div class="document"> | |
49 """ | |
50 galhtmlattr = """<b><a href="http://rgenetics.org">Galaxy Rgenetics Base Script Wrapper based </a> tool output %s run at %s</b><br/>""" | |
51 galhtmlpostfix = """</div></body></html>\n""" | |
52 | 40 |
53 def timenow(): | 41 def timenow(): |
54 """return current time as a string | 42 """return current time as a string |
55 """ | 43 """ |
56 return time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(time.time())) | 44 return time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(time.time())) |
95 __cr____cn__inp = read.table(inf,head=T,rownames=F,sep=__sq__Xt__sq__) | 83 __cr____cn__inp = read.table(inf,head=T,rownames=F,sep=__sq__Xt__sq__) |
96 __cr____cn__ write.table(inp,outf, quote=FALSE, sep=__dq__Xt__dq__,row.names=F) | 84 __cr____cn__ write.table(inp,outf, quote=FALSE, sep=__dq__Xt__dq__,row.names=F) |
97 __cr____cn__sessionInfo() | 85 __cr____cn__sessionInfo() |
98 __cr____cn__ | 86 __cr____cn__ |
99 """ | 87 """ |
100 self.myname = sys.argv[0] # get our name because we write ourselves out as a tool later | 88 if opts.output_dir: # simplify for the tool tarball |
89 os.chdir(opts.output_dir) | |
101 self.thumbformat = 'jpg' | 90 self.thumbformat = 'jpg' |
102 self.opts = opts | 91 self.opts = opts |
103 self.toolname = re.sub('[^a-zA-Z0-9_]+', '', opts.tool_name) | 92 self.toolname = re.sub('[^a-zA-Z0-9_]+', '', opts.tool_name) |
93 self.toolid = self.toolname | |
104 s = open(self.opts.script_path,'r').read() | 94 s = open(self.opts.script_path,'r').read() |
105 self.script = restore_text(s) | 95 self.script = restore_text(s) |
106 self.pyfile = self.myname | 96 self.myname = sys.argv[0] # get our name because we write ourselves out as a tool later |
107 self.xmlfile = '%s.xml' % os.path.splitext(self.pyfile)[0] # punt | 97 self.pyfile = self.myname # crude but efficient - the cruft won't hurt much |
98 self.xmlfile = '%s.xml' % self.toolname | |
108 self.sfile = '%s.%s' % (self.toolname,opts.interpreter) | 99 self.sfile = '%s.%s' % (self.toolname,opts.interpreter) |
109 localscript = open(self.sfile,'w') | 100 localscript = open(self.sfile,'w') |
110 localscript.write(self.script) | 101 localscript.write(self.script) |
111 localscript.close() | 102 localscript.close() |
112 if opts.output_dir or self.opts.makeTool: # may not want these complexities if a simple script | 103 if opts.output_dir or self.opts.make_Tool: # may not want these complexities if a simple script |
113 self.tlog = os.path.join(opts.output_dir,"%s_runner.log" % self.toolname) | 104 self.tlog = os.path.join(opts.output_dir,"%s_runner.log" % self.toolname) |
114 artifactpath = os.path.join(opts.output_dir,'%s_run.script' % self.toolname) | 105 artifactpath = os.path.join(opts.output_dir,'%s_run.script' % self.toolname) |
115 artifact = open(artifactpath,'w') | 106 artifact = open(artifactpath,'w') |
116 artifact.write(self.script) | 107 artifact.write(self.script) |
117 artifact.write('\n') | 108 artifact.write('\n') |
121 a = self.cl.append | 112 a = self.cl.append |
122 a(opts.interpreter) | 113 a(opts.interpreter) |
123 a('-') # use stdin | 114 a('-') # use stdin |
124 a(opts.input_tab) | 115 a(opts.input_tab) |
125 a(opts.output_tab) | 116 a(opts.output_tab) |
117 self.outFormats = 'tabular' # TODO make this an option at tool generation time | |
118 self.inputFormats = 'tabular' # TODO make this an option at tool generation time | |
119 | |
120 | |
121 def makeXML(self): | |
122 """ | |
123 Create a Galaxy xml tool wrapper for the new script as a string to write out | |
124 """ | |
125 newXML="""<tool id="%(toolid)s" name="%(toolname)s" version="0.01"> | |
126 %(tooldesc)s | |
127 %(command)s | |
128 <inputs> | |
129 %(inputs)s | |
130 </inputs> | |
131 <outputs> | |
132 %(outputs)s | |
133 </outputs> | |
134 <help> | |
135 %(help)s | |
136 </help> | |
137 <configfiles> | |
138 <configfile name="runMe"> | |
139 %(script)s | |
140 </configfile> | |
141 </configfiles> | |
142 </tool>""" # needs a dict with toolname, toolid, interpreter, scriptname, command, inputs as a multi line string ready to write, outputs ditto, help ditto | |
143 | |
144 newCommand="""<command interpreter="python"> | |
145 %(toolname)s.py --script_path "$runMe" --interpreter "%(interpreter)s" | |
146 --tool_name "%(toolname)s" %(command_inputs)s %(command_outputs)s | |
147 </command>""" # may NOT be an input or htmlout | |
148 | |
149 xdict = {} | |
150 xdict['script'] = self.script # we pass this as a configfile because it's less painful that galaxy_tool_data_dir | |
151 # embed script in tool - remove dependence on something else outside in the wilds | |
152 if self.opts.help_text: | |
153 h = open(self.opts.help_text,'r').read() | |
154 xdict['help'] = restore_text(h) | |
155 else: | |
156 xdict['help'] = 'Please ask the tool author for help as none was supplied at tool generation' | |
157 if self.opts.tool_desc: | |
158 xdict['tooldesc'] = '<description>%s</description>' % self.opts.tool_desc | |
159 else: | |
160 xdict['tooldesc'] = '' | |
161 xdict['command_outputs'] = '' # will probably be some! | |
162 xdict['outputs'] = '' # will probably be some! | |
163 if self.opts.input_tab: | |
164 xdict['command_inputs'] = '--input_tab "$input1"' | |
165 xdict['inputs'] = '<param name="input1" type="data" format="%s" label="Select a suitable input file from your history"/>' % self.inputFormats | |
166 else: | |
167 xdict['command_inputs'] = '' # assume no input - eg a random data generator | |
168 xdict['inputs'] = '' | |
169 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 | |
170 xdict['toolname'] = self.toolname | |
171 xdict['toolid'] = self.toolid | |
172 xdict['interpreter'] = self.opts.interpreter | |
173 xdict['scriptname'] = self.sfile | |
174 if self.opts.make_HTML: | |
175 xdict['command_outputs'] += '--output_dir "$html_file.files_path" --output_html "$html_file"' | |
176 xdict['outputs'] += '<data format="html" name="html_file" label="${job_name}.html"/>\n' | |
177 if self.opts.output_tab: | |
178 xdict['command_outputs'] += '--output_tab "$tab_file"' | |
179 xdict['outputs'] += '<data format="%s" name="tab_file" label="${job_name}"/>\n' % self.outFormats | |
180 xdict['command'] = newCommand % xdict | |
181 xmls = newXML % xdict | |
182 xf = open(self.xmlfile,'w') | |
183 xf.write(xmls) | |
184 xf.write('\n') | |
185 xf.close() | |
186 # ready for the tarball | |
187 | |
126 | 188 |
127 def makeTooltar(self): | 189 def makeTooltar(self): |
128 """ | 190 """ |
129 a tool is a gz tarball with eg | 191 a tool is a gz tarball with eg |
130 /toolname/tool.xml /toolname/tool.py /toolname/test-data/test1_in.foo ... | 192 /toolname/tool.xml /toolname/tool.py /toolname/test-data/test1_in.foo ... |
131 """ | 193 """ |
132 retval = self.run() | 194 retval = self.run() |
133 if retval: | 195 if retval: |
134 print >> sys.stderr,'## Run failed. Cannot build yet. Please fix and retry' | 196 print >> sys.stderr,'## Run failed. Cannot build yet. Please fix and retry' |
135 sys.exit(1) | 197 sys.exit(1) |
136 tarpath = os.path.join(self.opts.output_dir,"%s.gz" % self.toolname) | 198 self.makeXML() |
199 tdir = self.toolname | |
200 os.mkdir(tdir) | |
201 shutil.copyfile(self.xmlfile,os.path.join(tdir,self.xmlfile)) | |
202 shutil.copyfile(self.pyfile,os.path.join(tdir,'%s.py' % self.toolname)) | |
203 shutil.copyfile(self.sfile,os.path.join(tdir,self.sfile)) | |
204 tarpath = "%s.gz" % self.toolname | |
137 tar = tarfile.open(tarpath, "w:gz") | 205 tar = tarfile.open(tarpath, "w:gz") |
138 tar.add(self.xmlfile,arcname='%s.xml' % self.toolname) | 206 tar.add(tdir,arcname=self.toolname) |
139 tar.add(self.pyfile,arcname=os.path.basename(self.pyfile)) | |
140 tar.add(self.sfile,arcname=self.sfile) | |
141 tar.close() | 207 tar.close() |
142 self.makeHtml() | 208 shutil.rmtree(tdir) |
209 self.makeHtml() # call this to return the new gzip inside the autogenerated html file | |
210 ## TODO: replace with optional direct upload to local toolshed? | |
143 return retval | 211 return retval |
144 | 212 |
145 def compressPDF(self,inpdf=None,thumbformat='png'): | 213 def compressPDF(self,inpdf=None,thumbformat='png'): |
146 """need absolute path to pdf | 214 """need absolute path to pdf |
147 """ | 215 """ |
179 elif n > 0: | 247 elif n > 0: |
180 size = ' (%d B)' % (int(n)) | 248 size = ' (%d B)' % (int(n)) |
181 return size | 249 return size |
182 | 250 |
183 def makeHtml(self): | 251 def makeHtml(self): |
184 """ | 252 """ Create an HTML file content to list all the artefacts found in the output_dir |
185 """ | 253 """ |
186 flist = os.listdir(self.opts.output_dir) | 254 |
187 flist = [x for x in flist if x <> 'Rplots.pdf'] | 255 galhtmlprefix = """<?xml version="1.0" encoding="utf-8" ?> |
188 flist.sort() | 256 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
189 html = [galhtmlprefix % progname,] | 257 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
190 html.append('<h2>Galaxy %s outputs run at %s</h2><br/>\n' % (self.toolname,timenow())) | 258 <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
191 fhtml = [] | 259 <meta name="generator" content="Galaxy %s tool output - see http://g2.trac.bx.psu.edu/" /> |
192 if len(flist) > 0: | 260 <title></title> |
193 html.append('<table cellpadding="3" cellspacing="3">\n') | 261 <link rel="stylesheet" href="/static/style/base.css" type="text/css" /> |
194 for fname in flist: | 262 </head> |
263 <body> | |
264 <div class="document"> | |
265 """ | |
266 galhtmlattr = """<hr/><b><a href="http://rgenetics.org">Galaxy Tool Factory Script Wrapper</a> tool output %s run at %s</b><br/>""" | |
267 galhtmlpostfix = """</div></body></html>\n""" | |
268 | |
269 flist = os.listdir(self.opts.output_dir) | |
270 flist = [x for x in flist if x <> 'Rplots.pdf'] | |
271 flist.sort() | |
272 html = [galhtmlprefix % progname,] | |
273 html.append('<h2>Galaxy %s outputs run at %s</h2><br/>\n' % (self.toolname,timenow())) | |
274 fhtml = [] | |
275 if len(flist) > 0: | |
276 html.append('<table cellpadding="3" cellspacing="3">\n') | |
277 for fname in flist: | |
195 dname,e = os.path.splitext(fname) | 278 dname,e = os.path.splitext(fname) |
196 sfsize = self.getfSize(fname,self.opts.output_dir) | 279 sfsize = self.getfSize(fname,self.opts.output_dir) |
197 if e.lower() == '.pdf' : # compress and make a thumbnail | 280 if e.lower() == '.pdf' : # compress and make a thumbnail |
198 thumb = '%s.%s' % (dname,self.thumbformat) | 281 thumb = '%s.%s' % (dname,self.thumbformat) |
199 pdff = os.path.join(self.opts.output_dir,fname) | 282 pdff = os.path.join(self.opts.output_dir,fname) |
202 s= '<tr><td><a href="%s"><img src="%s" title="Click to download a PDF of %s" hspace="10" width="600"></a></td></tr>\n' % (fname,thumb,fname) | 285 s= '<tr><td><a href="%s"><img src="%s" title="Click to download a PDF of %s" hspace="10" width="600"></a></td></tr>\n' % (fname,thumb,fname) |
203 html.append(s) | 286 html.append(s) |
204 fhtml.append('<li><a href="%s">%s %s</a></li>' % (fname,fname,sfsize)) | 287 fhtml.append('<li><a href="%s">%s %s</a></li>' % (fname,fname,sfsize)) |
205 else: | 288 else: |
206 fhtml.append('<li><a href="%s">%s %s</a></li>' % (fname,fname,sfsize)) | 289 fhtml.append('<li><a href="%s">%s %s</a></li>' % (fname,fname,sfsize)) |
207 html.append('</table>\n') | 290 html.append('</table>\n') |
208 if len(fhtml) > 0: | 291 if len(fhtml) > 0: |
209 fhtml.insert(0,'<ul>') | 292 fhtml.insert(0,'<ul>') |
210 fhtml.append('</ul>') | 293 fhtml.append('</ul>') |
211 html += fhtml # add all non-pdf files to the end of the display | 294 html += fhtml # add all non-pdf files to the end of the display |
212 else: | 295 else: |
213 html.append('<h2>### Error - %s returned no files - please confirm that parameters are sane</h1>' % self.opts.interpreter) | 296 html.append('<h2>### Error - %s returned no files - please confirm that parameters are sane</h1>' % self.opts.interpreter) |
214 html.append('<h3>%s log follows below</h3><hr><pre>\n' % self.opts.interpreter) | 297 html.append('<h3>%s log follows below</h3><hr/><pre>\n' % self.opts.interpreter) |
215 rlog = open(self.tlog,'r').readlines() | 298 rlog = open(self.tlog,'r').readlines() |
216 html += rlog | 299 html += rlog |
217 html.append('%s CL = %s</br>\n' % (self.toolname,' '.join(sys.argv))) | 300 html.append('%s CL = %s<br/>\n' % (self.toolname,' '.join(sys.argv))) |
218 html.append('CL = %s</br>\n' % (' '.join(self.cl))) | 301 html.append('</pre>\n') |
219 html.append('</pre>\n') | 302 html.append(galhtmlattr % (progname,timenow())) |
220 html.append(galhtmlattr % (progname,timenow())) | 303 html.append(galhtmlpostfix) |
221 html.append(galhtmlpostfix) | 304 htmlf = file(self.opts.output_html,'w') |
222 htmlf = file(self.opts.output_html,'w') | 305 htmlf.write('\n'.join(html)) |
223 htmlf.write('\n'.join(html)) | 306 htmlf.write('\n') |
224 htmlf.write('\n') | 307 htmlf.close() |
225 htmlf.close() | 308 self.html = html |
226 self.html = html | |
227 | 309 |
228 | 310 |
229 def run(self): | 311 def run(self): |
230 """ | 312 """ |
231 """ | 313 """ |
232 if self.opts.output_dir or self.opts.makeTool: | 314 if self.opts.output_dir or self.opts.make_Tool: |
233 sto = open(self.tlog,'w') | 315 sto = open(self.tlog,'w') |
234 p = subprocess.Popen(' '.join(self.cl),shell=True,stdout=sto,stderr=sto,stdin=subprocess.PIPE,cwd=self.opts.output_dir) | 316 p = subprocess.Popen(' '.join(self.cl),shell=True,stdout=sto,stderr=sto,stdin=subprocess.PIPE,cwd=self.opts.output_dir) |
235 else: | 317 else: |
236 p = subprocess.Popen(' '.join(self.cl),shell=True,stdin=subprocess.PIPE) | 318 p = subprocess.Popen(' '.join(self.cl),shell=True,stdin=subprocess.PIPE) |
237 p.stdin.write(self.script) | 319 p.stdin.write(self.script) |
238 p.stdin.close() | 320 p.stdin.close() |
239 retval = p.wait() | 321 retval = p.wait() |
240 if self.opts.output_dir or self.opts.makeTool: | 322 if self.opts.make_HTML or self.opts.make_Tool: |
241 sto.close() | 323 sto.close() |
242 self.makeHtml() | 324 self.makeHtml() |
243 return retval | 325 return retval |
244 | 326 |
245 | 327 |
254 a('--script_path',default=None) | 336 a('--script_path',default=None) |
255 a('--tool_name',default=None) | 337 a('--tool_name',default=None) |
256 a('--interpreter',default=None) | 338 a('--interpreter',default=None) |
257 a('--output_dir',default=None) | 339 a('--output_dir',default=None) |
258 a('--output_html',default=None) | 340 a('--output_html',default=None) |
259 a('--input_tab',default='NONE') | 341 a('--input_tab',default=None) |
260 a('--output_tab',default='NONE') | 342 a('--output_tab',default=None) |
261 a('--user_email',default=None) | 343 a('--user_email',default=None) |
262 a('--bad_user',default=None) | 344 a('--bad_user',default=None) |
263 a('--makeTool',default=None) | 345 a('--make_Tool',default=None) |
346 a('--make_HTML',default=None) | |
347 a('--help_text',default=None) | |
348 a('--tool_desc',default=None) | |
264 opts, args = op.parse_args() | 349 opts, args = op.parse_args() |
265 assert not opts.bad_user,'%s is NOT authorized to use this tool. Please ask your friendly admin' % opts.bad_user | 350 assert not opts.bad_user,'%s is NOT authorized to use this tool. Please ask your friendly admin' % opts.bad_user |
266 assert opts.tool_name,'## Tool Factory expects a tool name - eg --tool_name=DESeq' | 351 assert opts.tool_name,'## Tool Factory expects a tool name - eg --tool_name=DESeq' |
267 assert opts.interpreter,'## Tool Factory wrapper expects an interpreter - eg --interpreter=Rscript' | 352 assert opts.interpreter,'## Tool Factory wrapper expects an interpreter - eg --interpreter=Rscript' |
268 assert os.path.isfile(opts.script_path),'## Tool Factory wrapper expects a script path - eg --script_path=foo.R' | 353 assert os.path.isfile(opts.script_path),'## Tool Factory wrapper expects a script path - eg --script_path=foo.R' |
270 try: | 355 try: |
271 os.makedirs(opts.output_dir) | 356 os.makedirs(opts.output_dir) |
272 except: | 357 except: |
273 pass | 358 pass |
274 r = ScriptRunner(opts) | 359 r = ScriptRunner(opts) |
275 if opts.makeTool: | 360 if opts.make_Tool: |
276 retcode = r.makeTooltar() | 361 retcode = r.makeTooltar() |
277 else: | 362 else: |
278 retcode = r.run() | 363 retcode = r.run() |
279 if retcode: | 364 if retcode: |
280 sys.exit(retcode) # indicate failure to job runner | 365 sys.exit(retcode) # indicate failure to job runner |