cutseq 0.0.4__tar.gz → 0.0.5__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cutseq
3
- Version: 0.0.4
3
+ Version: 0.0.5
4
4
  Summary: Automatically cut adapter / barcode / UMI from NGS data
5
5
  Home-page: https://github.com/y9c/cutseq
6
6
  License: MIT
@@ -18,6 +18,7 @@ from cutadapt.adapters import (
18
18
  NonInternalFrontAdapter,
19
19
  PrefixAdapter,
20
20
  RightmostFrontAdapter,
21
+ SuffixAdapter,
21
22
  )
22
23
  from cutadapt.files import InputPaths, OutputFiles
23
24
  from cutadapt.modifiers import (
@@ -29,13 +30,11 @@ from cutadapt.modifiers import (
29
30
  UnconditionalCutter,
30
31
  )
31
32
  from cutadapt.pipeline import PairedEndPipeline, SingleEndPipeline
32
- from cutadapt.predicates import IsUntrimmed, TooShort
33
+ from cutadapt.predicates import Predicate, TooShort
33
34
  from cutadapt.runners import make_runner
34
35
  from cutadapt.steps import (
35
- InfoFileWriter,
36
36
  PairedEndFilter,
37
37
  PairedEndSink,
38
- PairedSingleEndStep,
39
38
  SingleEndFilter,
40
39
  SingleEndSink,
41
40
  )
@@ -118,7 +117,7 @@ class BarcodeConfig:
118
117
  class CutadaptConfig:
119
118
  def __init__(self):
120
119
  self.rname_suffix = False
121
- self.discarded_untrimmed = False
120
+ self.ensure_inline_barcode = False
122
121
  self.trim_polyA = False
123
122
  self.min_length = 20
124
123
  self.min_quality = 20
@@ -126,6 +125,25 @@ class CutadaptConfig:
126
125
  self.threads = 1
127
126
 
128
127
 
128
+ class IsUntrimmedRef(Predicate):
129
+ """
130
+ Select reads for which no adapter match was found
131
+ """
132
+
133
+ def __init__(self, ref_adapters):
134
+ self.ref_adapters = ref_adapters
135
+
136
+ def __repr__(self):
137
+ return "IsUntrimmedRef()"
138
+
139
+ def test(self, read, info):
140
+ # check not all adapters with ref_adapters are exist in the matches
141
+ match_adapters = [match.adapter for match in info.matches]
142
+ if any([adapter not in match_adapters for adapter in self.ref_adapters]):
143
+ return True
144
+ return False
145
+
146
+
129
147
  def run_steps(steps, dry_run=False):
130
148
  if dry_run:
131
149
  print(
@@ -138,7 +156,7 @@ def run_steps(steps, dry_run=False):
138
156
  return process.stdout.decode(), process.stderr.decode()
139
157
 
140
158
 
141
- def pipeline_single(input1, output1, short1, untrimed1, barcode, settings):
159
+ def pipeline_single(input1, output1, short1, untrimmed1, barcode, settings):
142
160
  modifiers = []
143
161
  # step 1: remove suffix in the read name
144
162
  modifiers.extend([SuffixRemover(".1"), SuffixRemover("/1")])
@@ -162,14 +180,11 @@ def pipeline_single(input1, output1, short1, untrimed1, barcode, settings):
162
180
  )
163
181
  # step 4: trim inline barcode
164
182
  if barcode.inline5.len > 0:
165
- modifiers.append(
166
- AdapterCutter(
167
- [PrefixAdapter(sequence=barcode.inline5.fw, max_errors=0.2)],
168
- times=1,
169
- )
170
- )
183
+ adapter_inline5 = PrefixAdapter(sequence=barcode.inline5.fw, max_errors=0.2)
184
+ modifiers.append(AdapterCutter([adapter_inline5], times=1))
171
185
  if barcode.inline3.len > 0:
172
- modifiers.append(UnconditionalCutter(-barcode.inline3.len))
186
+ adapter_inline3 = SuffixAdapter(sequence=barcode.inline3.fw, max_errors=0.2)
187
+ modifiers.append(AdapterCutter([adapter_inline3], times=1))
173
188
 
174
189
  # step 5: extract UMI
175
190
  if barcode.umi5.len > 0:
@@ -215,19 +230,35 @@ def pipeline_single(input1, output1, short1, untrimed1, barcode, settings):
215
230
  qualities=runner.input_file_format().has_qualities(),
216
231
  interleaved=False,
217
232
  )
