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.
@@ -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"{seq_id}.fasta"
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
- "ambiguous_consensus",
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
- print(
72
- "ambiguous_consensus",
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
- round(primer_properties[3], 1), # penalty
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 scheme in final_schemes:
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
- "ambiguous_consensus",
148
- final_schemes[scheme]["LEFT"][1],
149
- final_schemes[scheme]["RIGHT"][2],
150
- scheme,
151
- round(final_schemes[scheme]["penalty"], 1),
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 = final_schemes[scheme]["LEFT"][1]
157
- amplicon_stop = final_schemes[scheme]["RIGHT"][2]
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
- scheme,
161
- round(final_schemes[scheme]["penalty"], 1),
162
- final_schemes[scheme]["deltaG"],
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 final_schemes[scheme]:
172
- if oligo_type == "penalty" or oligo_type == "deltaG":
173
- continue
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
- scheme,
200
+ amp_name,
186
201
  oligo_type,
187
- final_schemes[scheme][oligo_type][1] + 1,
188
- final_schemes[scheme][oligo_type][2],
202
+ amp[oligo_type][1] + 1,
203
+ amp[oligo_type][2],
189
204
  seq.upper(),
190
205
  len(seq),
191
- round(primers.calc_gc(final_schemes[scheme][oligo_type][0]), 1),
192
- round(primers.calc_temp(final_schemes[scheme][oligo_type][0]), 1),
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(final_schemes[scheme][oligo_type][3], 1),
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
- f"{scheme}_{oligo_type}",
203
- final_schemes[scheme][oligo_type],
204
- direction
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">{scheme}_{oligo_type}\n{seq.upper()}", file=fasta)
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\talternate_primer_name\tpool\tstart\tstop\tseq\tsize\tgc_best\ttemp_best\tmean_gc\tmean_temp\tpenalty",
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
- for pool in amplicon_scheme:
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
- new_name = f"AMPLICON_{str(counter)}"
238
- counter += 1
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
- primer_names = list(amplicon_scheme[pool][amp].keys())
241
- left = (primer_names[0], amplicon_scheme[pool][amp][primer_names[0]])
242
- right = (primer_names[1], amplicon_scheme[pool][amp][primer_names[1]])
243
- amp_length = right[1][2] - left[1][1]
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(left[1][3] + right[1][3], 1)
249
- print(
250
- "ambiguous_consensus",
251
- left[1][1],
252
- right[1][2],
253
- new_name,
254
- bed_score,
255
- sep="\t",
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
- # write primer assignments tabular file
259
- print(
260
- f"{new_name}_LEFT",
261
- f"{new_name}_RIGHT",
262
- sep="\t",
263
- file=tabular
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 [("+", left), ("-", right)]:
267
- seq = ambiguous_consensus[primer[1][1]:primer[1][2]]
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"{new_name}_RIGHT"
294
+ primer_name = f"{amp_name}_RIGHT"
271
295
  else:
272
- primer_name = f"{new_name}_LEFT"
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
- new_name,
304
+ amp_name,
281
305
  amp_length,
282
306
  primer_name,
283
- primer[0],
284
- pool,
285
- primer[1][1] + 1,
286
- primer[1][2],
307
+ primer[-1],
308
+ pool+1,
309
+ primer[1] + 1,
310
+ primer[2],
287
311
  seq.upper(),
288
- len(primer[1][0]),
289
- round(primers.calc_gc(primer[1][0]), 1),
290
- round(primers.calc_temp(primer[1][0]), 1),
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[1][3], 1),
317
+ round(primer[3], 1),
318
+ amplicon_has_off_target,
294
319
  sep="\t",
295
320
  file=tsv
296
321
  )
297
- # write primer bed file
298
- write_primers_to_bed(
299
- primer_bed_file,
300
- primer_name,
301
- primer[1],
302
- direction
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 dimers in primer_dimers:
364
+ for pool, primer1, primer2 in primer_dimers:
317
365
  print(
318
- dimers[0][0],
319
- dimers[0][2],
320
- dimers[1][2],
321
- round(primers.calc_dimer(dimers[0][3][0], dimers[1][3][0]).tm, 1),
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("final amplicon design")
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 = 0
422
- for pool in amplicon_scheme:
423
- for amp in amplicon_scheme[pool]:
424
- if pool == 0:
425
- position_amp = 0.7
426
- position_text = 0.6
427
- elif pool == 1:
428
- position_amp = 0.6
429
- position_text = 0.65
430
- primer_names = [i for i in amplicon_scheme[pool][amp]]
431
- left = amplicon_scheme[pool][amp][primer_names[0]]
432
- right = amplicon_scheme[pool][amp][primer_names[1]]
433
- # amplicons
434
- ax[1].hlines(position_amp, left[1], right[2], linewidth=5)
435
- # text
436
- ax[1].text(right[2] - (right[2]-left[1])/2, position_text, str(counter), fontsize=8)
437
- # primers
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 = 0
451
-
452
- for scheme in amplicon_scheme:
453
- left = amplicon_scheme[scheme]["LEFT"]
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 get_SINGLE_TILED_primers_for_plot(amplicon_scheme):
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
- for pool in amplicon_scheme:
519
- for amp in amplicon_scheme[pool]:
520
- primer_names = [i for i in amplicon_scheme[pool][amp]]
521
- left = amplicon_scheme[pool][amp][primer_names[0]]
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 scheme in amplicon_schemes:
536
- for type in amplicon_schemes[scheme]:
537
- if type == "penalty" or type == "deltaG":
538
- continue
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
- if mode == "SINGLE/TILED":
551
- amplicon_primers = get_SINGLE_TILED_primers_for_plot(amplicon_scheme)
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
+ )