Skip to content

Commit 8d80b89

Browse files
ybaah-biotechclaude
andcommitted
chore: add CLAUDE.md — persistent project context for Claude Code sessions
Captures data flow, phase completion status, critical conventions, BlastHit constructor pattern, test structure, and output file reference. Loaded automatically at the start of every new Claude Code session. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 674009a commit 8d80b89

1 file changed

Lines changed: 181 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project overview
6+
7+
eDNA metabarcoding pipeline — takes a FASTA file of environmental DNA sequences, runs BLAST alignment (web or local), resolves taxonomy using LCA logic, flags UK protected species, and outputs a species table, biodiversity metrics, and a regulatory PDF report. Built for environmental consultancies and UK regulators (Environment Agency, Natural England).
8+
9+
**Owner:** Yaw Baah — MSc Biotechnology (NTU), BSc Biology (Nottingham)
10+
**Repo:** https://github.com/ybaah-biotech/eDNA-sequence-analysis
11+
12+
---
13+
14+
## Common commands
15+
16+
```bash
17+
# Run full test suite
18+
python -m pytest tests/ -v
19+
20+
# Run single test file
21+
python -m pytest tests/test_parser.py -v
22+
23+
# Run single test by name
24+
python -m pytest tests/test_protected.py::TestCheckProtected::test_confirmed_exact_match -v
25+
26+
# Run with coverage
27+
python -m pytest tests/ --cov=src --cov-report=term-missing
28+
29+
# Offline demo (no BLAST install or network needed)
30+
python mock_run.py
31+
32+
# Pipeline — web BLAST
33+
python pipeline.py --fasta data/sample_sequences.fasta --email you@example.com --report --site "Site A" --analyst "Yaw Baah"
34+
35+
# Pipeline — local BLAST+
36+
python pipeline.py --fasta data/sample_sequences.fasta --local --db-path /data/blast/nt --threads 4 --report
37+
38+
# Download a local database (16S ~1GB, nt ~300GB)
39+
python scripts/setup_db.py --db 16S_ribosomal_RNA --dest /data/blast/
40+
41+
# Generate a learning module PDF
42+
python docs/modules/generate_module_01.py
43+
44+
# Check local BLAST database info
45+
blastdbcmd -db /path/to/database -info
46+
```
47+
48+
---
49+
50+
## Architecture
51+
52+
### Data flow
53+
54+
```
55+
FASTA file
56+
→ src/utils.py::load_fasta() {query_id: SeqRecord}
57+
→ src/blast_query.py OR local_blast.py XML files cached in output/xml/
58+
→ src/parser.py::parse_all_results() List[BlastHit]
59+
→ src/summarise.py::build_hit_table() DataFrame (+ LCA via resolve_taxonomy)
60+
→ src/protected.py::check_protected() DataFrame (+ protected_flag column)
61+
→ src/summarise.py::calculate_diversity() diversity dict
62+
→ src/summarise.py::export_results() blast_hit_table.csv, biodiversity_summary.csv
63+
→ src/report.py::generate_report() eDNA_Report.pdf
64+
```
65+
66+
### Phase completion status
67+
68+
| Phase | Module | Status |
69+
|-------|--------|--------|
70+
| 1 | `src/local_blast.py` — offline BLAST via subprocess, threading, DB version stamping | ✅ Complete |
71+
| 2 | `src/report.py` — 4-section regulatory PDF (cover, executive summary, species table, methodology) | ✅ Complete |
72+
| 3 | `src/protected.py` — 20 UK protected species, CONFIRMED/POSSIBLE alerts, PDF alert section | ✅ Complete |
73+
| 4 | Curated databases | 📋 Planned |
74+
| 5 | Multi-marker (COI/ITS/16S/18S) | 📋 Planned |
75+
| 6 | Rarefaction | 📋 Planned |
76+
| 7 | Beta diversity | 📋 Planned |
77+
| 8 | Cloud API | 📋 Planned |
78+
| 9 | ASV/DADA2 | 📋 Planned |
79+
| 10 | Nanopore streaming | 📋 Planned |
80+
81+
---
82+
83+
## Key source files
84+
85+
### src/parser.py
86+
- `BlastHit` dataclass — the core record type passed through the pipeline
87+
- `_extract_species(title)` — parses species name from a raw BLAST hit title. Uses `_SKIP_QUALIFIERS` (cf/aff/var/subsp), `_STOP_WORDS` (clone/isolate/strain), `_NON_SPECIFIC_TERMS` (uncultured/metagenome). Returns `"Genus sp."` or `"unclassified"`.
88+
- `parse_blast_xml(path)` — parses one XML file, selects best HSP via `max(hsps, key=lambda h: h.bits)`, clamps coverage to 100.0
89+
90+
### src/summarise.py
91+
- `resolve_taxonomy(df)` — LCA logic: ≥97% → species, 90–96% → genus consensus in identity window, <90% → genus consensus across all hits. Adds `resolved_species` column. Always call before diversity metrics.
92+
- `build_hit_table(hits)` — converts List[BlastHit] to DataFrame, adds `confidence_flag` (high/medium/low), calls resolve_taxonomy
93+
- `calculate_diversity(df)` — uses `resolved_species` column, excludes `"unclassified"`, returns Shannon H', Pielou J', species richness, counts
94+
95+
### src/protected.py
96+
- `PROTECTED_UK_SPECIES` — dict of 20 UK aquatic/riparian protected species (EPS, WCA Sch.5, S41, WCA Sch.6)
97+
- `PROTECTED_GENERA` — auto-built at import from PROTECTED_UK_SPECIES
98+
- `check_protected(df)` — adds `protected_flag` ("CONFIRMED"/"POSSIBLE"/None) to rank-1 rows only. CONFIRMED = exact species match. POSSIBLE = genus matches a genus containing protected species.
99+
- `get_alerts(df)` — returns `{"has_alerts": bool, "confirmed": [...], "possible": [...], "highest_alert_level": ...}`
100+
101+
### src/local_blast.py
102+
- `run_local_blast(sequences, xml_dir, db_path, ...)` — drop-in replacement for `run_blast_queries`. Caches XML per query_id. Uses `ThreadPoolExecutor` for parallel execution. Writes `db_version.json` via `stamp_db_version()`.
103+
- `_check_blast_installed(program)` — raises `RuntimeError` with install instructions if binary not found
104+
105+
### src/report.py
106+
- `generate_report(hit_table, diversity, output_path, ..., alerts=None)` — builds full PDF
107+
- Section order: cover page → `_protected_species_section` (only if alerts) → executive summary → species table → biodiversity metrics → methodology
108+
- Confidence colours: HIGH=green, MEDIUM=amber, LOW=red. LOW rows get red background in species table.
109+
110+
---
111+
112+
## Critical conventions
113+
114+
**Species naming:**
115+
- `"Genus sp."` — resolved to genus only (LCA downgrade or no epithet found)
116+
- `"unclassified"` — no meaningful taxonomic signal; excluded from all diversity calculations
117+
- Never use raw `species` column for diversity — always use `resolved_species`
118+
119+
**BlastHit constructor (positional — used in tests):**
120+
```python
121+
BlastHit(query_id, query_length, hit_rank, accession, species, description,
122+
identity_pct, evalue, bit_score, alignment_length, query_coverage_pct)
123+
```
124+
125+
**Identity thresholds (defined in summarise.py as `_CONF_HIGH=97.0`, `_CONF_MED=90.0`):**
126+
- ≥97% → species level (HIGH confidence)
127+
- 90–96% → genus level (MEDIUM confidence)
128+
- <90% → low confidence (still reported, excluded from diversity, flagged red)
129+
130+
**Never do:**
131+
- `alignment.hsps[0]` — always `max(hsps, key=lambda h: h.bits)`
132+
- Use `NCBIWWW.qblast` in new code — use `src/local_blast.py`
133+
- Exceed 100.0 for `query_coverage_pct` — clamp with `min(..., 100.0)`
134+
- Count `"unclassified"` as a taxon in diversity
135+
- Call `calculate_diversity()` before `resolve_taxonomy()`
136+
137+
---
138+
139+
## Testing
140+
141+
50 tests across two files. All must pass before any commit.
142+
143+
- `tests/test_parser.py` — 38 tests: species parsing (16), XML parsing (4), diversity metrics (8), stop words (4), LCA resolution (6)
144+
- `tests/test_protected.py` — 12 tests: check_protected (6), get_alerts (4), PROTECTED_GENERA structure (2)
145+
146+
Test DataFrames for protected tests are built directly from `pd.DataFrame` dicts — no BlastHit constructor needed.
147+
148+
---
149+
150+
## Outputs
151+
152+
| File | Description |
153+
|------|-------------|
154+
| `blast_hit_table.csv` | All hits with resolved_species, confidence_flag, protected_flag |
155+
| `biodiversity_summary.csv` | Shannon H', Pielou J', species richness, per-taxon counts |
156+
| `db_version.json` | Database title, version string, timestamp (local mode only) |
157+
| `eDNA_Report.pdf` | Regulatory PDF — generated with `--report` flag |
158+
159+
---
160+
161+
## Learning materials
162+
163+
`docs/modules/` — PDF learning modules (ReportLab generated):
164+
- Module 01: BLAST & Alignment
165+
- Module 02: Local BLAST & Databases
166+
- Module 03: Regulatory Reports & Interpretation
167+
- Module 04: Protected Species & UK Environmental Law
168+
169+
`notebooks/` — Jupyter notebooks (run in GitHub Codespaces):
170+
- 01: BLAST & Species Parsing (21 cells, fully offline)
171+
- 02: Local BLAST & Databases (16 cells, fully offline)
172+
173+
`docs/linkedin/` — LinkedIn post drafts for each completed phase.
174+
175+
---
176+
177+
## CI / devcontainer
178+
179+
- GitHub Actions: `.github/workflows/tests.yml` — runs pytest on push to main/dev, uploads coverage to Codecov
180+
- Devcontainer: `.devcontainer/` — Python 3.12, BLAST+ pre-installed via apt-get, runs test suite on create
181+
- Issue templates: `.github/ISSUE_TEMPLATE/bug.yml` and `feature.yml`

0 commit comments

Comments
 (0)