varvamp 1.1.3__py3-none-any.whl → 1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- varvamp/__init__.py +1 -1
- varvamp/command.py +60 -33
- varvamp/scripts/blast.py +36 -66
- varvamp/scripts/default_config.py +1 -2
- varvamp/scripts/logging.py +12 -10
- varvamp/scripts/primers.py +2 -2
- varvamp/scripts/qpcr.py +38 -36
- varvamp/scripts/reporting.py +194 -156
- varvamp/scripts/scheme.py +115 -107
- {varvamp-1.1.3.dist-info → varvamp-1.2.1.dist-info}/METADATA +4 -6
- varvamp-1.2.1.dist-info/RECORD +21 -0
- {varvamp-1.1.3.dist-info → varvamp-1.2.1.dist-info}/WHEEL +1 -1
- varvamp-1.1.3.dist-info/RECORD +0 -21
- {varvamp-1.1.3.dist-info → varvamp-1.2.1.dist-info}/entry_points.txt +0 -0
- {varvamp-1.1.3.dist-info → varvamp-1.2.1.dist-info}/top_level.txt +0 -0
varvamp/scripts/reporting.py
CHANGED
|
@@ -16,13 +16,14 @@ from matplotlib.backends.backend_pdf import PdfPages
|
|
|
16
16
|
# varVAMP
|
|
17
17
|
from varvamp.scripts import primers
|
|
18
18
|
from varvamp.scripts import config
|
|
19
|
+
from varvamp.scripts import logging
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
def write_fasta(path, seq_id, seq):
|
|
22
|
+
def write_fasta(path, file_name, seq_id, seq):
|
|
22
23
|
"""
|
|
23
24
|
write fasta files
|
|
24
25
|
"""
|
|
25
|
-
name = f"{
|
|
26
|
+
name = f"{file_name}.fasta"
|
|
26
27
|
out = os.path.join(path, name)
|
|
27
28
|
with open(out, 'w') as o:
|
|
28
29
|
print(f">{seq_id}\n{seq}", file=o)
|
|
@@ -39,7 +40,7 @@ def write_alignment(path, alignment):
|
|
|
39
40
|
print(f">{seq[0]}\n{seq[1]}", file=o)
|
|
40
41
|
|
|
41
42
|
|
|
42
|
-
def write_regions_to_bed(primer_regions, path, mode=None):
|
|
43
|
+
def write_regions_to_bed(primer_regions, scheme_name, path, mode=None):
|
|
43
44
|
"""
|
|
44
45
|
write primer regions as bed file
|
|
45
46
|
"""
|
|
@@ -48,39 +49,43 @@ def write_regions_to_bed(primer_regions, path, mode=None):
|
|
|
48
49
|
outfile = f"{path}probe_regions.bed"
|
|
49
50
|
else:
|
|
50
51
|
outfile = f"{path}primer_regions.bed"
|
|
51
|
-
counter = 0
|
|
52
52
|
|
|
53
53
|
with open(outfile, 'w') as o:
|
|
54
|
-
for region in primer_regions:
|
|
54
|
+
for counter, region in enumerate(primer_regions):
|
|
55
55
|
print(
|
|
56
|
-
"
|
|
56
|
+
f"{scheme_name}_consensus",
|
|
57
57
|
region[0],
|
|
58
58
|
region[1],
|
|
59
59
|
"REGION_"+str(counter),
|
|
60
60
|
sep="\t",
|
|
61
61
|
file=o
|
|
62
62
|
)
|
|
63
|
-
counter += 1
|
|
64
63
|
|
|
65
64
|
|
|
66
|
-
def write_primers_to_bed(outfile, primer_name, primer_properties, direction):
|
|
65
|
+
def write_primers_to_bed(outfile, scheme_name, primer_name, primer_properties, numeric_value, direction, sequence=None):
|
|
67
66
|
"""
|
|
68
67
|
write primers as bed file
|
|
69
68
|
"""
|
|
70
69
|
with open(outfile, 'a') as o:
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
# write header for primer bed
|
|
71
|
+
if os.path.getsize(outfile) == 0 and sequence is not None:
|
|
72
|
+
print("#chrom\tchromStart\tchromEnd\tprimer-name\tpool\tstrand\tprimer-sequence", file=o)
|
|
73
|
+
data = [f"{scheme_name}_consensus",
|
|
73
74
|
primer_properties[1], # start
|
|
74
75
|
primer_properties[2], # stop
|
|
75
76
|
primer_name,
|
|
76
|
-
|
|
77
|
-
direction
|
|
77
|
+
numeric_value, # can be pool or score
|
|
78
|
+
direction]
|
|
79
|
+
if sequence is not None:
|
|
80
|
+
data.append(sequence)
|
|
81
|
+
print(
|
|
82
|
+
*data,
|
|
78
83
|
sep="\t",
|
|
79
84
|
file=o
|
|
80
85
|
)
|
|
81
86
|
|
|
82
87
|
|
|
83
|
-
def write_all_primers(path, all_primers):
|
|
88
|
+
def write_all_primers(path, scheme_name, all_primers):
|
|
84
89
|
"""
|
|
85
90
|
write all primers that varVAMP designed as bed file
|
|
86
91
|
"""
|
|
@@ -88,7 +93,7 @@ def write_all_primers(path, all_primers):
|
|
|
88
93
|
|
|
89
94
|
for direction in all_primers:
|
|
90
95
|
for primer in all_primers[direction]:
|
|
91
|
-
write_primers_to_bed(outfile, primer, all_primers[direction][primer], direction)
|
|
96
|
+
write_primers_to_bed(outfile, scheme_name, primer, all_primers[direction][primer], round(all_primers[direction][primer][3], 2), direction)
|
|
92
97
|
|
|
93
98
|
|
|
94
99
|
def get_permutations(seq):
|
|
@@ -121,7 +126,7 @@ def calc_mean_stats(permutations):
|
|
|
121
126
|
return round(gc/len(permutations), 1), round(temp/len(permutations), 1)
|
|
122
127
|
|
|
123
128
|
|
|
124
|
-
def write_qpcr_to_files(path, final_schemes, ambiguous_consensus):
|
|
129
|
+
def write_qpcr_to_files(path, final_schemes, ambiguous_consensus, scheme_name, log_file):
|
|
125
130
|
"""
|
|
126
131
|
write all relevant bed files and tsv file for the qPCR design
|
|
127
132
|
"""
|
|
@@ -134,32 +139,43 @@ def write_qpcr_to_files(path, final_schemes, ambiguous_consensus):
|
|
|
134
139
|
|
|
135
140
|
with open(tsv_file, "w") as tsv, open(tsv_file_2, "w") as tsv2, open(amplicon_bed_file, "w") as bed, open(primer_fasta_file, "w") as fasta:
|
|
136
141
|
print(
|
|
137
|
-
"qpcr_scheme\toligo_type\tstart\tstop\tseq\tsize\tgc_best\ttemp_best\tmean_gc\tmean_temp\tpenalty",
|
|
142
|
+
"qpcr_scheme\toligo_type\tstart\tstop\tseq\tsize\tgc_best\ttemp_best\tmean_gc\tmean_temp\tpenalty\toff_target_amplicons",
|
|
138
143
|
file=tsv2
|
|
139
144
|
)
|
|
140
145
|
print(
|
|
141
|
-
"qpcr_scheme\tpenalty\tdeltaG\tlength\tstart\tstop\tseq",
|
|
146
|
+
"qpcr_scheme\toff_target_amplicons\tpenalty\tdeltaG\tlength\tstart\tstop\tseq",
|
|
142
147
|
file=tsv
|
|
143
148
|
)
|
|
144
|
-
for
|
|
149
|
+
for n, amp in enumerate(final_schemes):
|
|
150
|
+
amp_name = f"{scheme_name}_{n}"
|
|
145
151
|
# write bed amplicon file
|
|
146
152
|
print(
|
|
147
|
-
"
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
round(
|
|
153
|
+
f"{scheme_name}_consensus",
|
|
154
|
+
amp["LEFT"][1],
|
|
155
|
+
amp["RIGHT"][2],
|
|
156
|
+
amp_name,
|
|
157
|
+
round(amp["penalty"], 1),
|
|
158
|
+
".",
|
|
152
159
|
sep="\t",
|
|
153
160
|
file=bed
|
|
154
161
|
)
|
|
155
162
|
# write tsv
|
|
156
|
-
amplicon_start =
|
|
157
|
-
amplicon_stop =
|
|
163
|
+
amplicon_start = amp["LEFT"][1]
|
|
164
|
+
amplicon_stop = amp["RIGHT"][2]
|
|
165
|
+
if "off_targets" in amp:
|
|
166
|
+
if amp["off_targets"]:
|
|
167
|
+
amplicon_has_off_target = "Yes"
|
|
168
|
+
write_BLAST_warning(amp_name, log_file)
|
|
169
|
+
else:
|
|
170
|
+
amplicon_has_off_target = "No"
|
|
171
|
+
else:
|
|
172
|
+
amplicon_has_off_target = "n.d."
|
|
158
173
|
amplicon_seq = ambiguous_consensus[amplicon_start:amplicon_stop]
|
|
159
174
|
print(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
175
|
+
amp_name,
|
|
176
|
+
amplicon_has_off_target,
|
|
177
|
+
round(amp["penalty"], 1),
|
|
178
|
+
amp["deltaG"],
|
|
163
179
|
len(amplicon_seq),
|
|
164
180
|
amplicon_start + 1,
|
|
165
181
|
amplicon_stop,
|
|
@@ -168,11 +184,9 @@ def write_qpcr_to_files(path, final_schemes, ambiguous_consensus):
|
|
|
168
184
|
file=tsv
|
|
169
185
|
)
|
|
170
186
|
# write tsv2
|
|
171
|
-
for oligo_type in
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
seq = ambiguous_consensus[final_schemes[scheme][oligo_type][1]:final_schemes[scheme][oligo_type][2]]
|
|
175
|
-
if oligo_type == "RIGHT" or all([oligo_type == "PROBE", final_schemes[scheme]["PROBE"][5] == "-"]):
|
|
187
|
+
for oligo_type in ["LEFT", "PROBE", "RIGHT"]:
|
|
188
|
+
seq = ambiguous_consensus[amp[oligo_type][1]:amp[oligo_type][2]]
|
|
189
|
+
if oligo_type == "RIGHT" or (oligo_type == "PROBE" and amp["PROBE"][5] == "-"):
|
|
176
190
|
seq = primers.rev_complement(seq)
|
|
177
191
|
direction = "-"
|
|
178
192
|
else:
|
|
@@ -180,34 +194,39 @@ def write_qpcr_to_files(path, final_schemes, ambiguous_consensus):
|
|
|
180
194
|
|
|
181
195
|
permutations = get_permutations(seq)
|
|
182
196
|
gc, temp = calc_mean_stats(permutations)
|
|
197
|
+
primer_name = f"{amp_name}_{oligo_type}"
|
|
183
198
|
|
|
184
199
|
print(
|
|
185
|
-
|
|
200
|
+
amp_name,
|
|
186
201
|
oligo_type,
|
|
187
|
-
|
|
188
|
-
|
|
202
|
+
amp[oligo_type][1] + 1,
|
|
203
|
+
amp[oligo_type][2],
|
|
189
204
|
seq.upper(),
|
|
190
205
|
len(seq),
|
|
191
|
-
round(primers.calc_gc(
|
|
192
|
-
round(primers.calc_temp(
|
|
206
|
+
round(primers.calc_gc(amp[oligo_type][0]), 1),
|
|
207
|
+
round(primers.calc_temp(amp[oligo_type][0]), 1),
|
|
193
208
|
gc,
|
|
194
209
|
temp,
|
|
195
|
-
round(
|
|
210
|
+
round(amp[oligo_type][3], 1),
|
|
211
|
+
amplicon_has_off_target,
|
|
196
212
|
sep="\t",
|
|
197
213
|
file=tsv2
|
|
198
214
|
)
|
|
199
215
|
# write primer bed file
|
|
200
216
|
write_primers_to_bed(
|
|
201
217
|
primer_bed_file,
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
218
|
+
scheme_name,
|
|
219
|
+
primer_name,
|
|
220
|
+
amp[oligo_type],
|
|
221
|
+
round(amp[oligo_type][3], 2),
|
|
222
|
+
direction,
|
|
223
|
+
seq.upper()
|
|
205
224
|
)
|
|
206
225
|
# write fasta
|
|
207
|
-
print(f">{
|
|
226
|
+
print(f">{primer_name}\n{seq.upper()}", file=fasta)
|
|
208
227
|
|
|
209
228
|
|
|
210
|
-
def write_scheme_to_files(path, amplicon_scheme, ambiguous_consensus, mode):
|
|
229
|
+
def write_scheme_to_files(path, amplicon_scheme, ambiguous_consensus, scheme_name, mode, log_file):
|
|
211
230
|
"""
|
|
212
231
|
write all relevant bed files and a tsv file with all primer stats
|
|
213
232
|
"""
|
|
@@ -216,60 +235,65 @@ def write_scheme_to_files(path, amplicon_scheme, ambiguous_consensus, mode):
|
|
|
216
235
|
amplicon_bed_file = os.path.join(path, "amplicons.bed")
|
|
217
236
|
tabular_file = os.path.join(path, "primer_to_amplicon_assignment.tabular")
|
|
218
237
|
|
|
219
|
-
counter = 0
|
|
220
|
-
|
|
221
238
|
# open files to write
|
|
222
239
|
with open(tsv_file, "w") as tsv, open(amplicon_bed_file, "w") as bed, open(tabular_file, "w") as tabular:
|
|
223
240
|
# write header for primer tsv
|
|
224
241
|
print(
|
|
225
|
-
"amlicon_name\tamplicon_length\tprimer_name\
|
|
242
|
+
"amlicon_name\tamplicon_length\tprimer_name\tprimer_name_all_primers\tpool\tstart\tstop\tseq\tsize\tgc_best\ttemp_best\tmean_gc\tmean_temp\tpenalty\toff_target_amplicons",
|
|
226
243
|
file=tsv
|
|
227
244
|
)
|
|
228
|
-
|
|
229
|
-
|
|
245
|
+
amplicon_bed_records = []
|
|
246
|
+
primer_bed_records = []
|
|
247
|
+
primer_assignment_records = []
|
|
248
|
+
pools = {amp.get("pool", 0) for amp in amplicon_scheme}
|
|
249
|
+
for pool in pools:
|
|
230
250
|
if mode == "single":
|
|
231
251
|
primer_fasta_file = os.path.join(path, "primers.fasta")
|
|
232
252
|
else:
|
|
233
|
-
primer_fasta_file = os.path.join(path, f"primers_pool_{pool}.fasta")
|
|
253
|
+
primer_fasta_file = os.path.join(path, f"primers_pool_{pool+1}.fasta")
|
|
234
254
|
with open(primer_fasta_file, "w") as primer_fasta:
|
|
235
|
-
for amp in amplicon_scheme[pool]:
|
|
255
|
+
for counter, amp in enumerate(amplicon_scheme[pool::len(pools)]):
|
|
236
256
|
# give a new amplicon name
|
|
237
|
-
|
|
238
|
-
|
|
257
|
+
amplicon_index = counter*len(pools) + pool
|
|
258
|
+
amp_name = f"{scheme_name}_{amplicon_index}"
|
|
239
259
|
# get left and right primers and their names
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
260
|
+
amp_length = amp["RIGHT"][2] - amp["LEFT"][1]
|
|
261
|
+
if "off_targets" in amp:
|
|
262
|
+
if amp["off_targets"]:
|
|
263
|
+
amplicon_has_off_target = "Yes"
|
|
264
|
+
write_BLAST_warning(amp_name, log_file)
|
|
265
|
+
else:
|
|
266
|
+
amplicon_has_off_target = "No"
|
|
267
|
+
else:
|
|
268
|
+
amplicon_has_off_target = "n.d."
|
|
244
269
|
# write amplicon bed
|
|
245
270
|
if mode == "tiled":
|
|
246
|
-
bed_score = pool
|
|
271
|
+
bed_score = pool+1
|
|
247
272
|
elif mode == "single":
|
|
248
|
-
bed_score = round(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
file=bed
|
|
273
|
+
bed_score = round(amp["LEFT"][3] + amp["RIGHT"][3], 1)
|
|
274
|
+
amplicon_bed_records.append(
|
|
275
|
+
(
|
|
276
|
+
amp["LEFT"][1],
|
|
277
|
+
amp["RIGHT"][2],
|
|
278
|
+
amp_name,
|
|
279
|
+
bed_score
|
|
280
|
+
)
|
|
257
281
|
)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
282
|
+
primer_assignment_records.append(
|
|
283
|
+
(
|
|
284
|
+
# will need amplicon_index for sorting
|
|
285
|
+
amplicon_index,
|
|
286
|
+
(f"{amp_name}_LEFT", f"{amp_name}_RIGHT")
|
|
287
|
+
)
|
|
264
288
|
)
|
|
265
289
|
# write primer tsv and primer bed
|
|
266
|
-
for direction, primer in [("+",
|
|
267
|
-
seq = ambiguous_consensus[primer[1]
|
|
290
|
+
for direction, primer in [("+", amp["LEFT"]), ("-", amp["RIGHT"])]:
|
|
291
|
+
seq = ambiguous_consensus[primer[1]:primer[2]]
|
|
268
292
|
if direction == "-":
|
|
269
293
|
seq = primers.rev_complement(seq)
|
|
270
|
-
primer_name = f"{
|
|
294
|
+
primer_name = f"{amp_name}_RIGHT"
|
|
271
295
|
else:
|
|
272
|
-
primer_name = f"{
|
|
296
|
+
primer_name = f"{amp_name}_LEFT"
|
|
273
297
|
# write primers to fasta pool file
|
|
274
298
|
print(f">{primer_name}\n{seq.upper()}", file=primer_fasta)
|
|
275
299
|
# calc primer parameters for all permutations
|
|
@@ -277,30 +301,54 @@ def write_scheme_to_files(path, amplicon_scheme, ambiguous_consensus, mode):
|
|
|
277
301
|
gc, temp = calc_mean_stats(permutations)
|
|
278
302
|
# write tsv file
|
|
279
303
|
print(
|
|
280
|
-
|
|
304
|
+
amp_name,
|
|
281
305
|
amp_length,
|
|
282
306
|
primer_name,
|
|
283
|
-
primer[
|
|
284
|
-
pool,
|
|
285
|
-
primer[1]
|
|
286
|
-
primer[
|
|
307
|
+
primer[-1],
|
|
308
|
+
pool+1,
|
|
309
|
+
primer[1] + 1,
|
|
310
|
+
primer[2],
|
|
287
311
|
seq.upper(),
|
|
288
|
-
len(primer[
|
|
289
|
-
round(primers.calc_gc(primer[
|
|
290
|
-
round(primers.calc_temp(primer[
|
|
312
|
+
len(primer[0]),
|
|
313
|
+
round(primers.calc_gc(primer[0]), 1),
|
|
314
|
+
round(primers.calc_temp(primer[0]), 1),
|
|
291
315
|
gc,
|
|
292
316
|
temp,
|
|
293
|
-
round(primer[
|
|
317
|
+
round(primer[3], 1),
|
|
318
|
+
amplicon_has_off_target,
|
|
294
319
|
sep="\t",
|
|
295
320
|
file=tsv
|
|
296
321
|
)
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
322
|
+
primer_bed_records.append(
|
|
323
|
+
(
|
|
324
|
+
# will need amplicon_index for sorting
|
|
325
|
+
amplicon_index,
|
|
326
|
+
(primer_name, primer, pool+1, direction, seq.upper())
|
|
327
|
+
)
|
|
303
328
|
)
|
|
329
|
+
# write amplicon bed with amplicons sorted by start position
|
|
330
|
+
for record in sorted(amplicon_bed_records, key=lambda x: x[0]):
|
|
331
|
+
print(
|
|
332
|
+
f"{scheme_name}_consensus",
|
|
333
|
+
*record,
|
|
334
|
+
".",
|
|
335
|
+
sep="\t",
|
|
336
|
+
file=bed
|
|
337
|
+
)
|
|
338
|
+
# use sorting by amplicon index for primer assignment file
|
|
339
|
+
for record in sorted(primer_assignment_records):
|
|
340
|
+
print(
|
|
341
|
+
*record[1],
|
|
342
|
+
sep="\t",
|
|
343
|
+
file=tabular
|
|
344
|
+
)
|
|
345
|
+
# same for primer bed
|
|
346
|
+
for record in sorted(primer_bed_records):
|
|
347
|
+
write_primers_to_bed(
|
|
348
|
+
primer_bed_file,
|
|
349
|
+
scheme_name,
|
|
350
|
+
*record[1]
|
|
351
|
+
)
|
|
304
352
|
|
|
305
353
|
|
|
306
354
|
def write_dimers(path, primer_dimers):
|
|
@@ -313,12 +361,12 @@ def write_dimers(path, primer_dimers):
|
|
|
313
361
|
"pool\tprimer_name_1\tprimer_name_2\tdimer melting temp",
|
|
314
362
|
file=tsv
|
|
315
363
|
)
|
|
316
|
-
for
|
|
364
|
+
for pool, primer1, primer2 in primer_dimers:
|
|
317
365
|
print(
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
round(primers.calc_dimer(
|
|
366
|
+
pool+1,
|
|
367
|
+
primer1[1],
|
|
368
|
+
primer2[1],
|
|
369
|
+
round(primers.calc_dimer(primer1[2][0], primer2[2][0]).tm, 1),
|
|
322
370
|
sep="\t",
|
|
323
371
|
file=tsv
|
|
324
372
|
)
|
|
@@ -368,7 +416,7 @@ def alignment_entropy(alignment_cleaned):
|
|
|
368
416
|
return entropy_df
|
|
369
417
|
|
|
370
418
|
|
|
371
|
-
def entropy_subplot(ax, alignment_cleaned):
|
|
419
|
+
def entropy_subplot(ax, alignment_cleaned, scheme_name):
|
|
372
420
|
"""
|
|
373
421
|
creates the entropy subplot
|
|
374
422
|
"""
|
|
@@ -380,7 +428,7 @@ def entropy_subplot(ax, alignment_cleaned):
|
|
|
380
428
|
ax[0].set_ylim((0, 1))
|
|
381
429
|
ax[0].set_xlim(0, max(entropy_df["position"]))
|
|
382
430
|
ax[0].set_ylabel("normalized Shannon's entropy")
|
|
383
|
-
ax[0].set_title("
|
|
431
|
+
ax[0].set_title(f"{scheme_name} amplicon design")
|
|
384
432
|
ax[0].spines['top'].set_visible(False)
|
|
385
433
|
ax[0].spines['right'].set_visible(False)
|
|
386
434
|
|
|
@@ -418,26 +466,23 @@ def amplicon_subplot(ax, amplicon_scheme):
|
|
|
418
466
|
"""
|
|
419
467
|
creates the amplicon subplot
|
|
420
468
|
"""
|
|
421
|
-
counter
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
ax[1].hlines(position_amp, left[1], left[2], linewidth=5, color="red")
|
|
439
|
-
ax[1].hlines(position_amp, right[1], right[2], linewidth=5, color="red")
|
|
440
|
-
counter += 1
|
|
469
|
+
for counter, amp in enumerate(amplicon_scheme):
|
|
470
|
+
pool = amp.get("pool", 0)
|
|
471
|
+
if pool == 0:
|
|
472
|
+
position_amp = 0.7
|
|
473
|
+
position_text = 0.6
|
|
474
|
+
elif pool == 1:
|
|
475
|
+
position_amp = 0.6
|
|
476
|
+
position_text = 0.65
|
|
477
|
+
left = amp["LEFT"]
|
|
478
|
+
right = amp["RIGHT"]
|
|
479
|
+
# amplicons
|
|
480
|
+
ax[1].hlines(position_amp, left[1], right[2], linewidth=5)
|
|
481
|
+
# text
|
|
482
|
+
ax[1].text(right[2] - (right[2]-left[1])/2, position_text, str(counter), fontsize=8)
|
|
483
|
+
# primers
|
|
484
|
+
ax[1].hlines(position_amp, left[1], left[2], linewidth=5, color="red")
|
|
485
|
+
ax[1].hlines(position_amp, right[1], right[2], linewidth=5, color="red")
|
|
441
486
|
# legends
|
|
442
487
|
ax[1].hlines(position_amp, left[1]+config.PRIMER_SIZES[1], right[2]-config.PRIMER_SIZES[1], linewidth=5, label="amplicons")
|
|
443
488
|
ax[1].hlines(position_amp, left[1], left[2], linewidth=5, color="red", label="primers")
|
|
@@ -447,12 +492,10 @@ def qpcr_subplot(ax, amplicon_scheme):
|
|
|
447
492
|
"""
|
|
448
493
|
creates the qpcr subplot
|
|
449
494
|
"""
|
|
450
|
-
counter
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
right = amplicon_scheme[scheme]["RIGHT"]
|
|
455
|
-
probe = amplicon_scheme[scheme]["PROBE"]
|
|
495
|
+
for counter, amp in enumerate(amplicon_scheme):
|
|
496
|
+
left = amp["LEFT"]
|
|
497
|
+
right = amp["RIGHT"]
|
|
498
|
+
probe = amp["PROBE"]
|
|
456
499
|
# amplicons
|
|
457
500
|
ax[1].hlines(0.8, left[1], right[2], linewidth=5)
|
|
458
501
|
# text
|
|
@@ -463,14 +506,13 @@ def qpcr_subplot(ax, amplicon_scheme):
|
|
|
463
506
|
# probe
|
|
464
507
|
ax[1].hlines(0.75, probe[1], probe[2], linewidth=5, color="darkgrey")
|
|
465
508
|
|
|
466
|
-
counter += 1
|
|
467
509
|
# legends
|
|
468
510
|
ax[1].hlines(0.8, left[1]+config.PRIMER_SIZES[1], right[2]-config.PRIMER_SIZES[1], linewidth=5, label="amplicons")
|
|
469
511
|
ax[1].hlines(0.8, left[1], left[2], linewidth=5, color="red", label="primers")
|
|
470
512
|
ax[1].hlines(0.75, probe[1], probe[2], linewidth=5, color="darkgrey", label="probe")
|
|
471
513
|
|
|
472
514
|
|
|
473
|
-
def varvamp_plot(path, alignment_cleaned, primer_regions, all_primers=None, amplicon_scheme=None, probe_regions=None):
|
|
515
|
+
def varvamp_plot(path, alignment_cleaned, primer_regions, scheme_name, all_primers=None, amplicon_scheme=None, probe_regions=None):
|
|
474
516
|
"""
|
|
475
517
|
creates overview plot for the amplicon design
|
|
476
518
|
and per base coverage plots
|
|
@@ -483,7 +525,7 @@ def varvamp_plot(path, alignment_cleaned, primer_regions, all_primers=None, ampl
|
|
|
483
525
|
fig, ax = plt.subplots(2, 1, figsize=[22, 6], squeeze=True, sharex=True, gridspec_kw={'height_ratios': [4, 1]})
|
|
484
526
|
fig.subplots_adjust(hspace=0)
|
|
485
527
|
# entropy plot
|
|
486
|
-
entropy_subplot(ax, alignment_cleaned)
|
|
528
|
+
entropy_subplot(ax, alignment_cleaned, scheme_name)
|
|
487
529
|
# primer regions plot
|
|
488
530
|
region_subplot(ax, primer_regions)
|
|
489
531
|
# probe region plot for probes
|
|
@@ -509,48 +551,32 @@ def varvamp_plot(path, alignment_cleaned, primer_regions, all_primers=None, ampl
|
|
|
509
551
|
plt.close()
|
|
510
552
|
|
|
511
553
|
|
|
512
|
-
def
|
|
554
|
+
def get_primers_for_plot(amplicon_scheme, scheme_name, mode):
|
|
513
555
|
"""
|
|
514
556
|
get the primers for per base pair plot (single, tiled)
|
|
515
557
|
"""
|
|
516
558
|
amplicon_primers = []
|
|
517
559
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
right = amplicon_scheme[pool][amp][primer_names[1]]
|
|
523
|
-
amplicon_primers.append((primer_names[0], left))
|
|
524
|
-
amplicon_primers.append((primer_names[1], right))
|
|
525
|
-
|
|
526
|
-
return amplicon_primers
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
def get_QPCR_primers_for_plot(amplicon_schemes):
|
|
530
|
-
"""
|
|
531
|
-
get the primers for per base pair plot (qpcr)
|
|
532
|
-
"""
|
|
533
|
-
amplicon_primers = []
|
|
560
|
+
if mode == "SINGLE/TILED":
|
|
561
|
+
oligo_types = ["LEFT", "RIGHT"]
|
|
562
|
+
else:
|
|
563
|
+
oligo_types = ["PROBE", "LEFT", "RIGHT"]
|
|
534
564
|
|
|
535
|
-
for
|
|
536
|
-
for
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
primer_name = f"{scheme}_{type}"
|
|
540
|
-
amplicon_primers.append((primer_name, amplicon_schemes[scheme][type]))
|
|
565
|
+
for counter, amp in enumerate(amplicon_scheme):
|
|
566
|
+
for oligo_type in oligo_types:
|
|
567
|
+
primer_name = f"{scheme_name}_{counter}_{oligo_type}"
|
|
568
|
+
amplicon_primers.append((primer_name, amp[oligo_type]))
|
|
541
569
|
|
|
542
570
|
return amplicon_primers
|
|
543
571
|
|
|
544
572
|
|
|
545
|
-
def per_base_mismatch_plot(path, amplicon_scheme, threshold, mode="SINGLE/TILED"):
|
|
573
|
+
def per_base_mismatch_plot(path, amplicon_scheme, threshold, scheme_name, mode="SINGLE/TILED"):
|
|
546
574
|
"""
|
|
547
575
|
per base pair mismatch multiplot
|
|
548
576
|
"""
|
|
549
577
|
out = os.path.join(path, "per_base_mismatches.pdf")
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
elif mode == "QPCR":
|
|
553
|
-
amplicon_primers = get_QPCR_primers_for_plot(amplicon_scheme)
|
|
578
|
+
|
|
579
|
+
amplicon_primers = get_primers_for_plot(amplicon_scheme, scheme_name, mode)
|
|
554
580
|
# ini multi pdf
|
|
555
581
|
with PdfPages(out) as pdf:
|
|
556
582
|
# always print 4 primers to one page
|
|
@@ -581,3 +607,15 @@ def per_base_mismatch_plot(path, amplicon_scheme, threshold, mode="SINGLE/TILED"
|
|
|
581
607
|
# - to pdf
|
|
582
608
|
pdf.savefig(fig, bbox_inches='tight')
|
|
583
609
|
plt.close()
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def write_BLAST_warning(amplicon_name, log_file):
|
|
613
|
+
"""
|
|
614
|
+
for each primer pair that has potential unspecific amplicons
|
|
615
|
+
write warnings to file.
|
|
616
|
+
"""
|
|
617
|
+
logging.raise_error(
|
|
618
|
+
f"{amplicon_name} could produce off-targets. No better amplicon in this area was found.",
|
|
619
|
+
log_file,
|
|
620
|
+
exit=False,
|
|
621
|
+
)
|