Mercurial > repos > devteam > picard
view picard_wrapper.py @ 2:9227b8c3093b
Updated command line format per dev team standards.
author | devteam <devteam@galaxyproject.org> |
---|---|
date | Tue, 02 Apr 2013 09:42:36 -0400 |
parents | 1cd7f3b42609 |
children |
line wrap: on
line source
#!/usr/bin/env python """ Originally written by Kelly Vincent pretty output and additional picard wrappers by Ross Lazarus for rgenetics Runs all available wrapped Picard tools. usage: picard_wrapper.py [options] code Ross wrote licensed under the LGPL see http://www.gnu.org/copyleft/lesser.html """ import optparse, os, sys, subprocess, tempfile, shutil, time, logging 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://getgalaxy.org/" /> <title></title> <link rel="stylesheet" href="/static/style/base.css" type="text/css" /> </head> <body> <div class="document"> """ galhtmlattr = """Galaxy tool %s run at %s</b><br/>""" galhtmlpostfix = """</div></body></html>\n""" def stop_err( msg ): sys.stderr.write( '%s\n' % msg ) sys.exit() def timenow(): """return current time as a string """ return time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(time.time())) class PicardBase(): """ simple base class with some utilities for Picard adapted and merged with Kelly Vincent's code april 2011 Ross lots of changes... """ def __init__(self, opts=None,arg0=None): """ common stuff needed at init for a picard tool """ assert opts <> None, 'PicardBase needs opts at init' self.opts = opts if self.opts.outdir == None: self.opts.outdir = os.getcwd() # fixmate has no html file eg so use temp dir assert self.opts.outdir <> None,'## PicardBase needs a temp directory if no output directory passed in' self.picname = self.baseName(opts.jar) if self.picname.startswith('picard'): self.picname = opts.picard_cmd # special case for some tools like replaceheader? self.progname = self.baseName(arg0) self.version = '0.002' self.delme = [] # list of files to destroy self.title = opts.title self.inputfile = opts.input try: os.makedirs(opts.outdir) except: pass try: os.makedirs(opts.tmpdir) except: pass self.log_filename = os.path.join(self.opts.outdir,'%s.log' % self.picname) self.metricsOut = os.path.join(opts.outdir,'%s.metrics.txt' % self.picname) self.setLogging(logfname=self.log_filename) def baseName(self,name=None): return os.path.splitext(os.path.basename(name))[0] def setLogging(self,logfname="picard_wrapper.log"): """setup a logger """ logging.basicConfig(level=logging.INFO, filename=logfname, filemode='a') def readLarge(self,fname=None): """ read a potentially huge file. """ try: # get stderr, allowing for case where it's very large tmp = open( fname, 'rb' ) s = '' buffsize = 1048576 try: while True: more = tmp.read( buffsize ) if len(more) > 0: s += more else: break except OverflowError: pass tmp.close() except Exception, e: stop_err( 'Read Large Exception : %s' % str( e ) ) return s def runCL(self,cl=None,output_dir=None): """ construct and run a command line we have galaxy's temp path as opt.temp_dir so don't really need isolation sometimes stdout is needed as the output - ugly hacks to deal with potentially vast artifacts """ assert cl <> None, 'PicardBase runCL needs a command line as cl' if output_dir == None: output_dir = self.opts.outdir if type(cl) == type([]): cl = ' '.join(cl) fd,templog = tempfile.mkstemp(dir=output_dir,suffix='rgtempRun.txt') tlf = open(templog,'wb') fd,temperr = tempfile.mkstemp(dir=output_dir,suffix='rgtempErr.txt') tef = open(temperr,'wb') process = subprocess.Popen(cl, shell=True, stderr=tef, stdout=tlf, cwd=output_dir) rval = process.wait() tlf.close() tef.close() stderrs = self.readLarge(temperr) stdouts = self.readLarge(templog) if rval > 0: s = '## executing %s returned status %d and stderr: \n%s\n' % (cl,rval,stderrs) stdouts = '%s\n%s' % (stdouts,stderrs) else: s = '## executing %s returned status %d and nothing on stderr\n' % (cl,rval) logging.info(s) os.unlink(templog) # always os.unlink(temperr) # always return s, stdouts, rval # sometimes s is an output def runPic(self, jar, cl): """ cl should be everything after the jar file name in the command """ runme = ['java -Xmx%s' % self.opts.maxjheap] runme.append(" -Djava.io.tmpdir='%s' " % self.opts.tmpdir) runme.append('-jar %s' % jar) runme += cl s,stdouts,rval = self.runCL(cl=runme, output_dir=self.opts.outdir) return stdouts,rval def samToBam(self,infile=None,outdir=None): """ use samtools view to convert sam to bam """ fd,tempbam = tempfile.mkstemp(dir=outdir,suffix='rgutilsTemp.bam') cl = ['samtools view -h -b -S -o ',tempbam,infile] tlog,stdouts,rval = self.runCL(cl,outdir) return tlog,tempbam,rval def sortSam(self, infile=None,outfile=None,outdir=None): """ """ print '## sortSam got infile=%s,outfile=%s,outdir=%s' % (infile,outfile,outdir) cl = ['samtools sort',infile,outfile] tlog,stdouts,rval = self.runCL(cl,outdir) return tlog def cleanup(self): for fname in self.delme: try: os.unlink(fname) except: pass def prettyPicout(self,transpose,maxrows): """organize picard outpouts into a report html page """ res = [] try: r = open(self.metricsOut,'r').readlines() except: r = [] if len(r) > 0: res.append('<b>Picard on line resources</b><ul>\n') res.append('<li><a href="http://picard.sourceforge.net/index.shtml">Click here for Picard Documentation</a></li>\n') res.append('<li><a href="http://picard.sourceforge.net/picard-metric-definitions.shtml">Click here for Picard Metrics definitions</a></li></ul><hr/>\n') if transpose: res.append('<b>Picard output (transposed to make it easier to see)</b><hr/>\n') else: res.append('<b>Picard output</b><hr/>\n') res.append('<table cellpadding="3" >\n') dat = [] heads = [] lastr = len(r) - 1 # special case for estimate library complexity hist thist = False for i,row in enumerate(r): if row.strip() > '': srow = row.split('\t') if row.startswith('#'): heads.append(row.strip()) # want strings else: dat.append(srow) # want lists if row.startswith('## HISTOGRAM'): thist = True if len(heads) > 0: hres = ['<tr class="d%d"><td colspan="2">%s</td></tr>' % (i % 2,x) for i,x in enumerate(heads)] res += hres heads = [] if len(dat) > 0: if transpose and not thist: tdat = map(None,*dat) # transpose an arbitrary list of lists tdat = ['<tr class="d%d"><td>%s</td><td>%s </td></tr>\n' % ((i+len(heads)) % 2,x[0],x[1]) for i,x in enumerate(tdat)] else: tdat = ['\t'.join(x).strip() for x in dat] # back to strings :( tdat = ['<tr class="d%d"><td colspan="2">%s</td></tr>\n' % ((i+len(heads)) % 2,x) for i,x in enumerate(tdat)] res += tdat dat = [] res.append('</table>\n') return res def fixPicardOutputs(self,transpose,maxloglines): """ picard produces long hard to read tab header files make them available but present them transposed for readability """ logging.shutdown() self.cleanup() # remove temp files stored in delme rstyle="""<style type="text/css"> tr.d0 td {background-color: oldlace; color: black;} tr.d1 td {background-color: aliceblue; color: black;} </style>""" res = [rstyle,] res.append(galhtmlprefix % self.progname) res.append(galhtmlattr % (self.picname,timenow())) flist = [x for x in os.listdir(self.opts.outdir) if not x.startswith('.')] pdflist = [x for x in flist if os.path.splitext(x)[-1].lower() == '.pdf'] if len(pdflist) > 0: # assumes all pdfs come with thumbnail .jpgs for p in pdflist: pbase = os.path.splitext(p)[0] # removes .pdf imghref = '%s.jpg' % pbase mimghref = '%s-0.jpg' % pbase # multiple pages pdf -> multiple thumbnails without asking! if mimghref in flist: imghref=mimghref # only one for thumbnail...it's a multi page pdf res.append('<table cellpadding="10"><tr><td>\n') res.append('<a href="%s"><img src="%s" title="Click image preview for a print quality PDF version" hspace="10" align="middle"></a>\n' % (p,imghref)) res.append('</tr></td></table>\n') if len(flist) > 0: res.append('<b>The following output files were created (click the filename to view/download a copy):</b><hr/>') res.append('<table>\n') for i,f in enumerate(flist): fn = os.path.split(f)[-1] res.append('<tr><td><a href="%s">%s</a></td></tr>\n' % (fn,fn)) res.append('</table><p/>\n') pres = self.prettyPicout(transpose,maxloglines) if len(pres) > 0: res += pres l = open(self.log_filename,'r').readlines() llen = len(l) if llen > 0: res.append('<b>Picard Tool Run Log</b><hr/>\n') rlog = ['<pre>',] if llen > maxloglines: n = min(50,int(maxloglines/2)) rlog += l[:n] rlog.append('------------ ## %d rows deleted ## --------------\n' % (llen-maxloglines)) rlog += l[-n:] else: rlog += l rlog.append('</pre>') if llen > maxloglines: rlog.append('\n<b>## WARNING - %d log lines truncated - <a href="%s">%s</a> contains entire output</b>' % (llen - maxloglines,self.log_filename,self.log_filename)) res += rlog else: res.append("### Odd, Picard left no log file %s - must have really barfed badly?\n" % self.log_filename) res.append('<hr/>The freely available <a href="http://picard.sourceforge.net/command-line-overview.shtml">Picard software</a> \n') res.append( 'generated all outputs reported here running as a <a href="http://getgalaxy.org">Galaxy</a> tool') res.append(galhtmlpostfix) outf = open(self.opts.htmlout,'w') outf.write(''.join(res)) outf.write('\n') outf.close() def makePicInterval(self,inbed=None,outf=None): """ picard wants bait and target files to have the same header length as the incoming bam/sam a meaningful (ie accurate) representation will fail because of this - so this hack it would be far better to be able to supply the original bed untouched Additional checking added Ross Lazarus Dec 2011 to deal with two 'bug' reports on the list """ assert inbed <> None bed = open(inbed,'r').readlines() sbed = [x.split('\t') for x in bed] # lengths MUST be 5 lens = [len(x) for x in sbed] strands = [x[3] for x in sbed if not x[3] in ['+','-']] maxl = max(lens) minl = min(lens) e = [] if maxl <> minl: e.append("## Input error: Inconsistent field count in %s - please read the documentation on bait/target format requirements, fix and try again" % inbed) if maxl <> 5: e.append("## Input error: %d fields found in %s, 5 required - please read the warning and documentation on bait/target format requirements, fix and try again" % (maxl,inbed)) if len(strands) > 0: e.append("## Input error: Fourth column in %s is not the required strand (+ or -) - please read the warning and documentation on bait/target format requirements, fix and try again" % (inbed)) if len(e) > 0: # write to stderr and quit print >> sys.stderr, '\n'.join(e) sys.exit(1) thead = os.path.join(self.opts.outdir,'tempSamHead.txt') if self.opts.datatype == 'sam': cl = ['samtools view -H -S',self.opts.input,'>',thead] else: cl = ['samtools view -H',self.opts.input,'>',thead] self.runCL(cl=cl,output_dir=self.opts.outdir) head = open(thead,'r').readlines() s = '## got %d rows of header\n' % (len(head)) logging.info(s) o = open(outf,'w') o.write(''.join(head)) o.write(''.join(bed)) o.close() return outf def cleanSam(self, insam=None, newsam=None, picardErrors=[],outformat=None): """ interesting problem - if paired, must remove mate pair of errors too or we have a new set of errors after cleaning - missing mate pairs! Do the work of removing all the error sequences pysam is cool infile = pysam.Samfile( "-", "r" ) outfile = pysam.Samfile( "-", "w", template = infile ) for s in infile: outfile.write(s) errors from ValidateSameFile.jar look like WARNING: Record 32, Read name SRR006041.1202260, NM tag (nucleotide differences) is missing ERROR: Record 33, Read name SRR006041.1042721, Empty sequence dictionary. ERROR: Record 33, Read name SRR006041.1042721, RG ID on SAMRecord not found in header: SRR006041 """ assert os.path.isfile(insam), 'rgPicardValidate cleansam needs an input sam file - cannot find %s' % insam assert newsam <> None, 'rgPicardValidate cleansam needs an output new sam file path' removeNames = [x.split(',')[1].replace(' Read name ','') for x in picardErrors if len(x.split(',')) > 2] remDict = dict(zip(removeNames,range(len(removeNames)))) infile = pysam.Samfile(insam,'rb') info = 'found %d error sequences in picardErrors, %d unique' % (len(removeNames),len(remDict)) if len(removeNames) > 0: outfile = pysam.Samfile(newsam,'wb',template=infile) # template must be an open file i = 0 j = 0 for row in infile: dropme = remDict.get(row.qname,None) # keep if None if not dropme: outfile.write(row) j += 1 else: # discard i += 1 info = '%s\n%s' % (info, 'Discarded %d lines writing %d to %s from %s' % (i,j,newsam,insam)) outfile.close() infile.close() else: # we really want a nullop or a simple pointer copy infile.close() if newsam: shutil.copy(insam,newsam) logging.info(info) def __main__(): doFix = False # tools returning htmlfile don't need this doTranspose = True # default maxloglines = 100 # default #Parse Command Line op = optparse.OptionParser() # All tools op.add_option('-i', '--input', dest='input', help='Input SAM or BAM file' ) op.add_option('-e', '--inputext', default=None) op.add_option('-o', '--output', default=None) op.add_option('-n', '--title', default="Pick a Picard Tool") op.add_option('-t', '--htmlout', default=None) op.add_option('-d', '--outdir', default=None) op.add_option('-x', '--maxjheap', default='4g') op.add_option('-b', '--bisulphite', default='false') op.add_option('-s', '--sortorder', default='query') op.add_option('','--tmpdir', default='/tmp') op.add_option('-j','--jar',default='') op.add_option('','--picard-cmd',default=None) # Many tools op.add_option( '', '--output-format', dest='output_format', help='Output format' ) op.add_option( '', '--bai-file', dest='bai_file', help='The path to the index file for the input bam file' ) op.add_option( '', '--ref', dest='ref', help='Built-in reference with fasta and dict file', default=None ) # CreateSequenceDictionary op.add_option( '', '--ref-file', dest='ref_file', help='Fasta to use as reference', default=None ) op.add_option( '', '--species-name', dest='species_name', help='Species name to use in creating dict file from fasta file' ) op.add_option( '', '--build-name', dest='build_name', help='Name of genome assembly to use in creating dict file from fasta file' ) op.add_option( '', '--trunc-names', dest='trunc_names', help='Truncate sequence names at first whitespace from fasta file' ) # MarkDuplicates op.add_option( '', '--remdups', default='true', help='Remove duplicates from output file' ) op.add_option( '', '--optdupdist', default="100", help='Maximum pixels between two identical sequences in order to consider them optical duplicates.' ) # CollectInsertSizeMetrics op.add_option('', '--taillimit', default="0") op.add_option('', '--histwidth', default="0") op.add_option('', '--minpct', default="0.01") op.add_option('', '--malevel', default='') op.add_option('', '--deviations', default="0.0") # CollectAlignmentSummaryMetrics op.add_option('', '--maxinsert', default="20") op.add_option('', '--adaptors', default='') # FixMateInformation and validate # CollectGcBiasMetrics op.add_option('', '--windowsize', default='100') op.add_option('', '--mingenomefrac', default='0.00001') # AddOrReplaceReadGroups op.add_option( '', '--rg-opts', dest='rg_opts', help='Specify extra (optional) arguments with full, otherwise preSet' ) op.add_option( '', '--rg-lb', dest='rg_library', help='Read Group Library' ) op.add_option( '', '--rg-pl', dest='rg_platform', help='Read Group platform (e.g. illumina, solid)' ) op.add_option( '', '--rg-pu', dest='rg_plat_unit', help='Read Group platform unit (eg. run barcode) ' ) op.add_option( '', '--rg-sm', dest='rg_sample', help='Read Group sample name' ) op.add_option( '', '--rg-id', dest='rg_id', help='Read Group ID' ) op.add_option( '', '--rg-cn', dest='rg_seq_center', help='Read Group sequencing center name' ) op.add_option( '', '--rg-ds', dest='rg_desc', help='Read Group description' ) # ReorderSam op.add_option( '', '--allow-inc-dict-concord', dest='allow_inc_dict_concord', help='Allow incomplete dict concordance' ) op.add_option( '', '--allow-contig-len-discord', dest='allow_contig_len_discord', help='Allow contig length discordance' ) # ReplaceSamHeader op.add_option( '', '--header-file', dest='header_file', help='sam or bam file from which header will be read' ) op.add_option('','--assumesorted', default='true') op.add_option('','--readregex', default="[a-zA-Z0-9]+:[0-9]:([0-9]+):([0-9]+):([0-9]+).*") #estimatelibrarycomplexity op.add_option('','--minid', default="5") op.add_option('','--maxdiff', default="0.03") op.add_option('','--minmeanq', default="20") #hsmetrics op.add_option('','--baitbed', default=None) op.add_option('','--targetbed', default=None) #validate op.add_option('','--ignoreflags', action='append', type="string") op.add_option('','--maxerrors', default=None) op.add_option('','--datatype', default=None) op.add_option('','--bamout', default=None) op.add_option('','--samout', default=None) opts, args = op.parse_args() opts.sortme = opts.assumesorted == 'false' assert opts.input <> None # need to add # instance that does all the work pic = PicardBase(opts,sys.argv[0]) tmp_dir = opts.outdir haveTempout = False # we use this where sam output is an option rval = 0 stdouts = 'Not run yet' # set ref and dict files to use (create if necessary) ref_file_name = opts.ref if opts.ref_file <> None: csd = 'CreateSequenceDictionary' realjarpath = os.path.split(opts.jar)[0] jarpath = os.path.join(realjarpath,'%s.jar' % csd) # for refseq tmp_ref_fd, tmp_ref_name = tempfile.mkstemp( dir=opts.tmpdir , prefix = pic.picname) ref_file_name = '%s.fasta' % tmp_ref_name # build dict dict_file_name = '%s.dict' % tmp_ref_name os.symlink( opts.ref_file, ref_file_name ) cl = ['REFERENCE=%s' % ref_file_name] cl.append('OUTPUT=%s' % dict_file_name) cl.append('URI=%s' % os.path.basename( opts.ref_file )) cl.append('TRUNCATE_NAMES_AT_WHITESPACE=%s' % opts.trunc_names) if opts.species_name: cl.append('SPECIES=%s' % opts.species_name) if opts.build_name: cl.append('GENOME_ASSEMBLY=%s' % opts.build_name) pic.delme.append(dict_file_name) pic.delme.append(ref_file_name) pic.delme.append(tmp_ref_name) stdouts,rval = pic.runPic(jarpath, cl) # run relevant command(s) # define temporary output # if output is sam, it must have that extension, otherwise bam will be produced # specify sam or bam file with extension if opts.output_format == 'sam': suff = '.sam' else: suff = '' tmp_fd, tempout = tempfile.mkstemp( dir=opts.tmpdir, suffix=suff ) cl = ['VALIDATION_STRINGENCY=LENIENT',] if pic.picname == 'AddOrReplaceReadGroups': # sort order to match Galaxy's default cl.append('SORT_ORDER=coordinate') # input cl.append('INPUT=%s' % opts.input) # outputs cl.append('OUTPUT=%s' % tempout) # required read groups cl.append('RGLB="%s"' % opts.rg_library) cl.append('RGPL="%s"' % opts.rg_platform) cl.append('RGPU="%s"' % opts.rg_plat_unit) cl.append('RGSM="%s"' % opts.rg_sample) if opts.rg_id: cl.append('RGID="%s"' % opts.rg_id) # optional read groups if opts.rg_seq_center: cl.append('RGCN="%s"' % opts.rg_seq_center) if opts.rg_desc: cl.append('RGDS="%s"' % opts.rg_desc) stdouts,rval = pic.runPic(opts.jar, cl) haveTempout = True elif pic.picname == 'BamIndexStats': tmp_fd, tmp_name = tempfile.mkstemp( dir=tmp_dir ) tmp_bam_name = '%s.bam' % tmp_name tmp_bai_name = '%s.bai' % tmp_bam_name os.symlink( opts.input, tmp_bam_name ) os.symlink( opts.bai_file, tmp_bai_name ) cl.append('INPUT=%s' % ( tmp_bam_name )) pic.delme.append(tmp_bam_name) pic.delme.append(tmp_bai_name) pic.delme.append(tmp_name) stdouts,rval = pic.runPic( opts.jar, cl ) f = open(pic.metricsOut,'a') f.write(stdouts) # got this on stdout from runCl f.write('\n') f.close() doTranspose = False # but not transposed elif pic.picname == 'EstimateLibraryComplexity': cl.append('I=%s' % opts.input) cl.append('O=%s' % pic.metricsOut) if float(opts.minid) > 0: cl.append('MIN_IDENTICAL_BASES=%s' % opts.minid) if float(opts.maxdiff) > 0.0: cl.append('MAX_DIFF_RATE=%s' % opts.maxdiff) if float(opts.minmeanq) > 0: cl.append('MIN_MEAN_QUALITY=%s' % opts.minmeanq) if opts.readregex > '': cl.append('READ_NAME_REGEX="%s"' % opts.readregex) if float(opts.optdupdist) > 0: cl.append('OPTICAL_DUPLICATE_PIXEL_DISTANCE=%s' % opts.optdupdist) stdouts,rval = pic.runPic(opts.jar, cl) elif pic.picname == 'CollectAlignmentSummaryMetrics': # Why do we do this fakefasta thing? # Because we need NO fai to be available or picard barfs unless it matches the input data. # why? Dunno Seems to work without complaining if the .bai file is AWOL.... fakefasta = os.path.join(opts.outdir,'%s_fake.fasta' % os.path.basename(ref_file_name)) try: os.symlink(ref_file_name,fakefasta) except: s = '## unable to symlink %s to %s - different devices? Will shutil.copy' info = s shutil.copy(ref_file_name,fakefasta) pic.delme.append(fakefasta) cl.append('ASSUME_SORTED=true') adaptlist = opts.adaptors.split(',') adaptorseqs = ['ADAPTER_SEQUENCE=%s' % x for x in adaptlist] cl += adaptorseqs cl.append('IS_BISULFITE_SEQUENCED=%s' % opts.bisulphite) cl.append('MAX_INSERT_SIZE=%s' % opts.maxinsert) cl.append('OUTPUT=%s' % pic.metricsOut) cl.append('R=%s' % fakefasta) cl.append('TMP_DIR=%s' % opts.tmpdir) if not opts.assumesorted.lower() == 'true': # we need to sort input sortedfile = '%s.sorted' % os.path.basename(opts.input) if opts.datatype == 'sam': # need to work with a bam tlog,tempbam,trval = pic.samToBam(opts.input,opts.outdir) pic.delme.append(tempbam) try: tlog = pic.sortSam(tempbam,sortedfile,opts.outdir) except: print '## exception on sorting sam file %s' % opts.input else: # is already bam try: tlog = pic.sortSam(opts.input,sortedfile,opts.outdir) except : # bug - [bam_sort_core] not being ignored - TODO fixme print '## exception %s on sorting bam file %s' % (sys.exc_info()[0],opts.input) cl.append('INPUT=%s.bam' % os.path.abspath(os.path.join(opts.outdir,sortedfile))) pic.delme.append(os.path.join(opts.outdir,sortedfile)) else: cl.append('INPUT=%s' % os.path.abspath(opts.input)) stdouts,rval = pic.runPic(opts.jar, cl) elif pic.picname == 'CollectGcBiasMetrics': assert os.path.isfile(ref_file_name),'PicardGC needs a reference sequence - cannot read %s' % ref_file_name # sigh. Why do we do this fakefasta thing? Because we need NO fai to be available or picard barfs unless it has the same length as the input data. # why? Dunno fakefasta = os.path.join(opts.outdir,'%s_fake.fasta' % os.path.basename(ref_file_name)) try: os.symlink(ref_file_name,fakefasta) except: s = '## unable to symlink %s to %s - different devices? May need to replace with shutil.copy' info = s shutil.copy(ref_file_name,fakefasta) pic.delme.append(fakefasta) x = 'rgPicardGCBiasMetrics' pdfname = '%s.pdf' % x jpgname = '%s.jpg' % x tempout = os.path.join(opts.outdir,'rgPicardGCBiasMetrics.out') temppdf = os.path.join(opts.outdir,pdfname) cl.append('R=%s' % fakefasta) cl.append('WINDOW_SIZE=%s' % opts.windowsize) cl.append('MINIMUM_GENOME_FRACTION=%s' % opts.mingenomefrac) cl.append('INPUT=%s' % opts.input) cl.append('OUTPUT=%s' % tempout) cl.append('TMP_DIR=%s' % opts.tmpdir) cl.append('CHART_OUTPUT=%s' % temppdf) cl.append('SUMMARY_OUTPUT=%s' % pic.metricsOut) stdouts,rval = pic.runPic(opts.jar, cl) if os.path.isfile(temppdf): cl2 = ['convert','-resize x400',temppdf,os.path.join(opts.outdir,jpgname)] # make the jpg for fixPicardOutputs to find s,stdouts,rval = pic.runCL(cl=cl2,output_dir=opts.outdir) else: s='### runGC: Unable to find pdf %s - please check the log for the causal problem\n' % temppdf lf = open(pic.log_filename,'a') lf.write(s) lf.write('\n') lf.close() elif pic.picname == 'CollectInsertSizeMetrics': """ <command interpreter="python"> picard_wrapper.py -i "$input_file" -n "$out_prefix" --tmpdir "${__new_file_path__}" --deviations "$deviations" --histwidth "$histWidth" --minpct "$minPct" --malevel "$malevel" -j "${GALAXY_DATA_INDEX_DIR}/shared/jars/picard/CollectInsertSizeMetrics.jar" -d "$html_file.files_path" -t "$html_file" </command> """ isPDF = 'InsertSizeHist.pdf' pdfpath = os.path.join(opts.outdir,isPDF) histpdf = 'InsertSizeHist.pdf' cl.append('I=%s' % opts.input) cl.append('O=%s' % pic.metricsOut) cl.append('HISTOGRAM_FILE=%s' % histpdf) #if opts.taillimit <> '0': # this was deprecated although still mentioned in the docs at 1.56 # cl.append('TAIL_LIMIT=%s' % opts.taillimit) if opts.histwidth <> '0': cl.append('HISTOGRAM_WIDTH=%s' % opts.histwidth) if float( opts.minpct) > 0.0: cl.append('MINIMUM_PCT=%s' % opts.minpct) if float(opts.deviations) > 0.0: cl.append('DEVIATIONS=%s' % opts.deviations) if opts.malevel: malists = opts.malevel.split(',') malist = ['METRIC_ACCUMULATION_LEVEL=%s' % x for x in malists] cl += malist stdouts,rval = pic.runPic(opts.jar, cl) if os.path.exists(pdfpath): # automake thumbnail - will be added to html cl2 = ['mogrify', '-format jpg -resize x400 %s' % pdfpath] pic.runCL(cl=cl2,output_dir=opts.outdir) else: s = 'Unable to find expected pdf file %s<br/>\n' % pdfpath s += 'This <b>always happens if single ended data was provided</b> to this tool,\n' s += 'so please double check that your input data really is paired-end NGS data.<br/>\n' s += 'If your input was paired data this may be a bug worth reporting to the galaxy-bugs list\n<br/>' logging.info(s) if len(stdouts) > 0: logging.info(stdouts) elif pic.picname == 'MarkDuplicates': # assume sorted even if header says otherwise cl.append('ASSUME_SORTED=%s' % (opts.assumesorted)) # input cl.append('INPUT=%s' % opts.input) # outputs cl.append('OUTPUT=%s' % opts.output) cl.append('METRICS_FILE=%s' % pic.metricsOut ) # remove or mark duplicates cl.append('REMOVE_DUPLICATES=%s' % opts.remdups) # the regular expression to be used to parse reads in incoming SAM file cl.append('READ_NAME_REGEX="%s"' % opts.readregex) # maximum offset between two duplicate clusters cl.append('OPTICAL_DUPLICATE_PIXEL_DISTANCE=%s' % opts.optdupdist) stdouts,rval = pic.runPic(opts.jar, cl) elif pic.picname == 'FixMateInformation': cl.append('I=%s' % opts.input) cl.append('O=%s' % tempout) cl.append('SORT_ORDER=%s' % opts.sortorder) stdouts,rval = pic.runPic(opts.jar,cl) haveTempout = True elif pic.picname == 'ReorderSam': # input cl.append('INPUT=%s' % opts.input) # output cl.append('OUTPUT=%s' % tempout) # reference cl.append('REFERENCE=%s' % ref_file_name) # incomplete dict concordance if opts.allow_inc_dict_concord == 'true': cl.append('ALLOW_INCOMPLETE_DICT_CONCORDANCE=true') # contig length discordance if opts.allow_contig_len_discord == 'true': cl.append('ALLOW_CONTIG_LENGTH_DISCORDANCE=true') stdouts,rval = pic.runPic(opts.jar, cl) haveTempout = True elif pic.picname == 'ReplaceSamHeader': cl.append('INPUT=%s' % opts.input) cl.append('OUTPUT=%s' % tempout) cl.append('HEADER=%s' % opts.header_file) stdouts,rval = pic.runPic(opts.jar, cl) haveTempout = True elif pic.picname == 'CalculateHsMetrics': maxloglines = 100 baitfname = os.path.join(opts.outdir,'rgPicardHsMetrics.bait') targetfname = os.path.join(opts.outdir,'rgPicardHsMetrics.target') baitf = pic.makePicInterval(opts.baitbed,baitfname) if opts.targetbed == opts.baitbed: # same file sometimes targetf = baitf else: targetf = pic.makePicInterval(opts.targetbed,targetfname) cl.append('BAIT_INTERVALS=%s' % baitf) cl.append('TARGET_INTERVALS=%s' % targetf) cl.append('INPUT=%s' % os.path.abspath(opts.input)) cl.append('OUTPUT=%s' % pic.metricsOut) cl.append('TMP_DIR=%s' % opts.tmpdir) stdouts,rval = pic.runPic(opts.jar,cl) elif pic.picname == 'ValidateSamFile': import pysam doTranspose = False sortedfile = os.path.join(opts.outdir,'rgValidate.sorted') stf = open(pic.log_filename,'w') tlog = None if opts.datatype == 'sam': # need to work with a bam tlog,tempbam,rval = pic.samToBam(opts.input,opts.outdir) try: tlog = pic.sortSam(tempbam,sortedfile,opts.outdir) except: print '## exception on sorting sam file %s' % opts.input else: # is already bam try: tlog = pic.sortSam(opts.input,sortedfile,opts.outdir) except: # bug - [bam_sort_core] not being ignored - TODO fixme print '## exception on sorting bam file %s' % opts.input if tlog: print '##tlog=',tlog stf.write(tlog) stf.write('\n') sortedfile = '%s.bam' % sortedfile # samtools does that cl.append('O=%s' % pic.metricsOut) cl.append('TMP_DIR=%s' % opts.tmpdir) cl.append('I=%s' % sortedfile) opts.maxerrors = '99999999' cl.append('MAX_OUTPUT=%s' % opts.maxerrors) if opts.ignoreflags[0] <> 'None': # picard error values to ignore igs = ['IGNORE=%s' % x for x in opts.ignoreflags if x <> 'None'] cl.append(' '.join(igs)) if opts.bisulphite.lower() <> 'false': cl.append('IS_BISULFITE_SEQUENCED=true') if opts.ref <> None or opts.ref_file <> None: cl.append('R=%s' % ref_file_name) stdouts,rval = pic.runPic(opts.jar,cl) if opts.datatype == 'sam': pic.delme.append(tempbam) newsam = opts.output outformat = 'bam' pe = open(pic.metricsOut,'r').readlines() pic.cleanSam(insam=sortedfile, newsam=newsam, picardErrors=pe,outformat=outformat) pic.delme.append(sortedfile) # not wanted stf.close() pic.cleanup() else: print >> sys.stderr,'picard.py got an unknown tool name - %s' % pic.picname sys.exit(1) if haveTempout: # Some Picard tools produced a potentially intermediate bam file. # Either just move to final location or create sam if os.path.exists(tempout): shutil.move(tempout, os.path.abspath(opts.output)) if opts.htmlout <> None or doFix: # return a pretty html page pic.fixPicardOutputs(transpose=doTranspose,maxloglines=maxloglines) if rval <> 0: print >> sys.stderr, '## exit code=%d; stdout=%s' % (rval,stdouts) # signal failure if __name__=="__main__": __main__()