its-fun-2-map 1.0.0__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.
Files changed (37) hide show
  1. its_fun_2_map-1.0.0/LICENSE +21 -0
  2. its_fun_2_map-1.0.0/PKG-INFO +664 -0
  3. its_fun_2_map-1.0.0/README.md +632 -0
  4. its_fun_2_map-1.0.0/pyproject.toml +103 -0
  5. its_fun_2_map-1.0.0/setup.cfg +4 -0
  6. its_fun_2_map-1.0.0/src/its_fun_2_map/UNITEd.py +1323 -0
  7. its_fun_2_map-1.0.0/src/its_fun_2_map/__init__.py +17 -0
  8. its_fun_2_map-1.0.0/src/its_fun_2_map/assembly_module.py +514 -0
  9. its_fun_2_map-1.0.0/src/its_fun_2_map/blast_output_parser.py +1097 -0
  10. its_fun_2_map-1.0.0/src/its_fun_2_map/blast_round1.py +102 -0
  11. its_fun_2_map-1.0.0/src/its_fun_2_map/blast_round1_parser.py +818 -0
  12. its_fun_2_map-1.0.0/src/its_fun_2_map/blast_round2.py +160 -0
  13. its_fun_2_map-1.0.0/src/its_fun_2_map/fastp_module.py +556 -0
  14. its_fun_2_map-1.0.0/src/its_fun_2_map/its_a_summary_compiler.py +599 -0
  15. its_fun_2_map-1.0.0/src/its_fun_2_map/its_decision_making.R +159 -0
  16. its_fun_2_map-1.0.0/src/its_fun_2_map/its_fun_tools.py +148 -0
  17. its_fun_2_map-1.0.0/src/its_fun_2_map/its_primer_binding.py +576 -0
  18. its_fun_2_map-1.0.0/src/its_fun_2_map/mapping_module.py +584 -0
  19. its_fun_2_map-1.0.0/src/its_fun_2_map/parse_fastp_json.R +56 -0
  20. its_fun_2_map-1.0.0/src/its_fun_2_map/pull_ncbi_lineage.py +103 -0
  21. its_fun_2_map-1.0.0/src/its_fun_2_map.egg-info/PKG-INFO +664 -0
  22. its_fun_2_map-1.0.0/src/its_fun_2_map.egg-info/SOURCES.txt +35 -0
  23. its_fun_2_map-1.0.0/src/its_fun_2_map.egg-info/dependency_links.txt +1 -0
  24. its_fun_2_map-1.0.0/src/its_fun_2_map.egg-info/entry_points.txt +12 -0
  25. its_fun_2_map-1.0.0/src/its_fun_2_map.egg-info/requires.txt +10 -0
  26. its_fun_2_map-1.0.0/src/its_fun_2_map.egg-info/top_level.txt +1 -0
  27. its_fun_2_map-1.0.0/tests/test_assembly.py +45 -0
  28. its_fun_2_map-1.0.0/tests/test_blast_parsing.py +110 -0
  29. its_fun_2_map-1.0.0/tests/test_cli.py +61 -0
  30. its_fun_2_map-1.0.0/tests/test_fastp_args.py +42 -0
  31. its_fun_2_map-1.0.0/tests/test_mapping.py +45 -0
  32. its_fun_2_map-1.0.0/tests/test_package.py +46 -0
  33. its_fun_2_map-1.0.0/tests/test_primers.py +75 -0
  34. its_fun_2_map-1.0.0/tests/test_r_scripts.py +227 -0
  35. its_fun_2_map-1.0.0/tests/test_summary.py +42 -0
  36. its_fun_2_map-1.0.0/tests/test_tools.py +136 -0
  37. its_fun_2_map-1.0.0/tests/test_unite.py +195 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 The Trustees of the Natural History Museum, London
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,664 @@
1
+ Metadata-Version: 2.4
2
+ Name: its-fun-2-map
3
+ Version: 1.0.0
4
+ Summary: A pipeline for extracting and validating fungal ITS barcode sequences from museum specimen genome skims
5
+ Author: M. Kamouyiaros, D. Parsons
6
+ Maintainer: Natural History Museum, London
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/museomics/its-fun-2-map
9
+ Project-URL: Repository, https://github.com/museomics/its-fun-2-map
10
+ Project-URL: Issues, https://github.com/museomics/its-fun-2-map/issues
11
+ Keywords: bioinformatics,fungi,ITS,barcode,metabarcoding,genome-skimming,museomics
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: R
18
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
19
+ Requires-Python: >=3.12
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: biopython
23
+ Requires-Dist: openpyxl>=3.1
24
+ Requires-Dist: pandas
25
+ Requires-Dist: rpy2>=3.5
26
+ Requires-Dist: seqpy-tools>=0.1.2
27
+ Provides-Extra: dev
28
+ Requires-Dist: build; extra == "dev"
29
+ Requires-Dist: pytest>=8; extra == "dev"
30
+ Requires-Dist: twine; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # its-fun-2-map
34
+
35
+ A pipeline for processing fungal genome skims from museum specimens. Includes quality control, reference mapping (as a read-baiting step), assembly, BLAST taxonomic assignment, primer-based ITS region extraction, and validation.
36
+
37
+ ## Contents
38
+ 1. [Installation and quick start](#installation-and-quick-start)
39
+ 2. [Workflow](#workflow)
40
+ 3. [Configuration](#configuration)
41
+ 4. [Pipeline Overview](#pipeline-overview)
42
+ 5. [Output Directory Structure](#output-directory-structure)
43
+ 6. [Dependencies](#dependencies)
44
+ 7. [Authors](#authors)
45
+
46
+ ## Installation and quick start
47
+ Everything the pipeline needs (tools, R, Python and their packages) are installed by the conda environment in `its-fun-2-map.yaml`. The only things you need to fetch yourself are the reference databases.
48
+
49
+ **1. Install [conda](https://www.anaconda.com/docs/getting-started/miniconda/install#quickstart-install-instructions)** if you don't already have it.
50
+
51
+ **2. Clone the repository and create the environment:**
52
+ ```bash
53
+ git clone https://github.com/museomics/its-fun-2-map.git
54
+ cd its-fun-2-map
55
+ conda env create -f its-fun-2-map.yaml
56
+ conda activate its-fun-2-map
57
+ ```
58
+
59
+ **3. Install the pipeline package** into the activated environment:
60
+ ```bash
61
+ pip install -e .
62
+ ```
63
+ (or `pip install its-fun-2-map` to install from PyPI). The conda environment is still required either way, as the pipeline shells out to fastp, seqkit, bwa, samtools, BBMap, SPAdes, BLAST+ and R, none of which are pip-installable.
64
+
65
+ **4. Download the reference databases** from UNITE:
66
+ - [UNITE Full "UNITE+INSD"](https://unite.ut.ee/repository.php#panel6a) - reference sequence retrieval (Step 2)
67
+ - [UNITE General Release (sh_general_release_dynamic)](https://unite.ut.ee/repository.php#panel5a) - BLAST Round 1 (Step 5)
68
+ - [UNITE UCHIME ITS2 reference dataset](https://unite.ut.ee/repository.php#panel7a) - BLAST Round 2a (Step 6)
69
+ - [UNITE UCHIME ITS1 reference dataset](https://unite.ut.ee/repository.php#panel7a) - BLAST Round 2b (Step 7)
70
+
71
+ **5. Check the install:**
72
+ ```bash
73
+ itsfun-qc --help
74
+ ```
75
+
76
+ **6. Prepare a tracking sheet** (CSV or XLSX) with sample IDs and taxids - see [Tracking Sheet Format](#tracking-sheet-format).
77
+
78
+ **7. Run the pipeline.** All steps can be run sequentially with `run_pipeline.sh`: set your paths and parameters in its configurable parameters block, then run it. See the [tutorial](https://github.com/museomics/its-fun-2-map/blob/main/tutorial.md) for a worked example.
79
+
80
+ Full version details for every dependency are in [Dependencies](#dependencies).
81
+
82
+ ## Workflow
83
+ ![ITS-FUN-2-MAP pipeline flowchart](img/its-fun-2-map_flowchart.svg)
84
+
85
+ ## Configuration
86
+ ### Default Parameters
87
+
88
+ | Parameter | Default | Description |
89
+ |-----------|---------|-------------|
90
+ | `MIN_LENGTH` | 100 bp | Minimum sequence length for BLAST filtering |
91
+ | `MIN_PIDENT` | 85% | Minimum percent identity for BLAST hits |
92
+ | `EVALUE_CUTOFF` | 1e-5 | BLAST e-value threshold |
93
+ | `NUMBER_REFS` | 40 | Number of reference sequences to retrieve |
94
+ | `TAX_RANK` | genus | Taxonomic rank for reference retrieval |
95
+
96
+ ### Tracking Sheet Format
97
+ The pipeline accepts CSV or XLSX tracking sheets with the following columns:
98
+
99
+ **Required columns:**
100
+ - Sample ID column (configurable via `--column_name` or `--id_header`)
101
+ - Taxid column (for UNITEd step, configurable via `--taxid_header`)
102
+
103
+ **Optional but recommended columns:**
104
+ - `forward` / `fwd` - path to forward reads
105
+ - `reverse` / `rev` - path to reverse reads
106
+
107
+ If forward/reverse columns are present, file paths are read directly from the tracking sheet. Otherwise, files are searched for in the input directory using sample ID-based string matching.
108
+
109
+ **For taxonomy validation:**
110
+ - `Kingdom`, `Phylum`, `Class`, `Order`, `Family`, `Genus`, `Species` - taxonomic hierarchy columns
111
+
112
+ If your spreadsheet doesn't contain taxonomic hierarchy columns, you can use the helper command `itsfun-lineage`. This will take any spreadsheet (both CSV and XLSX allowed) and queries all taxonomic IDs within in using NCBI Entrez. It then appends the taxonomic hierarchy columns to the spreadsheet. Outputs a log file and a CSV to your working directory for downstream use.
113
+
114
+ If using the pipeline shell script, this can be opted in by changing "need_lineage" to "YES" in [Advanced Usage: Configurable paramaters](https://github.com/museomics/its-fun-2-map/blob/main/tutorial.md).
115
+
116
+ ```
117
+ ## Configurable parameters
118
+ # itsfun-refs parameters
119
+ need_lineage="NO" #change to "YES" to use itsfun-lineage
120
+
121
+ ```
122
+
123
+ ## Pipeline Overview
124
+ ```
125
+ Raw Reads → QC → Reference Retrieval → Read Mapping → Assembly → BLAST Taxonomy Validation → ITS Extraction
126
+ ```
127
+ **All pipeline steps can be run sequentially using the `run_pipeline.sh` script.**
128
+
129
+ Installing the package puts one command per pipeline step on your `PATH`, so steps can also be run individually from any directory:
130
+
131
+ | Command | Step | Module |
132
+ |---------|------|--------|
133
+ | `itsfun-qc` | 1 - Quality control | `fastp_module` |
134
+ | `itsfun-refs` | 2 - Pseudo-reference retrieval | `UNITEd` |
135
+ | `itsfun-map` | 3 - Read mapping & baiting | `mapping_module` |
136
+ | `itsfun-assemble` | 4 - Contig assembly | `assembly_module` |
137
+ | `itsfun-blast1` | 5 - BLASTn round 1 | `blast_round1` |
138
+ | `itsfun-blast1-parse` | 5.5 - Round 1 parsing & validation | `blast_round1_parser` |
139
+ | `itsfun-blast2` | 6, 7 - BLASTn round 2 (ITS2/ITS1) | `blast_round2` |
140
+ | `itsfun-blast-parse` | 6.5, 7.5 - Round 2 parsing & validation | `blast_output_parser` |
141
+ | `itsfun-extract` | 8 - ITS primer binding & extraction | `its_primer_binding` |
142
+ | `itsfun-summary` | 9 - Summary metrics aggregation | `its_a_summary_compiler` |
143
+ | `itsfun-lineage` | helper - add NCBI lineages to a tracking sheet | `pull_ncbi_lineage` |
144
+
145
+ Every command takes `--help`. Each is also runnable as a module, e.g.
146
+ `python -m its_fun_2_map.fastp_module --help`.
147
+
148
+ ---
149
+
150
+
151
+ ## Step 1: Quality Control (`itsfun-qc`)
152
+
153
+ Quality control processing of raw paired-end reads using fastp with a two-stage approach.
154
+
155
+ - **Input:** Raw paired-end sequencing reads
156
+ - **Output:**
157
+ - `{sample}_trimmed_1.fq`, `{sample}_trimmed_2.fq` - trimmed paired reads
158
+ - `{sample}_merged.fq` - merged overlapping reads
159
+ - `{sample}_unmerged_1.fq`, `{sample}_unmerged_2.fq` - reads that couldn't be merged
160
+ - `{sample}_trim.json`, `{sample}_merge.json` - QC metrics
161
+ - `{sample}_overlaps.html` - overlap distribution visualisation
162
+ - `fastp_summary.csv` - aggregated summary statistics
163
+ - **Features:**
164
+ - Adapter trimming and filtering (auto-detection for PE reads)
165
+ - Quality filtering (Q30 threshold by default, configurable via `--qualified_quality_phred`)
166
+ - Read merging (Minimum merged length: 30 bp)
167
+ - Read statistics generation
168
+
169
+ **Parameters:**
170
+ | Parameter | Default | Description |
171
+ |-----------|---------|-------------|
172
+ | `--qualified_quality_phred` | 30 | Phred score below which a base is counted as unqualified during trimming (fastp's `-q`). Note fastp's own default is 15 |
173
+ | `--threads` | 4 | Number of **samples** processed in parallel (i.e. concurrent fastp processes) |
174
+ | `--fastp_threads` | 3 | Worker threads used **within** each fastp process (fastp's `-w`). Matches fastp's own default |
175
+ | `--fastp_extra_args` | - | Additional arguments for the trimming fastp call, given as a **single quoted string** |
176
+
177
+ > **Note on threading.** `--threads` and `--fastp_threads` multiply. Total worker threads is approximately `--threads x --fastp_threads`, so the default `--threads 4` uses around 12 threads, not 4. The effective figure is written to the log at startup. On a scheduler, set `--threads $SLURM_CPUS_PER_TASK --fastp_threads 1` to make the CPU request and actual usage match.
178
+
179
+ > **Note on `--fastp_extra_args`.** This argument takes one quoted string:
180
+ > ```bash
181
+ > --fastp_extra_args "--trim_front1 10 --trim_front2 10"
182
+ > ```
183
+ > It cannot be used to set `-q`/`--qualified_quality_phred` or `-w`/`--thread`, which the module sets itself - use `--qualified_quality_phred` and `--fastp_threads` instead. Supplying either via `--fastp_extra_args` causes the run to exit with an error before processing begins.
184
+
185
+ ---
186
+
187
+ ## Step 2: Pseudo-Reference Sequence Retrieval (`itsfun-refs`)
188
+ Retrieves taxonomically relevant reference sequences from the UNITE database using NCBI taxonomy matching.
189
+
190
+ **Process:**
191
+ 1. Queries NCBI Entrez API for taxonomic lineage of each specimen
192
+ 2. Matches NCBI taxonomy against UNITE database taxonomy
193
+ 3. Extracts sequences based on user-specified parameters
194
+ 4. Supports fallback to higher taxonomic ranks if matches not found
195
+
196
+ **Key Features:**
197
+ - **Diversity mode:** Distributes sequences across child taxa, prioritising target species matches (up to 25% of requested sequences)
198
+ - **Traverse mode:** Searches up the taxonomic hierarchy to collect target number of sequences
199
+ - **Flexible matching:** Case-insensitive matching with normalisation for species name variations
200
+
201
+ - **Input:** Tracking sheet with specimen taxonomy
202
+ - **Output:**
203
+ - `{sample_id}_seed.fasta` - reference sequences for each specimen
204
+ - Summary CSV with retrieval statistics
205
+ - **Database:** [UNITE public (Full "UNITE+INSD") database](https://unite.ut.ee/repository.php#panel6a)
206
+
207
+ **Parameters:**
208
+ | Parameter | Default | Description |
209
+ |-----------|---------|-------------|
210
+ | `--tax_rank` | - | Taxonomic rank to search (species/genus/family/order/class/phylum) |
211
+ | `--number` | - | Number of reference sequences to retrieve |
212
+ | `--diversity` | False | Distribute sequences across child taxa |
213
+ | `--traverse` | - | Maximum rank to traverse up to if insufficient sequences found |
214
+ | `--all` | False | Retrieve all matching sequences |
215
+
216
+ ---
217
+
218
+ ## Step 3: Read Mapping & Baiting (`itsfun-map`)
219
+ Maps quality-filtered reads to retrieved reference sequences using BWA to enrich for target ITS regions. Supports both modern DNA (BWA-MEM) and ancient/historical DNA (BWA-ALN) alignment strategies.
220
+
221
+ **Alignment Algorithms:**
222
+ | Algorithm | Use Case | Parameters |
223
+ |-----------|----------|------------|
224
+ | `bwa-mem` | Modern/historical DNA | Default BWA-MEM settings with `-M` flag |
225
+ | `bwa-aln` | Ancient/historical DNA | `-l 16500 -n 0.01 -o 2` (Green et al. 2010) |
226
+
227
+ **Process:**
228
+ 1. Automatically indexes reference FASTA files if not already indexed
229
+ 2. Maps merged reads (single-end mode)
230
+ 3. Maps unmerged read pairs (paired-end mode)
231
+ 4. Extracts mapped reads to FASTQ format
232
+ 5. Repairs paired-end read files to ensure synchronisation
233
+ 6. Generates mapping statistics
234
+
235
+ **Parameters:**
236
+ | Parameter | Default | Description |
237
+ |-----------|---------|-------------|
238
+ | `--aligner` | - | BWA algorithm: `bwa-mem` or `bwa-aln` (required) |
239
+ | `--threads` | 4 | Number of **samples** mapped in parallel (i.e. concurrent BWA jobs) |
240
+ | `--bwa_threads` | 8 | Worker threads used **within** each BWA job (BWA's `-t`) |
241
+
242
+ > **Note on threading.** As in Step 1, `--threads` and `--bwa_threads` multiply. Total worker threads is approximately `--threads x --bwa_threads`, so the defaults use around 32 threads, not 4. On a scheduler, set `--threads $SLURM_CPUS_PER_TASK --bwa_threads 1` to make the CPU request and actual usage match.
243
+
244
+ **Input:**
245
+ - Quality-filtered FASTQ files from Step 1 (`*_merged.fq`, `*_unmerged_1.fq`, `*_unmerged_2.fq`)
246
+ - Per-sample reference FASTA files from Step 2 (`*_seed.fasta`)
247
+
248
+ **Output:**
249
+ - `{sample}_mapped.fastq` - mapped merged reads
250
+ - `{sample}_mapped_unmerged_1.fastq`, `{sample}_mapped_unmerged_2.fastq` - mapped paired reads (repaired)
251
+ - `{sample}_mapped_flagstats.txt` - alignment statistics for merged reads
252
+ - `{sample}_unmerged_1_flagstats.txt`, `{sample}_unmerged_2_flagstats.txt` - alignment statistics for paired reads
253
+ - `mapping_summary.csv` - aggregated mapping metrics for all samples
254
+
255
+ ---
256
+
257
+ ## Step 4: Contig Assembly (`itsfun-assemble`)
258
+ Assembles mapped reads into contigs using SPAdes with a multi-stage fallback strategy to maximise assembly success.
259
+
260
+ **Assembly Strategy:**
261
+ | Stage | Method | K-mer Sizes | Description |
262
+ |-------|--------|-------------|-------------|
263
+ | Initial | Single-reads (i.e. merged) | 21,33,55 | Uses mapped merged reads only |
264
+ | Fallback 1 | Single-reads (i.e. merged) | 21 | Retries with minimal k-mer if initial fails |
265
+ | Fallback 2 | Merged + Unmerged PE | 21 | Uses all available reads if Fallback 1 fails |
266
+
267
+ **Process:**
268
+ 1. Attempts assembly with merged reads using default k-mer sizes
269
+ 2. If assembly fails or produces no valid scaffolds, retries with k=21 only
270
+ 3. If still unsuccessful, includes unmerged paired reads in the assembly
271
+ 4. Calculates assembly metrics including N50, scaffold counts, and lengths
272
+
273
+ **Input:**
274
+ - Mapped merged reads from Step 3 (`*_mapped.fastq`)
275
+ - Original unmerged reads from Step 1 (`*_unmerged_1.fq`, `*_unmerged_2.fq`)
276
+
277
+ **Output:**
278
+ - `{sample}.spades.out/` - SPAdes output directory containing:
279
+ - `scaffolds.fasta` - assembled scaffolds
280
+ - Assembly graphs and logs
281
+ - `assembly_summary.csv` - metrics for all samples including:
282
+ - Assembly method used (INITIAL/FALLBACK1/FALLBACK2)
283
+ - Number of scaffolds
284
+ - Scaffold lengths
285
+ - Mean scaffold length
286
+ - N50 value
287
+
288
+ ---
289
+
290
+ ## Step 5: BLASTn - Round 1 (General Database) (`itsfun-blast1`)
291
+ Searches assembled scaffolds against the general UNITE database for initial taxonomic identification.
292
+
293
+ **Process:**
294
+ 1. Optionally creates BLAST database from reference FASTA
295
+ 2. Locates `scaffolds.fasta` files for each sample in assembly directories
296
+ 3. Runs BLASTn searches in parallel
297
+ 4. Outputs results in tabular format (outfmt 6)
298
+
299
+ **Input:**
300
+ - Assembled scaffolds from Step 4 (`*.spades.out/scaffolds.fasta`)
301
+ - UNITE general release database
302
+ - Tracking sheet with sample IDs
303
+
304
+ **Output:**
305
+ - `{sample}_blast.tsv` - BLAST results in tabular format with columns:
306
+ - qseqid, sseqid, pident, length, mismatch, gapopen, qstart, qend, sstart, send, evalue, bitscore
307
+
308
+ **Database:** UNITE general release (sh_general_release_dynamic)
309
+
310
+ ---
311
+
312
+ ## Step 5.5: BLASTn Round 1 Parsing & Taxonomic Validation (`itsfun-blast1-parse`)
313
+ Parses BLAST Round 1 output and validates taxonomic assignments against expected taxonomy with cascading taxonomic rank matching.
314
+
315
+ **Taxonomic Validation Strategy:**
316
+ The parser attempts to match BLAST hits against expected taxonomy using a cascading approach:
317
+ 1. **Species-level match** - checks if hits match expected species
318
+ 2. **Genus-level match** - if no species match, checks genus
319
+ 3. **Family-level match** - if no genus match, checks family
320
+ 4. If no matches at any level, the sample is marked as FAIL
321
+
322
+ **Filtering Criteria:**
323
+ | Parameter | Default | Description |
324
+ |-----------|---------|-------------|
325
+ | `--min_len` | 100 bp | Minimum alignment length |
326
+ | `--min_pident` | 85% | Minimum percent identity |
327
+ | `--evalue_cutoff` | 1e-5 | Maximum e-value threshold |
328
+
329
+ **Validation Outcomes:**
330
+ - **PASS**: Exactly one contig matches expected taxonomy after filtering
331
+ - **FAIL**: Zero contigs match, or multiple contigs remain after filtering
332
+
333
+ **Input:**
334
+ - BLAST TSV files from Step 5
335
+ - Taxonomy CSV with expected taxonomic assignments (must contain ID column and taxonomic hierarchy: Kingdom, Phylum, Class, Order, Family, Genus, Species)
336
+ - Assembly directory for contig extraction
337
+
338
+ **Output:**
339
+ - `blast_validation_summary.csv` - comprehensive validation results including:
340
+ - `ID` - sample identifier
341
+ - `blast_round1_found_taxon` - taxonomic level where match was found
342
+ - `blast_round1_expected_taxonomy` - full expected taxonomy string
343
+ - `blast_round1_n_contigs_in` - total contigs in assembly
344
+ - `blast_round1_n_contigs_hits` - contigs matching expected taxonomy
345
+ - `blast_round1_contigs` - contig IDs that matched
346
+ - `blast_round1_hit_taxonomy` - taxonomy of best BLAST hit
347
+ - `blast_round1_hit_coords` - alignment coordinates
348
+ - `blast_round1_correct_taxonomy` - PASS/FAIL status
349
+ - `filtered_{sample}_blast.tsv` - filtered BLAST results for passing samples
350
+ - `{sample}_parsed_contig.fasta` - extracted contig sequences for passing samples
351
+
352
+ ---
353
+
354
+ ## Step 6: BLASTn - Round 2a (ITS2-Specific) (`itsfun-blast2`)
355
+ Searches validated contigs against the UCHIME ITS2 reference database for region-specific validation. This step uses `seqkit grep` to extract only the contigs that passed Round 1 validation before running BLAST.
356
+
357
+ **Process:**
358
+ 1. Reads BLAST Round 1 TSV results to identify validated contig IDs
359
+ 2. Extracts unique contig headers from each sample's Round 1 results
360
+ 3. Uses `seqkit grep` to extract matching scaffolds from assembly FASTA files
361
+ 4. Runs BLASTn against ITS2-specific UCHIME database
362
+ 5. Processes samples in parallel
363
+
364
+ **Input:**
365
+ - Assembly scaffolds from Step 4 (`*.spades.out/scaffolds.fasta`)
366
+ - BLAST Round 1 TSV results from Steps 5/5.5
367
+ - UCHIME ITS2 reference database
368
+ - Tracking sheet with sample IDs
369
+
370
+ **Output:**
371
+ - `{sample}_blast.tsv` - ITS2-specific BLAST results
372
+ - Temporary matched scaffold FASTAs (cleaned up after processing)
373
+
374
+ **Database:** UCHIME ITS2 reference dataset
375
+
376
+ ---
377
+
378
+ ## Step 6.5: BLASTn Parsing & Validation (ITS2) (`itsfun-blast-parse`)
379
+ Parses ITS2-specific BLAST results and applies multi-tier taxonomic validation with cascading fallbacks from family to genus to species level.
380
+
381
+ **Multi-Tier Taxonomy Matching Logic:**
382
+ When expected family is present:
383
+ 1. **0 family matches** → FAIL
384
+ 2. **1 family match** → PASS
385
+ 3. **>1 family matches** → Check genus level:
386
+ - No expected genus → FAIL
387
+ - 1 genus match → PASS
388
+ - 0 or >1 genus matches → Check species level:
389
+ - No expected species → FAIL
390
+ - 1 species match → PASS
391
+ - 0 or >1 species matches → FAIL
392
+
393
+ When expected family is empty (fallback to genus):
394
+ 1. **0 genus matches** → FAIL
395
+ 2. **1 genus match** → PASS
396
+ 3. **>1 genus matches** → Check species level (same logic as above)
397
+
398
+ **Input:**
399
+ - ITS2 BLAST TSV files from Step 6
400
+ - Taxonomy CSV with expected taxonomic assignments
401
+ - Assembly directory for contig extraction
402
+
403
+ **Output:**
404
+ - `taxonomy_validation_summary.csv` - validation results including:
405
+ - `ID` - sample identifier
406
+ - `expected_family` - expected taxonomic family (or genus/species if family unavailable)
407
+ - `expected_taxonomy` - full expected taxonomy string
408
+ - `n_contigs_in` - total contigs in assembly
409
+ - `n_contigs_hits` - contigs matching expected taxonomy
410
+ - `contigs` - matching contig IDs
411
+ - `hit_taxonomy` - taxonomy from best BLAST hit
412
+ - `its_coords` - ITS region coordinates on contig
413
+ - `correct_taxonomy` - PASS/FAIL status
414
+ - `contig_path` - path to extracted contig FASTA
415
+ - `{sample}_parsed_contig.fasta` - extracted contig sequences for passing samples
416
+
417
+ ---
418
+
419
+ ## Step 7: BLASTn - Round 2b (ITS1-Specific) (`itsfun-blast2`)
420
+ Parallel analysis searching against UCHIME ITS1 reference database. Uses the same workflow as Step 6 but with the ITS1-specific database.
421
+
422
+ **Input:**
423
+ - Assembly scaffolds from Step 4
424
+ - BLAST Round 1 TSV results from Steps 5/5.5
425
+ - UCHIME ITS1 reference database
426
+ - Tracking sheet with sample IDs
427
+
428
+ **Output:**
429
+ - `{sample}_blast.tsv` - ITS1-specific BLAST results
430
+
431
+ **Database:** UCHIME ITS1 reference dataset
432
+
433
+ ---
434
+
435
+ ## Step 7.5: BLASTn Parsing & Validation (ITS1) (`itsfun-blast-parse`)
436
+ Parses ITS1-specific BLAST results using the same multi-tier taxonomic validation as Step 6.5.
437
+
438
+ **Input:**
439
+ - ITS1 BLAST TSV files from Step 7
440
+ - Taxonomy CSV with expected taxonomic assignments
441
+ - Assembly directory for contig extraction
442
+
443
+ **Output:**
444
+ - `taxonomy_validation_summary.csv` - ITS1 validation results (same format as Step 6.5)
445
+ - `{sample}_parsed_contig.fasta` - extracted contig sequences for passing samples
446
+
447
+ ---
448
+
449
+ ## Step 8: ITS Primer Binding & Extraction (`itsfun-extract`)
450
+ Identifies primer binding sites and extracts ITS sequences from validated contigs using `seqkit amplicon`. Uses standard ITS primers from White et al. (1990) by default, with support for custom primer sets.
451
+
452
+ **Default Primers ([White et al. 1990](https://www.sciencedirect.com/science/chapter/edited-volume/abs/pii/B9780123721808500421?via%3Dihub)):**
453
+ | Primer | Sequence (5' → 3') |
454
+ |--------|-------------------|
455
+ | ITS1 | `TCCGTAGGTGAACCTGCGG` |
456
+ | ITS2 | `GCTGCGTTCTTCATCGATGC` |
457
+ | ITS3 | `GCATCGATGAAGAACGCAGC` |
458
+ | ITS4 | `TCCTCCGCTTATTGATATGC` |
459
+
460
+ **Target Regions:**
461
+ | Region | Forward Primer | Reverse Primer | Description |
462
+ |--------|---------------|----------------|-------------|
463
+ | ITS1 | ITS1 | ITS2 | ITS1 spacer region |
464
+ | ITS2 | ITS3 | ITS4 | ITS2 spacer region |
465
+ | ITS_complete | ITS1 | ITS4 | Full ITS region (ITS1 + 5.8S + ITS2) |
466
+
467
+ **Process:**
468
+ 1. Reads sample IDs from tracking sheet
469
+ 2. Locates FASTA files for each sample
470
+ 3. Runs `seqkit amplicon` with each primer pair (max 2 mismatches, strict mode)
471
+ 4. Generates extraction summary statistics
472
+
473
+ **Input:**
474
+ - Validated contig FASTAs from Steps 6.5/7.5 (`*_parsed_contig.fasta`)
475
+ - Tracking sheet with sample IDs
476
+ - Optional: Custom primers TSV and regions TSV
477
+ **Output:**
478
+ - `ITS1/{sample}_ITS1.fa` - extracted ITS1 sequences
479
+ - `ITS2/{sample}_ITS2.fa` - extracted ITS2 sequences
480
+ - `ITS_complete/{sample}_ITS_complete.fa` - extracted complete ITS sequences
481
+ - `extraction_summary.csv` - extraction success per sample and region
482
+ - `summary_report.txt` - detailed extraction statistics
483
+
484
+ **Custom Primers TSV Format:**
485
+ A header row is required, with the columns `name` and `sequence` (tab-separated):
486
+ ```
487
+ name sequence
488
+ FWD1 ATCGATCGATCG
489
+ REV1 GCTAGCTAGCTA
490
+ ```
491
+
492
+ **Custom Regions TSV Format:**
493
+ A header row is required, with the columns `region`, `forward` and `reverse` (tab-separated). The `forward` and `reverse` values must match `name` entries in the primers TSV:
494
+ ```
495
+ region forward reverse
496
+ MyRegion FWD1 REV1
497
+ ```
498
+
499
+ `--primers_tsv` and `--regions_tsv` must be supplied together, or neither (to use the built-in ITS defaults).
500
+
501
+ > **Note on custom regions.** `summary_report.txt` reports per-region counts and the "no regions extracted" list using whatever region names your regions TSV defines. The ITS-specific breakdown - complete ITS, ITS1 only, ITS2 only is emitted only when the region set includes `ITS1`, `ITS2` and `ITS_complete`.
502
+
503
+ ---
504
+
505
+
506
+ ## Step 9: Summary Metrics Aggregation (`itsfun-summary`)
507
+ Compiles comprehensive summary statistics across all pipeline steps, performs contig analysis to resolve ITS1/ITS2 results, and prepares final output FASTAs for submission.
508
+
509
+ **Process:**
510
+ 1. **CSV Discovery:** Finds and merges all pipeline summary CSVs (fastp, UNITEd, mapping, assembly, BLAST, extraction)
511
+ 2. **Contig Analysis:** Compares ITS1 and ITS2 results to identify:
512
+ - Contigs with both ITS1 and ITS2 on the same scaffold
513
+ - Overlapping coordinate detection (potential assembly issues)
514
+ - Resolution when ITS1 and ITS2 are on different contigs
515
+ 3. **Decision Making:** Runs R-based decision logic (`its_decision_making.R`) to determine final outcomes
516
+ 4. **FASTA Preparation:** Renames and organises final FASTAs for submission. This includes new sequence headers (default header= `<ID>|<ITS1/ITS2/ITS_complete/putative_ITS_region>` for any passes)
517
+
518
+ **Contig Analysis Logic:**
519
+ - **Both ITS1 and ITS2 PASS on same contig:** Check for coordinate overlap
520
+ - Non-overlapping → PASS (contiguous)
521
+ - Overlapping → WARNING (potential issue)
522
+ - **Both PASS on different contigs:** Default to ITS2 contig
523
+ - **Only ITS1 PASS:** Use ITS1 contig
524
+ - **Only ITS2 PASS:** Use ITS2 contig
525
+ - **Both FAIL:** Mark as failed
526
+
527
+ **Input:**
528
+ - All pipeline summary CSV files from previous steps
529
+ - Optional: Naming TSV for custom FASTA header formatting
530
+
531
+ **Output:**
532
+ - `final_results_dir/merged_summary_{date}.csv` - comprehensive merged summary with columns:
533
+ - UNITEd metrics (reference retrieval)
534
+ - Trimming/merging metrics (fastp)
535
+ - Assembly metrics (SPAdes)
536
+ - BLAST Round 1 results
537
+ - ITS1 and ITS2 BLAST results
538
+ - Extraction results
539
+ - Mapping statistics
540
+ - Contig analysis results (overlapping_coords, same_contig)
541
+ - Final decision (Final_outcome, Final_contig_desc, Final_contig)
542
+ - `final_results_dir/pass_fastas/` - renamed FASTAs for samples that passed all validation
543
+ - `final_results_dir/manual_verification/` - FASTAs requiring manual review
544
+
545
+ **Naming TSV Format (optional):**
546
+ ```
547
+ SAMPLE001 Genus_species_voucher123
548
+ SAMPLE002 Genus_species_voucher456
549
+ ```
550
+
551
+ ---
552
+
553
+ ## Output Directory Structure
554
+ ```
555
+ output_base/
556
+ ├── 00_logs/ # Detailed logs for each step
557
+ ├── 01_fastp_processed/ # Step 1: Quality-filtered reads
558
+ │ ├── {sample}_trimmed_1.fq
559
+ │ ├── {sample}_trimmed_2.fq
560
+ │ ├── {sample}_merged.fq
561
+ │ ├── {sample}_unmerged_1.fq
562
+ │ ├── {sample}_unmerged_2.fq
563
+ │ ├── {sample}_trim.json
564
+ │ ├── {sample}_merge.json
565
+ │ ├── {sample}_overlaps.html
566
+ │ ├── fastp_summary.out (long-formatted version of fastp_summary.csv)
567
+ │ ├── fastp_summary.csv
568
+ │ ├── trimmed_json_summary.csv
569
+ │ └── merged_json_summary.csv
570
+ ├── 02_UNITEd/ # Step 2: Retrieved reference sequences
571
+ │ ├── {sample}_seed.fasta
572
+ │ └── UNITEd_summary.csv
573
+ ├── 03_mapped_reads/ # Step 3: Reads mapped to references
574
+ │ ├── {sample}_mapped.fastq
575
+ │ ├── {sample}_mapped_unmerged_1.fastq
576
+ │ ├── {sample}_mapped_unmerged_2.fastq
577
+ │ ├── {sample}_mapped_flagstats.txt
578
+ │ ├── {sample}_unmerged_1_flagstats.txt
579
+ │ ├── {sample}_unmerged_2_flagstats.txt
580
+ │ └── mapping_summary.csv
581
+ ├── 04_assemblies/ # Step 4: Assembled contigs
582
+ │ ├── {sample}.spades.out/
583
+ │ │ ├── scaffolds.fasta
584
+ │ │ ├── contigs.fasta
585
+ │ │ └── ...
586
+ │ └── assembly_summary.csv
587
+ ├── 05_blast_round1/ # Step 5: Initial BLAST results
588
+ │ └── {sample}_blast_results.tsv
589
+ ├── 05a_blast_parsed1/ # Step 5.5: Filtered and validated results
590
+ │ ├── blast_validation_summary.csv
591
+ │ ├── filtered_{sample}_blast_results.tsv
592
+ │ └── {sample}_parsed_contig.fasta
593
+ ├── 06_blast_round2a-ITS2/ # Step 6: ITS2-specific BLAST results
594
+ │ └── {sample}_blast_results.tsv
595
+ ├── 06a_blast_parsed2a-ITS2/ # Step 6.5: Validated ITS2 results
596
+ │ ├── blast_parser_its2_summary.csv
597
+ │ ├── {sample}_blast_results-all_hits.tsv
598
+ │ └── {sample}_parsed_contig.fasta
599
+ ├── 07_blast_round2b-ITS1/ # Step 7: ITS1-specific BLAST results
600
+ │ └── {sample}_blast_results.tsv
601
+ ├── 07a_blast_parsed2b-ITS1/ # Step 7.5: Validated ITS1 results
602
+ │ ├── blast_parser_its1_summary.csv
603
+ │ ├── {sample}_blast_results-all_hits.tsv
604
+ │ └── {sample}_parsed_contig.fasta
605
+ ├── 08_its_primer_extraction/ # Step 8: Extracted ITS sequences
606
+ │ ├── ITS1/
607
+ │ │ └── {sample}_ITS1.fa
608
+ │ ├── ITS2/
609
+ │ │ └── {sample}_ITS2.fa
610
+ │ ├── ITS_complete/
611
+ │ │ └── {sample}_ITS_complete.fa
612
+ │ ├── extraction_summary.csv
613
+ │ └── summary_report.txt
614
+ └── final_results_dir/ # Step 9: Final aggregated outputs
615
+ ├── merged_summary_{date}.csv
616
+ ├── pass_fastas/
617
+ │ └── {sample}_{description}.fasta
618
+ └── manual_verification/
619
+ └── {sample}_manual_verification_needed.fasta
620
+ ```
621
+
622
+ ## Dependencies
623
+ All of the dependencies below are installed by the conda environment (`its-fun-2-map.yaml`) and the `its-fun-2-map` package - see [Installation and quick start](#installation-and-quick-start). They are listed here for reference.
624
+
625
+ ### Languages
626
+ - r-base 4.5.2 - keep on 4.5.x; rpy2's conda build encodes the R series, so bumping R alone leaves rpy2 with no matching build
627
+ - python 3.14 - minimum supported is 3.12, which `seqpy-tools` requires
628
+
629
+ ### Conda packages
630
+ | Package | Version |
631
+ |----------|----------------------|
632
+ | fastp | 1.0.1 |
633
+ | seqkit | 2.12.0 |
634
+ | bwa | 0.7.19 |
635
+ | samtools | 1.24 |
636
+ | bbmap | 40.02 |
637
+ | openjdk | 21 |
638
+ | spades | 4.2.0 |
639
+ | blast | 2.17.0 |
640
+
641
+ BBMap supplies `repair.sh`, which `itsfun-map` uses to resynchronise mapped read
642
+ pairs in Step 3; `openjdk` is its Java runtime. `htslib` is no longer listed - it is
643
+ pulled in automatically as a library dependency of `samtools`.
644
+
645
+ ### Python packages (not built-in)
646
+ | Package | Version |
647
+ |-----------|----------------------|
648
+ | pandas | 2.3.3 |
649
+ | numpy | 2.4.1 |
650
+ | biopython | 1.86 |
651
+ | rpy2 | 3.6.4 |
652
+ | scipy | 1.18.0 |
653
+ | openpyxl | 3.1.5 |
654
+ | pgzip | 0.4.0 |
655
+
656
+ ### R packages
657
+ - [jsonlite](https://cran.r-project.org/web/packages/jsonlite/index.html) 2.0.0 - used by `parse_fastp_json.R`; `its_decision_making.R` needs only base R
658
+
659
+ ### Custom tool packages
660
+ - `its_fun_2_map.its_fun_tools` - shared helpers, installed as part of the `its-fun-2-map` package. No `PYTHONPATH` fiddling and no need to run from the repository directory.
661
+ - [seqpy-tools](https://github.com/museomics/seqpy_tools) 0.1.0 ([available via pip](https://pypi.org/project/seqpy-tools/)) - imported by every module in the pipeline
662
+
663
+ ## Authors
664
+ **M. Kamouyiaros & D. Parsons** @ the Natural History Museum, London (NHMUK)