218
- steps = [
233
+ steps = []
234
+ steps.append(
219
235
  # --info-file=info.txt
220
236
  # PairedSingleEndStep(InfoFileWriter(outfiles.open_text("info.txt"))),
221
237
  # -m 10
222
238
  SingleEndFilter(
223
239
  TooShort(settings.min_length), outfiles.open_record_writer(short1)
224
240
  ),
225
- # TODO: --max-n=0 support
226
- # --discard-untrimmed
227
- # SingleEndFilter( IsUntrimmed(), IsUntrimmed(), pair_filter_mode="any"),
241
+ )
242
+ if (
243
+ settings.ensure_inline_barcode
244
+ and barcode.inline5.len + barcode.inline3.len > 0
245
+ ):
246
+ ref_adapters = []
247
+ if barcode.inline5.len > 0:
248
+ ref_adapters.append(adapter_inline5)
249
+ if barcode.inline3.len > 0:
250
+ ref_adapters.append(adapter_inline3)
251
+ steps.append(
252
+ # TODO: --max-n=0 support
253
+ SingleEndFilter(
254
+ IsUntrimmedRef(ref_adapters),
255
+ outfiles.open_record_writer(untrimmed1, interleaved=False),
256
+ ),
257
+ )
258
+ steps.append(
228
259
  # -o
229
260
  SingleEndSink(outfiles.open_record_writer(output1, interleaved=False)),
230
- ]
261
+ )
231
262
  pipeline = SingleEndPipeline(modifiers, steps)
232
263
  _stats = runner.run(pipeline, Progress(), outfiles)
233
264
  # _ = stats.as_json()
@@ -241,7 +272,7 @@ def pipeline_paired(
241
272
  output2,
242
273
  short1,
243
274
  short2,
244
- untrimed1,
275
+ untrimmed1,
245
276
  untrimmed2,
246
277
  barcode,
247
278
  settings,
@@ -290,23 +321,19 @@ def pipeline_paired(
290
321
  )
291
322
  # step 4: trim inline barcode
292
323
  if barcode.inline5.len > 0:
324
+ adapter_inline5 = PrefixAdapter(sequence=barcode.inline5.fw, max_errors=0.2)
293
325
  modifiers.append(
294
326
  (
295
- AdapterCutter(
296
- [PrefixAdapter(sequence=barcode.inline5.fw, max_errors=0.2)],
297
- times=1,
298
- ),
327
+ AdapterCutter([adapter_inline5], times=1),
299
328
  UnconditionalCutter(-barcode.inline5.len),
300
329
  )
301
330
  )
302
331
  if barcode.inline3.len > 0:
332
+ adapter_inline3 = PrefixAdapter(sequence=barcode.inline3.rc, max_errors=0.2)
303
333
  modifiers.append(
304
334
  (
305
335
  UnconditionalCutter(-barcode.inline3.len),
306
- AdapterCutter(
307
- [BackAdapter(sequence=barcode.inline3.rc, max_errors=0.2)],
308
- times=1,
309
- ),
336
+ AdapterCutter([adapter_inline3], times=1),
310
337
  )
311
338
  )
312
339
 
@@ -387,21 +414,40 @@ def pipeline_paired(
387
414
  qualities=runner.input_file_format().has_qualities(),
388
415
  interleaved=False,
389
416
  )
390
- steps = [
391
- # --info-file=info.txt
392
- # PairedSingleEndStep(InfoFileWriter(outfiles.open_text("info.txt"))),
393
- # -m 10:10
417
+ steps = []
418
+ # --info-file=info.txt
419
+ # PairedSingleEndStep(InfoFileWriter(outfiles.open_text("info.txt"))),
420
+ # -m 10:10
421
+ steps.append(
394
422
  PairedEndFilter(
395
423
  TooShort(settings.min_length),
396
424
  TooShort(settings.min_length),
397
425
  outfiles.open_record_writer(short1, short2, interleaved=False),
398
- ),
399
- # TODO: --max-n=0 support
400
- # --discard-untrimmed
401
- # PairedEndFilter( IsUntrimmed(), IsUntrimmed(), pair_filter_mode="any"),
426
+ )
427
+ )
428
+ if (
429
+ settings.ensure_inline_barcode
430
+ and barcode.inline5.len + barcode.inline3.len > 0
431
+ ):
432
+ steps.append(
433
+ # TODO: --max-n=0 support
434
+ PairedEndFilter(
435
+ IsUntrimmedRef([adapter_inline5])
436
+ if barcode.inline5.len > 0
437
+ else None,
438
+ IsUntrimmedRef([adapter_inline3])
439
+ if barcode.inline3.len > 0
440
+ else None,
441
+ outfiles.open_record_writer(
442
+ untrimmed1, untrimmed2, interleaved=False
443
+ ),
444
+ pair_filter_mode="any",
445
+ )
446
+ )
447
+ steps.append(
402
448
  # -o ... -p ...
403
- PairedEndSink(outfiles.open_record_writer(output1, output2)),
404
- ]
449
+ PairedEndSink(outfiles.open_record_writer(output1, output2))
450
+ )
405
451
  pipeline = PairedEndPipeline(modifiers, steps)
