Skip to content

Commit bd02010

Browse files
committed
prevent a breaking change in our params API and confirming that all nextflow.config params are exposed in the nvd wrapper CLI
1 parent 377be69 commit bd02010

4 files changed

Lines changed: 188 additions & 12 deletions

File tree

bin/__main__.py

Lines changed: 173 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@
5050
VALID_PROFILES = ["docker", "apptainer", "local", "chtc_hpc"]
5151
MAX_PREVIEW_ITEMS = 5 # Max items to show before "... and N more"
5252

53+
# Help panel names for organizing --help output
54+
PANEL_CORE = "Core Options"
55+
PANEL_PREPROCESSING = "Read Preprocessing"
56+
PANEL_DATABASES = "Database Paths"
57+
PANEL_ANALYSIS = "Analysis Parameters"
58+
PANEL_SRA = "SRA Submission"
59+
PANEL_LABKEY = "LabKey Integration"
60+
5361
# ============================================================================
5462
# UTILITIES
5563
# ============================================================================
@@ -253,6 +261,9 @@ def build_nextflow_command( # noqa: PLR0913
253261
@app.command("r", hidden=True) # Alias
254262
def run( # noqa: PLR0913, PLR0912, PLR0915, C901
255263
# Complexity/boolean/B008 warnings are acceptable for CLI with many options
264+
# -------------------------------------------------------------------------
265+
# Core Options
266+
# -------------------------------------------------------------------------
256267
samplesheet: Path = typer.Option(
257268
...,
258269
"--samplesheet",
@@ -261,157 +272,279 @@ def run( # noqa: PLR0913, PLR0912, PLR0915, C901
261272
exists=True,
262273
file_okay=True,
263274
dir_okay=False,
275+
rich_help_panel=PANEL_CORE,
264276
),
265277
experiment_id: str = typer.Option(
266278
...,
267279
"--experiment-id",
268280
"-e",
269281
help="Unique experiment identifier",
282+
rich_help_panel=PANEL_CORE,
270283
),
271284
tools: str = typer.Option(
272285
"all",
273286
"--tools",
274287
"-t",
275288
help="Tools to run: stat_blast, gottcha, or all",
289+
rich_help_panel=PANEL_CORE,
276290
),
277291
results: Path | None = typer.Option(
278292
None,
279293
"--results",
280294
"-r",
281295
help="Results directory (default: ./results)",
296+
rich_help_panel=PANEL_CORE,
282297
),
283298
profile: str | None = typer.Option(
284299
None,
285300
"--profile",
286301
"-p",
287302
help="Execution profile: docker, apptainer, local (auto-detect if not set)",
303+
rich_help_panel=PANEL_CORE,
288304
),
289305
config: Path | None = typer.Option(
290306
None,
291307
"--config",
292308
"-c",
293309
help="Custom config file (default: ~/.nvd2/config/user.config)",
294310
exists=True,
311+
rich_help_panel=PANEL_CORE,
295312
),
296313
resume: bool = typer.Option(
297314
False,
298315
"--resume",
299316
help="Resume from checkpoint",
317+
rich_help_panel=PANEL_CORE,
300318
),
301319
cleanup: bool = typer.Option(
302320
False,
303321
"--cleanup",
304322
help="Clean work directory after success",
323+
rich_help_panel=PANEL_CORE,
305324
),
306325
work_dir: Path | None = typer.Option(
307326
None,
308327
"--work-dir",
309328
"-w",
310329
help="Nextflow work directory",
330+
rich_help_panel=PANEL_CORE,
311331
),
312-
# Database overrides
332+
# -------------------------------------------------------------------------
333+
# Database Paths
334+
# -------------------------------------------------------------------------
313335
gottcha2_db: Path | None = typer.Option(
314336
None,
315337
"--gottcha2-db",
316338
help="Override GOTTCHA2 database path",
339+
rich_help_panel=PANEL_DATABASES,
317340
),
318341
blast_db: Path | None = typer.Option(
319342
None,
320343
"--blast-db",
321344
help="Override BLAST database directory",
345+
rich_help_panel=PANEL_DATABASES,
322346
),
323347
blast_db_prefix: str | None = typer.Option(
324348
None,
325349
"--blast-db-prefix",
326350
help="Override BLAST database prefix",
351+
rich_help_panel=PANEL_DATABASES,
327352
),
328353
stat_index: Path | None = typer.Option(
329354
None,
330355
"--stat-index",
331356
help="Override STAT index file",
357+
rich_help_panel=PANEL_DATABASES,
332358
),
333359
stat_dbss: Path | None = typer.Option(
334360
None,
335361
"--stat-dbss",
336362
help="Override STAT dbss file",
363+
rich_help_panel=PANEL_DATABASES,
337364
),
338365
stat_annotation: Path | None = typer.Option(
339366
None,
340367
"--stat-annotation",
341368
help="Override STAT annotation file",
369+
rich_help_panel=PANEL_DATABASES,
342370
),
343371
human_virus_taxlist: Path | None = typer.Option(
344372
None,
345373
"--human-virus-taxlist",
346374
help="Override human virus taxlist file",
375+
rich_help_panel=PANEL_DATABASES,
347376
),
348-
# Analysis parameters
377+
# -------------------------------------------------------------------------
378+
# Analysis Parameters
379+
# -------------------------------------------------------------------------
349380
cutoff_percent: float | None = typer.Option(
350381
None,
351382
"--cutoff-percent",
352383
help="Cutoff percentage (default: 0.001)",
384+
rich_help_panel=PANEL_ANALYSIS,
353385
),
354386
tax_stringency: float | None = typer.Option(
355387
None,
356388
"--tax-stringency",
357389
help="Taxonomy stringency (default: 0.7)",
390+
rich_help_panel=PANEL_ANALYSIS,
358391
),
359392
entropy: float | None = typer.Option(
360393
None,
361394
"--entropy",
362395
help="Entropy threshold (default: 0.9)",
396+
rich_help_panel=PANEL_ANALYSIS,
363397
),
364398
min_gottcha_reads: int | None = typer.Option(
365399
None,
366400
"--min-gottcha-reads",
367401
help="Minimum reads for GOTTCHA2 (default: 250)",
402+
rich_help_panel=PANEL_ANALYSIS,
368403
),
369404
max_blast_targets: int | None = typer.Option(
370405
None,
371406
"--max-blast-targets",
372407
help="Maximum BLAST targets (default: 100)",
408+
rich_help_panel=PANEL_ANALYSIS,
373409
),
374410
blast_retention_count: int | None = typer.Option(
375411
None,
376412
"--blast-retention-count",
377413
help="Retain top X BLAST hits (default: 5)",
414+
rich_help_panel=PANEL_ANALYSIS,
378415
),
379416
min_consecutive_bases: int | None = typer.Option(
380417
None,
381418
"--min-consecutive-bases",
382419
help="Minimum consecutive bases (default: 200)",
420+
rich_help_panel=PANEL_ANALYSIS,
383421
),
384422
qtrim: str | None = typer.Option(
385423
None,
386424
"--qtrim",
387425
help="Quality trimming mode (default: 't')",
426+
rich_help_panel=PANEL_ANALYSIS,
388427
),
389428
include_children: bool | None = typer.Option(
390429
None,
391430
"--include-children/--no-include-children",
392431
help="Include children in taxonomy (default: true)",
432+
rich_help_panel=PANEL_ANALYSIS,
393433
),
394434
max_concurrent_downloads: int | None = typer.Option(
395435
None,
396436
"--max-concurrent-downloads",
397437
help="Maximum concurrent SRA downloads (default: 3)",
438+
rich_help_panel=PANEL_ANALYSIS,
439+
),
440+
# -------------------------------------------------------------------------
441+
# Read Preprocessing
442+
# -------------------------------------------------------------------------
443+
preprocess: bool = typer.Option(
444+
False,
445+
"--preprocess",
446+
help="Enable all preprocessing steps (dedup, trim, scrub, filter)",
447+
rich_help_panel=PANEL_PREPROCESSING,
448+
),
449+
merge_pairs: bool | None = typer.Option(
450+
None,
451+
"--merge-pairs/--no-merge-pairs",
452+
help="Merge paired read mates based on overlaps",
453+
rich_help_panel=PANEL_PREPROCESSING,
454+
),
455+
dedup: bool | None = typer.Option(
456+
None,
457+
"--dedup/--no-dedup",
458+
help="Deduplicate reads (default: follows --preprocess)",
459+
rich_help_panel=PANEL_PREPROCESSING,
460+
),
461+
trim_adapters: bool | None = typer.Option(
462+
None,
463+
"--trim-adapters/--no-trim-adapters",
464+
help="Trim Illumina adapters (default: follows --preprocess)",
465+
rich_help_panel=PANEL_PREPROCESSING,
466+
),
467+
scrub_host_reads: bool | None = typer.Option(
468+
None,
469+
"--scrub-host-reads/--no-scrub-host-reads",
470+
help="Remove host reads with hostile (default: follows --preprocess)",
471+
rich_help_panel=PANEL_PREPROCESSING,
472+
),
473+
hostile_index: Path | None = typer.Option(
474+
None,
475+
"--hostile-index",
476+
help="Path to local hostile index (for offline use)",
477+
rich_help_panel=PANEL_PREPROCESSING,
478+
),
479+
hostile_index_name: str | None = typer.Option(
480+
None,
481+
"--hostile-index-name",
482+
help="Standard hostile index name (default: human-t2t-hla.rs-viral-202401_ml-phage-202401)",
483+
rich_help_panel=PANEL_PREPROCESSING,
484+
),
485+
filter_reads: bool | None = typer.Option(
486+
None,
487+
"--filter-reads/--no-filter-reads",
488+
help="Filter reads by quality/length (default: follows --preprocess)",
489+
rich_help_panel=PANEL_PREPROCESSING,
490+
),
491+
min_read_quality_illumina: int | None = typer.Option(
492+
None,
493+
"--min-read-quality-illumina",
494+
help="Minimum average quality for Illumina reads (default: 20)",
495+
rich_help_panel=PANEL_PREPROCESSING,
496+
),
497+
min_read_quality_nanopore: int | None = typer.Option(
498+
None,
499+
"--min-read-quality-nanopore",
500+
help="Minimum average quality for Nanopore reads (default: 12)",
501+
rich_help_panel=PANEL_PREPROCESSING,
398502
),
503+
min_read_length: int | None = typer.Option(
504+
None,
505+
"--min-read-length",
506+
help="Minimum read length (default: 50)",
507+
rich_help_panel=PANEL_PREPROCESSING,
508+
),
509+
max_read_length: int | None = typer.Option(
510+
None,
511+
"--max-read-length",
512+
help="Maximum read length (default: no limit)",
513+
rich_help_panel=PANEL_PREPROCESSING,
514+
),
515+
# -------------------------------------------------------------------------
516+
# SRA Submission
517+
# -------------------------------------------------------------------------
518+
sra_human_db: Path | None = typer.Option(
519+
None,
520+
"--sra-human-db",
521+
help="Path to human reads database for SRA submission scrubbing",
522+
rich_help_panel=PANEL_SRA,
523+
),
524+
# DEPRECATED: Use --sra-human-db instead
399525
human_read_scrub: Path | None = typer.Option(
400526
None,
401527
"--human-read-scrub",
402-
help="Human read scrubbing file path",
528+
help="[DEPRECATED] Use --sra-human-db instead",
529+
hidden=True,
403530
),
404-
# LabKey
531+
# -------------------------------------------------------------------------
532+
# LabKey Integration
533+
# -------------------------------------------------------------------------
405534
labkey: bool = typer.Option(
406535
False,
407536
"--labkey",
408537
help="Enable LabKey integration",
538+
rich_help_panel=PANEL_LABKEY,
409539
),
410-
# Dry run
540+
# -------------------------------------------------------------------------
541+
# Execution Control
542+
# -------------------------------------------------------------------------
411543
dry_run: bool = typer.Option(
412544
False,
413545
"--dry-run",
414546
help="Show command without executing",
547+
rich_help_panel=PANEL_CORE,
415548
),
416549
) -> None:
417550
"""
@@ -502,8 +635,41 @@ def run( # noqa: PLR0913, PLR0912, PLR0915, C901
502635
extra_params["include_children"] = "true" if include_children else "false"
503636
if max_concurrent_downloads is not None:
504637
extra_params["max_concurrent_downloads"] = max_concurrent_downloads
505-
if human_read_scrub:
506-
extra_params["human_read_scrub"] = human_read_scrub
638+
# Preprocessing options
639+
if preprocess:
640+
extra_params["preprocess"] = "true"
641+
if merge_pairs is not None:
642+
extra_params["merge_pairs"] = "true" if merge_pairs else "false"
643+
if dedup is not None:
644+
extra_params["dedup"] = "true" if dedup else "false"
645+
if trim_adapters is not None:
646+
extra_params["trim_adapters"] = "true" if trim_adapters else "false"
647+
if scrub_host_reads is not None:
648+
extra_params["scrub_host_reads"] = "true" if scrub_host_reads else "false"
649+
if hostile_index:
650+
extra_params["hostile_index"] = hostile_index
651+
if hostile_index_name:
652+
extra_params["hostile_index_name"] = hostile_index_name
653+
if filter_reads is not None:
654+
extra_params["filter_reads"] = "true" if filter_reads else "false"
655+
if min_read_quality_illumina is not None:
656+
extra_params["min_read_quality_illumina"] = min_read_quality_illumina
657+
if min_read_quality_nanopore is not None:
658+
extra_params["min_read_quality_nanopore"] = min_read_quality_nanopore
659+
if min_read_length is not None:
660+
extra_params["min_read_length"] = min_read_length
661+
if max_read_length is not None:
662+
extra_params["max_read_length"] = max_read_length
663+
# SRA submission options
664+
# Handle deprecated --human-read-scrub option
665+
if human_read_scrub and not sra_human_db:
666+
warning(
667+
"DEPRECATION: --human-read-scrub is deprecated. "
668+
"Please use --sra-human-db instead.",
669+
)
670+
extra_params["sra_human_db"] = human_read_scrub
671+
elif sra_human_db:
672+
extra_params["sra_human_db"] = sra_human_db
507673
if labkey:
508674
extra_params["labkey"] = "true"
509675

nextflow.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ params {
6565
// Path to human reads database for SRA submission scrubbing (clumpify workflow)
6666
sra_human_db = null
6767

68+
// DEPRECATED: Use sra_human_db instead. Kept for backward compatibility.
69+
human_read_scrub = null
70+
6871
// Quality and length filtering
6972
filter_reads = null
7073

pixi.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

workflows/clumpify.nf

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@ workflow CLUMPIFY_WORKFLOW {
1616
gated_reads.map { id, _platform, reads -> tuple(id, file(reads)) }
1717
)
1818

19-
// Check if sra_human_db is a valid file path
20-
if (params.sra_human_db != null && !file(params.sra_human_db).isFile()) {
21-
error("Error: Human database file does not exist: ${params.sra_human_db}")
19+
// Resolve human database path, supporting deprecated human_read_scrub param
20+
def human_db_path = params.sra_human_db ?: params.human_read_scrub
21+
22+
if (params.human_read_scrub != null && params.sra_human_db == null) {
23+
log.warn "DEPRECATION WARNING: --human_read_scrub is deprecated. Please use --sra_human_db instead."
24+
}
25+
26+
// Check if human database path is a valid file
27+
if (human_db_path != null && !file(human_db_path).isFile()) {
28+
error("Error: Human database file does not exist: ${human_db_path}")
2229
}
2330

24-
ch_human_reads = params.sra_human_db ? Channel.fromPath( params.sra_human_db ) : Channel.empty()
31+
ch_human_reads = human_db_path ? Channel.fromPath( human_db_path ) : Channel.empty()
2532

2633
SCRUB_HUMAN_READS(
2734
CLUMP_READS.out.combine(ch_human_reads)

0 commit comments

Comments
 (0)