1
|
1 #!/home/jjjjia/.conda/envs/py36/bin/python
|
|
2
|
|
3 #$ -S /home/jjjjia/.conda/envs/py36/bin/python
|
|
4 #$ -V # Pass environment variables to the job
|
|
5 #$ -N CPO_pipeline # Replace with a more specific job name
|
|
6 #$ -wd /home/jjjjia/testCases # Use the current working dir
|
|
7 #$ -pe smp 8 # Parallel Environment (how many cores)
|
|
8 #$ -l h_vmem=11G # Memory (RAM) allocation *per core*
|
|
9 #$ -e ./logs/$JOB_ID.err
|
|
10 #$ -o ./logs/$JOB_ID.log
|
|
11 #$ -m ea
|
|
12 #$ -M bja20@sfu.ca
|
|
13
|
|
14 #~/scripts/pipeline.py -i BC11-Kpn005_S2 -f /data/jjjjia/R1/BC11-Kpn005_S2_L001_R1_001.fastq.gz -r /data/jjjjia/R2/BC11-Kpn005_S2_L001_R2_001.fastq.gz -o pipelineResultsQsub -e "Klebsiella pneumoniae"
|
|
15
|
|
16 import subprocess
|
|
17 import pandas
|
|
18 import optparse
|
|
19 import os
|
|
20 import datetime
|
|
21 import sys
|
|
22 import time
|
|
23 import urllib.request
|
|
24 import gzip
|
|
25 import collections
|
|
26 import json
|
|
27 import numpy
|
6
|
28 import ete3 as e
|
|
29
|
1
|
30
|
|
31 #parses some parameters
|
|
32 parser = optparse.OptionParser("Usage: %prog [options] arg1 arg2 ...")
|
|
33 parser.add_option("-t", "--tree", dest="treePath", type="string", default="./pipelineTest/tree.txt", help="identifier of the isolate")
|
|
34 parser.add_option("-d", "--distance", dest="distancePath", type="string", default="./pipelineTest/distance.tab", help="absolute file path forward read (R1)")
|
|
35 parser.add_option("-m", "--metadata", dest="metadataPath", type="string", default="./pipelineTest/metadata.tsv",help="absolute file path to reverse read (R2)")
|
|
36 (options,args) = parser.parse_args()
|
|
37 treePath = str(options.treePath).lstrip().rstrip()
|
|
38 distancePath = str(options.distancePath).lstrip().rstrip()
|
|
39 metadataPath = str(options.metadataPath).lstrip().rstrip()
|
|
40
|
|
41
|
|
42 #region result objects
|
|
43 #define some objects to store values from results
|
|
44 #//TODO this is not the proper way of get/set private object variables. every value has manually assigned defaults intead of specified in init(). Also, use property(def getVar, def setVar).
|
|
45 class workflowResult(object):
|
|
46 def __init__(self):
|
|
47 self.new = False
|
|
48 self.ID = ""
|
|
49 self.ExpectedSpecies = ""
|
|
50 self.MLSTSpecies = ""
|
|
51 self.SequenceType = ""
|
|
52 self.MLSTScheme = ""
|
|
53 self.CarbapenemResistanceGenes =""
|
|
54 self.OtherAMRGenes=""
|
|
55 self.TotalPlasmids = 0
|
|
56 self.plasmids = []
|
|
57 self.DefinitelyPlasmidContigs =""
|
|
58 self.LikelyPlasmidContigs=""
|
|
59 self.row = ""
|
|
60 class plasmidObj(object):
|
|
61 def __init__(self):
|
|
62 self.PlasmidsID = 0
|
|
63 self.Num_Contigs = 0
|
|
64 self.PlasmidLength = 0
|
|
65 self.PlasmidRepType = ""
|
|
66 self.PlasmidMobility = ""
|
|
67 self.NearestReference = ""
|
|
68
|
|
69 #endregion
|
|
70
|
|
71 #region useful functions
|
|
72 def read(path):
|
|
73 return [line.rstrip('\n') for line in open(path)]
|
|
74 def execute(command):
|
|
75 process = subprocess.Popen(command, shell=False, cwd=curDir, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
76
|
|
77 # Poll process for new output until finished
|
|
78 while True:
|
|
79 nextline = process.stdout.readline()
|
|
80 if nextline == '' and process.poll() is not None:
|
|
81 break
|
|
82 sys.stdout.write(nextline)
|
|
83 sys.stdout.flush()
|
|
84
|
|
85 output = process.communicate()[0]
|
|
86 exitCode = process.returncode
|
|
87
|
|
88 if (exitCode == 0):
|
|
89 return output
|
|
90 else:
|
|
91 raise subprocess.CalledProcessError(exitCode, command)
|
|
92 def httpGetFile(url, filepath=""):
|
|
93 if (filepath == ""):
|
|
94 return urllib.request.urlretrieve(url)
|
|
95 else:
|
|
96 urllib.request.urlretrieve(url, filepath)
|
|
97 return True
|
|
98 def gunzip(inputpath="", outputpath=""):
|
|
99 if (outputpath == ""):
|
|
100 with gzip.open(inputpath, 'rb') as f:
|
|
101 gzContent = f.read()
|
|
102 return gzContent
|
|
103 else:
|
|
104 with gzip.open(inputpath, 'rb') as f:
|
|
105 gzContent = f.read()
|
|
106 with open(outputpath, 'wb') as out:
|
|
107 out.write(gzContent)
|
|
108 return True
|
6
|
109 def addFace(name):
|
|
110 #if its the reference branch, populate the faces with column headers
|
|
111 face = e.faces.TextFace(name,fsize=10,tight_text=True)
|
|
112 face.border.margin = 5
|
|
113 face.margin_right = 5
|
|
114 face.margin_left = 5
|
|
115 return face
|
1
|
116 #endregion
|
|
117
|
|
118 #region functions to parse result files
|
|
119 def ParseWorkflowResults(pathToResult):
|
|
120 _worflowResult = {}
|
6
|
121 r = pandas.read_csv(pathToResult, delimiter='\t', header=0)
|
1
|
122 r = r.replace(numpy.nan, '', regex=True)
|
|
123 for i in range(len(r.index)):
|
|
124 _results = workflowResult()
|
6
|
125 if(str(r.loc[r.index[i], 'new']).lower() == "new"):
|
1
|
126 _results.new = True
|
|
127 else:
|
|
128 _results.new = False
|
6
|
129 _results.ID = str(r.loc[r.index[i], 'ID'])
|
|
130 _results.ExpectedSpecies = str(r.loc[r.index[i], 'Expected Species'])
|
|
131 _results.MLSTSpecies = str(r.loc[r.index[i], 'MLST Species'])
|
|
132 _results.SequenceType = str(r.loc[r.index[i], 'Sequence Type'])
|
|
133 _results.MLSTScheme = (str(r.loc[r.index[i], 'MLST Scheme']))
|
|
134 _results.CarbapenemResistanceGenes = (str(r.loc[r.index[i], 'Carbapenem Resistance Genes']))
|
|
135 _results.OtherAMRGenes = (str(r.loc[r.index[i], 'Other AMR Genes']))
|
|
136 _results.TotalPlasmids = int(r.loc[r.index[i], 'Total Plasmids'])
|
1
|
137 for j in range(0,_results.TotalPlasmids):
|
|
138 _plasmid = plasmidObj()
|
6
|
139 _plasmid.PlasmidsID =(((str(r.loc[r.index[i], 'Plasmids ID'])).split(";"))[j])
|
|
140 _plasmid.Num_Contigs = (((str(r.loc[r.index[i], 'Num_Contigs'])).split(";"))[j])
|
|
141 _plasmid.PlasmidLength = (((str(r.loc[r.index[i], 'Plasmid Length'])).split(";"))[j])
|
|
142 _plasmid.PlasmidRepType = (((str(r.loc[r.index[i], 'Plasmid RepType'])).split(";"))[j])
|
|
143 _plasmid.PlasmidMobility = ((str(r.loc[r.index[i], 'Plasmid Mobility'])).split(";"))[j]
|
|
144 _plasmid.NearestReference = ((str(r.loc[r.index[i], 'Nearest Reference'])).split(";"))[j]
|
1
|
145 _results.plasmids.append(_plasmid)
|
6
|
146 _results.DefinitelyPlasmidContigs = (str(r.loc[r.index[i], 'Definitely Plasmid Contigs']))
|
|
147 _results.LikelyPlasmidContigs = (str(r.loc[r.index[i], 'Likely Plasmid Contigs']))
|
1
|
148 _results.row = "\t".join(str(x) for x in r.ix[i].tolist())
|
|
149 _worflowResult[_results.ID] = _results
|
|
150 return _worflowResult
|
|
151
|
|
152 #endregion
|
|
153
|
|
154 def Main():
|
|
155 metadata = ParseWorkflowResults(metadataPath)
|
|
156 distance = read(distancePath)
|
|
157 treeFile = "".join(read(treePath))
|
|
158
|
6
|
159 distanceDict = {} #store the distance matrix as rowname:list<string>
|
1
|
160 for i in range(len(distance)):
|
|
161 temp = distance[i].split("\t")
|
|
162 distanceDict[temp[0]] = temp[1:]
|
|
163 #region step5: tree construction
|
6
|
164
|
|
165 '''
|
|
166 #region create detailed tree
|
|
167
|
|
168 plasmidCount = 0
|
|
169 for n in t.traverse():
|
|
170 if (n.is_leaf() and not n.name == "Reference"):
|
|
171 mData = metadata[n.name.replace(".fa","")]
|
|
172 face = faces.TextFace(mData.MLSTSpecies,fsize=10,tight_text=True)
|
|
173 face.border.margin = 5
|
|
174 face.margin_left = 10
|
|
175 face.margin_right = 10
|
|
176 n.add_face(face, 0, "aligned")
|
|
177 face = faces.TextFace(mData.SequenceType,fsize=10,tight_text=True)
|
|
178 face.border.margin = 5
|
|
179 face.margin_right = 10
|
|
180 n.add_face(face, 1, "aligned")
|
|
181 face = faces.TextFace(mData.CarbapenemResistanceGenes,fsize=10,tight_text=True)
|
|
182 face.border.margin = 5
|
|
183 face.margin_right = 10
|
|
184 n.add_face(face, 2, "aligned")
|
|
185 index = 3
|
|
186 if (mData.TotalPlasmids > plasmidCount):
|
|
187 plasmidCount = mData.TotalPlasmids
|
|
188 for i in range(0, mData.TotalPlasmids):
|
|
189 face = faces.TextFace(mData.plasmids[i].PlasmidRepType,fsize=10,tight_text=True)
|
|
190 face.border.margin = 5
|
|
191 face.margin_right = 10
|
|
192 n.add_face(face, index, "aligned")
|
|
193 index+=1
|
|
194 face = faces.TextFace(mData.plasmids[i].PlasmidMobility,fsize=10,tight_text=True)
|
|
195 face.border.margin = 5
|
|
196 face.margin_right = 10
|
|
197 n.add_face(face, index, "aligned")
|
|
198 index+=1
|
|
199
|
|
200 face = faces.TextFace("Species",fsize=10,tight_text=True)
|
|
201 face.border.margin = 5
|
|
202 face.margin_right = 10
|
|
203 face.margin_left = 10
|
|
204 (t&"Reference").add_face(face, 0, "aligned")
|
|
205 face = faces.TextFace("Sequence Type",fsize=10,tight_text=True)
|
|
206 face.border.margin = 5
|
|
207 face.margin_right = 10
|
|
208 (t&"Reference").add_face(face, 1, "aligned")
|
|
209 face = faces.TextFace("Carbapenamases",fsize=10,tight_text=True)
|
|
210 face.border.margin = 5
|
|
211 face.margin_right = 10
|
|
212 (t&"Reference").add_face(face, 2, "aligned")
|
|
213 index = 3
|
|
214 for i in range(0, plasmidCount):
|
|
215 face = faces.TextFace("plasmid " + str(i) + " replicons",fsize=10,tight_text=True)
|
|
216 face.border.margin = 5
|
|
217 face.margin_right = 10
|
|
218 (t&"Reference").add_face(face, index, "aligned")
|
|
219 index+=1
|
|
220 face = faces.TextFace("plasmid " + str(i) + " mobility",fsize=10,tight_text=True)
|
|
221 face.border.margin = 5
|
|
222 face.margin_right = 10
|
|
223 (t&"Reference").add_face(face, index, "aligned")
|
|
224 index+=1
|
|
225
|
|
226 t.render("./pipelineTest/tree.png", w=5000,units="mm", tree_style=ts)
|
|
227
|
|
228 #endregion
|
|
229 '''
|
|
230 #region create box tree
|
|
231 #region step5: tree construction
|
|
232 treeFile = "".join(read(treePath))
|
|
233 t = e.Tree(treeFile)
|
1
|
234 t.set_outgroup(t&"Reference")
|
|
235
|
6
|
236 #set the tree style
|
|
237 ts = e.TreeStyle()
|
|
238 ts.show_leaf_name = False
|
1
|
239 ts.show_branch_length = True
|
|
240 ts.scale = 2000 #pixel per branch length unit
|
|
241 ts.branch_vertical_margin = 15 #pixel between branches
|
6
|
242 style2 = e.NodeStyle()
|
1
|
243 style2["fgcolor"] = "#000000"
|
|
244 style2["shape"] = "circle"
|
|
245 style2["vt_line_color"] = "#0000aa"
|
|
246 style2["hz_line_color"] = "#0000aa"
|
|
247 style2["vt_line_width"] = 2
|
|
248 style2["hz_line_width"] = 2
|
|
249 style2["vt_line_type"] = 0 # 0 solid, 1 dashed, 2 dotted
|
|
250 style2["hz_line_type"] = 0
|
|
251 for n in t.traverse():
|
|
252 n.set_style(style2)
|
|
253
|
6
|
254 #find the plasmid origins
|
1
|
255 plasmidIncs = {}
|
|
256 for key in metadata:
|
|
257 for plasmid in metadata[key].plasmids:
|
|
258 for inc in plasmid.PlasmidRepType.split(","):
|
|
259 if (inc.lower().find("inc") > -1):
|
|
260 if not (inc in plasmidIncs):
|
|
261 plasmidIncs[inc] = [metadata[key].ID]
|
|
262 else:
|
|
263 if metadata[key].ID not in plasmidIncs[inc]:
|
|
264 plasmidIncs[inc].append(metadata[key].ID)
|
|
265 #plasmidIncs = sorted(plasmidIncs)
|
6
|
266 for n in t.traverse(): #loop through the nodes of a tree
|
1
|
267 if (n.is_leaf() and n.name == "Reference"):
|
6
|
268 #if its the reference branch, populate the faces with column headers
|
|
269 index = 0
|
|
270 (t&"Reference").add_face(addFace("SampleID"), index, "aligned")
|
|
271 index = index + 1
|
|
272 (t&"Reference").add_face(addFace("New?"), index, "aligned")
|
|
273 index = index + 1
|
1
|
274 for i in range(len(plasmidIncs)): #this loop adds the columns (aka the incs) to the reference node
|
6
|
275 (t&"Reference").add_face(addFace(list(plasmidIncs.keys())[i]), i + index, "aligned")
|
|
276 index = index + len(plasmidIncs)
|
|
277 (t&"Reference").add_face(addFace("MLSTScheme"), index, "aligned")
|
|
278 index = index + 1
|
|
279 (t&"Reference").add_face(addFace("Sequence Type"), index, "aligned")
|
|
280 index = index + 1
|
|
281 (t&"Reference").add_face(addFace("Carbapenamases"), index, "aligned")
|
|
282 index = index + 1
|
|
283 for i in range(len(distanceDict[list(distanceDict.keys())[0]])): #this loop adds the distance matrix
|
|
284 (t&"Reference").add_face(addFace(distanceDict[list(distanceDict.keys())[0]][i]), index + i, "aligned")
|
|
285 index = index + len(distanceDict[list(distanceDict.keys())[0]])
|
|
286 elif (n.is_leaf() and not n.name == "Reference"):
|
|
287 #not reference branches, populate with metadata
|
|
288 index = 0
|
|
289 mData = metadata[n.name.replace(".fa","")]
|
|
290 n.add_face(addFace(mData.ID), index, "aligned")
|
|
291 index = index + 1
|
|
292 if (metadata[n.name.replace(".fa","")].new == True): #new column
|
|
293 face = e.RectFace(30,30,"green","green") # TextFace("Y",fsize=10,tight_text=True)
|
1
|
294 face.border.margin = 5
|
|
295 face.margin_right = 5
|
|
296 face.margin_left = 5
|
|
297 face.vt_align = 1
|
|
298 face.ht_align = 1
|
6
|
299 n.add_face(face, index, "aligned")
|
|
300 index = index + 1
|
1
|
301 for incs in plasmidIncs: #this loop adds presence/absence to the sample nodes
|
|
302 if (n.name.replace(".fa","") in plasmidIncs[incs]):
|
6
|
303 face = e.RectFace(30,30,"black","black") # TextFace("Y",fsize=10,tight_text=True)
|
1
|
304 face.border.margin = 5
|
|
305 face.margin_right = 5
|
|
306 face.margin_left = 5
|
|
307 face.vt_align = 1
|
|
308 face.ht_align = 1
|
6
|
309 n.add_face(face, list(plasmidIncs.keys()).index(incs) + index, "aligned")
|
|
310 index = index + len(plasmidIncs)
|
|
311 n.add_face(addFace(mData.MLSTSpecies), index, "aligned")
|
|
312 index = index + 1
|
|
313 n.add_face(addFace(mData.SequenceType), index, "aligned")
|
|
314 index = index + 1
|
|
315 n.add_face(addFace(mData.CarbapenemResistanceGenes), index, "aligned")
|
|
316 index = index + 1
|
1
|
317 for i in range(len(distanceDict[list(distanceDict.keys())[0]])): #this loop adds distance matrix
|
6
|
318 n.add_face(addFace(list(distanceDict[n.name])[i]), index + i, "aligned")
|
|
319
|
|
320 t.render("./tree.png", w=5000,units="mm", tree_style=ts) #save it as a png. or an phyloxml
|
1
|
321
|
|
322 #endregion
|
|
323 #endregion
|
|
324
|
|
325
|
|
326 start = time.time()#time the analysis
|
|
327
|
|
328 #analysis time
|
|
329 Main()
|
|
330
|
|
331 end = time.time()
|
|
332 print("Finished!\nThe analysis used: " + str(end-start) + " seconds") |