406
452
  _stats = runner.run(pipeline, Progress(), outfiles)
407
453
  # _ = stats.as_json()
@@ -411,12 +457,9 @@ def pipeline_paired(
411
457
  def run_cutseq(args):
412
458
  barcode_config = BarcodeConfig(args.adapter_scheme.upper())
413
459
  settings = CutadaptConfig()
414
- if args.with_rname_suffix:
415
- settings.rname_suffix = True
416
- if args.untrimmed_file is None:
417
- settings.discarded_untrimmed = True
418
- if args.trim_polyA:
419
- settings.trim_polyA = True
460
+ settings.rname_suffix = args.with_rname_suffix
461
+ settings.ensure_inline_barcode = args.ensure_inline_barcode
462
+ settings.trim_polyA = args.trim_polyA
420
463
  settings.threads = args.threads
421
464
  settings.min_length = args.min_length
422
465
  settings.dry_run = args.dry_run
@@ -506,6 +549,12 @@ def main():
506
549
  action="store_true",
507
550
  help="R1 and R2 suffix cotains suffix. MGI platform.",
508
551
  )
552
+
553
+ parser.add_argument(
554
+ "--ensure-inline-barcode",
555
+ action="store_true",
556
+ help="Output discarded reads without inline barcode.",
557
+ )
509
558
  parser.add_argument(
510
559
  "-U",
511
560
  "--untrimmed-file",
@@ -584,12 +633,8 @@ def main():
584
633
  args.short_file = validate_output_file(
585
634
  args.short_file, args.input_file, args.output_prefix, "short"
586
635
  )
587
- args.untrimmed_file = (
588
- validate_output_file(
589
- args.untrimmed_file, args.input_file, args.output_prefix, "untrimmed"
590
- )
591
- if args.untrimmed_file is not None
592
- else [None] * len(args.input_file)
636
+ args.untrimmed_file = validate_output_file(
637
+ args.untrimmed_file, args.input_file, args.output_prefix, "untrimmed"
593
638
  )
594
639
 
595
640
  run_cutseq(args)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "cutseq"
3
- version = "0.0.4"
3
+ version = "0.0.5"
4
4
  description = "Automatically cut adapter / barcode / UMI from NGS data"
5
5
  authors = ["Ye Chang <yech1990@gmail.com>"]
6
6
  license = "MIT"
File without changes
File without changes