samplesheet-parser 0.1.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.
- samplesheet_parser-0.1.0/.github/workflows/ci.yml +64 -0
- samplesheet_parser-0.1.0/.gitignore +40 -0
- samplesheet_parser-0.1.0/LICENSE +19 -0
- samplesheet_parser-0.1.0/PKG-INFO +384 -0
- samplesheet_parser-0.1.0/README.md +334 -0
- samplesheet_parser-0.1.0/examples/parse_examples.py +114 -0
- samplesheet_parser-0.1.0/examples/sample_sheets/README.md +92 -0
- samplesheet_parser-0.1.0/examples/sample_sheets/v1_dual_index.csv +27 -0
- samplesheet_parser-0.1.0/examples/sample_sheets/v1_multi_lane.csv +31 -0
- samplesheet_parser-0.1.0/examples/sample_sheets/v1_single_index.csv +25 -0
- samplesheet_parser-0.1.0/examples/sample_sheets/v2_nextseq_single_index.csv +25 -0
- samplesheet_parser-0.1.0/examples/sample_sheets/v2_novaseq_x_dual_index.csv +24 -0
- samplesheet_parser-0.1.0/examples/sample_sheets/v2_with_index_umi.csv +28 -0
- samplesheet_parser-0.1.0/examples/sample_sheets/v2_with_read_umi.csv +24 -0
- samplesheet_parser-0.1.0/pyproject.toml +73 -0
- samplesheet_parser-0.1.0/samplesheet_parser/__init__.py +50 -0
- samplesheet_parser-0.1.0/samplesheet_parser/enums.py +54 -0
- samplesheet_parser-0.1.0/samplesheet_parser/factory.py +240 -0
- samplesheet_parser-0.1.0/samplesheet_parser/parsers/__init__.py +6 -0
- samplesheet_parser-0.1.0/samplesheet_parser/parsers/v1.py +536 -0
- samplesheet_parser-0.1.0/samplesheet_parser/parsers/v2.py +622 -0
- samplesheet_parser-0.1.0/samplesheet_parser/validators.py +323 -0
- samplesheet_parser-0.1.0/tests/__init__.py +0 -0
- samplesheet_parser-0.1.0/tests/conftest.py +291 -0
- samplesheet_parser-0.1.0/tests/test_factory.py +106 -0
- samplesheet_parser-0.1.0/tests/test_parsers/__init__.py +0 -0
- samplesheet_parser-0.1.0/tests/test_parsers/test_v1.py +236 -0
- samplesheet_parser-0.1.0/tests/test_parsers/test_v2.py +183 -0
- samplesheet_parser-0.1.0/tests/test_validators/__init__.py +0 -0
- samplesheet_parser-0.1.0/tests/test_validators/test_validators.py +205 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: pip install -e ".[dev]"
|
|
26
|
+
|
|
27
|
+
- name: Lint with ruff
|
|
28
|
+
run: ruff check samplesheet_parser/
|
|
29
|
+
|
|
30
|
+
- name: Type check with mypy
|
|
31
|
+
run: mypy samplesheet_parser/ --ignore-missing-imports
|
|
32
|
+
continue-on-error: true
|
|
33
|
+
|
|
34
|
+
- name: Run tests
|
|
35
|
+
run: pytest tests/ -v --cov=samplesheet_parser --cov-report=xml
|
|
36
|
+
|
|
37
|
+
- name: Upload coverage to Codecov
|
|
38
|
+
uses: codecov/codecov-action@v4
|
|
39
|
+
if: matrix.python-version == '3.12'
|
|
40
|
+
with:
|
|
41
|
+
file: ./coverage.xml
|
|
42
|
+
|
|
43
|
+
publish:
|
|
44
|
+
needs: test
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
47
|
+
permissions:
|
|
48
|
+
id-token: write
|
|
49
|
+
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
|
|
53
|
+
- name: Set up Python
|
|
54
|
+
uses: actions/setup-python@v5
|
|
55
|
+
with:
|
|
56
|
+
python-version: "3.12"
|
|
57
|
+
|
|
58
|
+
- name: Build package
|
|
59
|
+
run: |
|
|
60
|
+
pip install hatchling
|
|
61
|
+
python -m hatchling build
|
|
62
|
+
|
|
63
|
+
- name: Publish to PyPI
|
|
64
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
BLOGPOST.md
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.Python
|
|
7
|
+
*.egg-info/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
.eggs/
|
|
11
|
+
*.egg
|
|
12
|
+
|
|
13
|
+
# Testing
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.coverage
|
|
16
|
+
coverage.xml
|
|
17
|
+
htmlcov/
|
|
18
|
+
.tox/
|
|
19
|
+
|
|
20
|
+
# Type checking
|
|
21
|
+
.mypy_cache/
|
|
22
|
+
|
|
23
|
+
# Virtual environments
|
|
24
|
+
.venv/
|
|
25
|
+
venv/
|
|
26
|
+
env/
|
|
27
|
+
|
|
28
|
+
# Editor
|
|
29
|
+
.vscode/
|
|
30
|
+
.idea/
|
|
31
|
+
*.swp
|
|
32
|
+
*.swo
|
|
33
|
+
|
|
34
|
+
# macOS
|
|
35
|
+
.DS_Store
|
|
36
|
+
|
|
37
|
+
# Backup files created by parser cleaning
|
|
38
|
+
*.backup
|
|
39
|
+
*.tmp
|
|
40
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
Copyright 2026 Chaitanya Kasaraneni
|
|
8
|
+
|
|
9
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
you may not use this file except in compliance with the License.
|
|
11
|
+
You may obtain a copy of the License at
|
|
12
|
+
|
|
13
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
|
|
15
|
+
Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
See the License for the specific language governing permissions and
|
|
19
|
+
limitations under the License.
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: samplesheet-parser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Format-agnostic parser for Illumina SampleSheet.csv files — supports IEM V1 and BCLConvert V2
|
|
5
|
+
Project-URL: Homepage, https://github.com/chaitanyakasaraneni/samplesheet-parser
|
|
6
|
+
Project-URL: Documentation, https://illumina-samplesheet.readthedocs.io
|
|
7
|
+
Project-URL: Repository, https://github.com/chaitanyakasaraneni/samplesheet-parser
|
|
8
|
+
Project-URL: Issues, https://github.com/chaitanyakasaraneni/samplesheet-parser/issues
|
|
9
|
+
Author-email: Chaitanya Kasaraneni <kc.kasaraneni@gmail.com>
|
|
10
|
+
License: Apache License
|
|
11
|
+
Version 2.0, January 2004
|
|
12
|
+
http://www.apache.org/licenses/
|
|
13
|
+
|
|
14
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
15
|
+
|
|
16
|
+
Copyright 2026 Chaitanya Kasaraneni
|
|
17
|
+
|
|
18
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
19
|
+
you may not use this file except in compliance with the License.
|
|
20
|
+
You may obtain a copy of the License at
|
|
21
|
+
|
|
22
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
23
|
+
|
|
24
|
+
Unless required by applicable law or agreed to in writing, software
|
|
25
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
26
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
27
|
+
See the License for the specific language governing permissions and
|
|
28
|
+
limitations under the License.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: bcl2fastq,bclconvert,bioinformatics,demultiplexing,genomics,illumina,ngs,samplesheet,sequencing
|
|
31
|
+
Classifier: Development Status :: 3 - Alpha
|
|
32
|
+
Classifier: Intended Audience :: Developers
|
|
33
|
+
Classifier: Intended Audience :: Science/Research
|
|
34
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
35
|
+
Classifier: Programming Language :: Python :: 3
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
40
|
+
Classifier: Typing :: Typed
|
|
41
|
+
Requires-Python: >=3.10
|
|
42
|
+
Requires-Dist: loguru>=0.7
|
|
43
|
+
Provides-Extra: dev
|
|
44
|
+
Requires-Dist: black>=24.0; extra == 'dev'
|
|
45
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
46
|
+
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
|
|
47
|
+
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
48
|
+
Requires-Dist: ruff>=0.3; extra == 'dev'
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
# samplesheet-parser
|
|
52
|
+
|
|
53
|
+
**Format-agnostic Python parser for Illumina `SampleSheet.csv` files.**
|
|
54
|
+
|
|
55
|
+
Supports IEM V1 (bcl2fastq / NovaSeq 6000 era) and BCLConvert V2 (NovaSeq X series) with automatic format detection, index validation, and `OverrideCycles` / UMI decoding — no format hints required from the caller.
|
|
56
|
+
|
|
57
|
+
[](https://badge.fury.io/py/samplesheet-parser)
|
|
58
|
+
[](https://www.python.org/downloads/)
|
|
59
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
60
|
+
[](https://github.com/chaitanyakasaraneni/samplesheet-parser/actions)
|
|
61
|
+
[](https://codecov.io/gh/chaitanyakasaraneni/samplesheet-parser)
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## The problem
|
|
66
|
+
|
|
67
|
+
Labs running mixed instrument fleets — NovaSeq 6000 alongside NovaSeq X series — produce two structurally incompatible `SampleSheet.csv` formats:
|
|
68
|
+
|
|
69
|
+
| | IEM V1 | BCLConvert V2 |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| Discriminator | `IEMFileVersion` in `[Header]` | `FileFormatVersion` in `[Header]` |
|
|
72
|
+
| Data section | `[Data]` | `[BCLConvert_Data]` |
|
|
73
|
+
| Settings section | `[Settings]` | `[BCLConvert_Settings]` |
|
|
74
|
+
| Index columns | `index`, `index2` (lowercase) | `Index`, `Index2` (uppercase) |
|
|
75
|
+
| Read cycles | Bare integers | Key-value (`Read1Cycles,151`) |
|
|
76
|
+
| UMI encoding | Not supported | `OverrideCycles` string |
|
|
77
|
+
| Used with | bcl2fastq | BCLConvert ≥ 3.x |
|
|
78
|
+
|
|
79
|
+
Without a single parser, every pipeline component that reads a SampleSheet needs an `if v1 else v2` branch — or worse, the format is hardcoded and the wrong sheet is silently processed.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Installation
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pip install samplesheet-parser
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Requires Python 3.10+. The only mandatory dependency is [`loguru`](https://github.com/Delgan/loguru).
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Quickstart
|
|
94
|
+
|
|
95
|
+
### Auto-detect format (recommended)
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from samplesheet_parser import SampleSheetFactory
|
|
99
|
+
|
|
100
|
+
factory = SampleSheetFactory()
|
|
101
|
+
sheet = factory.create_parser("SampleSheet.csv", parse=True)
|
|
102
|
+
|
|
103
|
+
print(factory.version) # SampleSheetVersion.V1 or .V2
|
|
104
|
+
print(sheet.index_type()) # "dual", "single", or "none"
|
|
105
|
+
print(factory.get_umi_length()) # 0 if no UMI
|
|
106
|
+
|
|
107
|
+
for sample in sheet.samples():
|
|
108
|
+
print(sample["sample_id"], sample["index"])
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Validate before demultiplexing
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from samplesheet_parser import SampleSheetFactory, SampleSheetValidator
|
|
115
|
+
|
|
116
|
+
sheet = SampleSheetFactory().create_parser("SampleSheet.csv", parse=True)
|
|
117
|
+
result = SampleSheetValidator().validate(sheet)
|
|
118
|
+
|
|
119
|
+
print(result.summary())
|
|
120
|
+
# PASS — 0 error(s), 1 warning(s)
|
|
121
|
+
|
|
122
|
+
for err in result.errors:
|
|
123
|
+
print(err)
|
|
124
|
+
# [ERROR] DUPLICATE_INDEX: Index 'ATTACTCG+TATAGCCT' appears more than once in lane 1.
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### UMI extraction (V2 only)
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from samplesheet_parser import SampleSheetV2
|
|
131
|
+
|
|
132
|
+
sheet = SampleSheetV2("SampleSheet.csv", parse=True)
|
|
133
|
+
|
|
134
|
+
# OverrideCycles: Y151;I10U9;I10;Y151 → 9 bp UMI in Index1
|
|
135
|
+
print(sheet.get_umi_length()) # 9
|
|
136
|
+
rs = sheet.get_read_structure()
|
|
137
|
+
print(rs.umi_location) # "index2"
|
|
138
|
+
print(rs.read_structure) # {"read1_template": 151, "index2_length": 10, "index2_umi": 9, ...}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Use parsers directly
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from samplesheet_parser import SampleSheetV1, SampleSheetV2
|
|
145
|
+
|
|
146
|
+
# V1
|
|
147
|
+
v1 = SampleSheetV1("SampleSheet_v1.csv", parse=True)
|
|
148
|
+
print(v1.experiment_name) # "240115_A01234_0042_AHJLG7DRXX"
|
|
149
|
+
print(v1.instrument_type) # "NovaSeq 6000"
|
|
150
|
+
print(v1.adapter_read1) # "AGATCGGAAGAGCACACGTCTGAACTCCAGTCA"
|
|
151
|
+
print(v1.adapter_read2) # "AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT"
|
|
152
|
+
print(v1.reverse_complement) # 0
|
|
153
|
+
print(v1.read_lengths) # [151, 151]
|
|
154
|
+
|
|
155
|
+
# V2
|
|
156
|
+
v2 = SampleSheetV2("SampleSheet_v2.csv", parse=True)
|
|
157
|
+
print(v2.instrument_platform) # "NovaSeqXSeries"
|
|
158
|
+
print(v2.software_version) # "3.9.3"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Format detection logic
|
|
164
|
+
|
|
165
|
+
The factory uses a three-step strategy — stopping as early as possible:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
1. Scan [Header] for FileFormatVersion → V2
|
|
169
|
+
or IEMFileVersion → V1
|
|
170
|
+
|
|
171
|
+
2. If undetermined: scan full file for
|
|
172
|
+
[BCLConvert_Settings] or
|
|
173
|
+
[BCLConvert_Data] → V2
|
|
174
|
+
|
|
175
|
+
3. Default → V1
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The file is read only once. No second `open()`, no `seek()`.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## OverrideCycles decoding
|
|
183
|
+
|
|
184
|
+
The V2 `OverrideCycles` string encodes the full read structure using single-letter type codes:
|
|
185
|
+
|
|
186
|
+
| Code | Meaning |
|
|
187
|
+
|---|---|
|
|
188
|
+
| `Y` | Template (sequenced) bases |
|
|
189
|
+
| `I` | Index bases |
|
|
190
|
+
| `U` | UMI bases |
|
|
191
|
+
| `N` | Masked / skipped bases |
|
|
192
|
+
|
|
193
|
+
Segment order: `Read1 ; Index1 ; Index2 ; Read2`
|
|
194
|
+
|
|
195
|
+
| OverrideCycles | UMI length | UMI location |
|
|
196
|
+
|---|---|---|
|
|
197
|
+
| `Y151;I10;I10;Y151` | 0 | — |
|
|
198
|
+
| `Y151;I10U9;I10;Y151` | 9 bp | Index1 |
|
|
199
|
+
| `U5Y146;I8;I8;U5Y146` | 5 bp | Read1 + Read2 |
|
|
200
|
+
| `Y76;I8;Y76` | 0 | — (single-index) |
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Validation checks
|
|
205
|
+
|
|
206
|
+
| Code | Level | Condition |
|
|
207
|
+
|---|---|---|
|
|
208
|
+
| `EMPTY_SAMPLES` | error | No samples found in data section |
|
|
209
|
+
| `INVALID_INDEX_CHARS` | error | Index contains non-ACGTN characters |
|
|
210
|
+
| `INDEX_TOO_LONG` | error | Index longer than 24 bp |
|
|
211
|
+
| `DUPLICATE_INDEX` | error | Two samples share an index in the same lane |
|
|
212
|
+
| `DUPLICATE_SAMPLE_ID` | error | Same `Sample_ID` appears twice in one lane |
|
|
213
|
+
| `INDEX_TOO_SHORT` | warning | Index shorter than 6 bp |
|
|
214
|
+
| `NO_ADAPTERS` | warning | No adapter sequences configured |
|
|
215
|
+
| `ADAPTER_MISMATCH` | warning | Adapter is not a standard Illumina sequence |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## API reference
|
|
220
|
+
|
|
221
|
+
### `SampleSheetFactory`
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
factory = SampleSheetFactory()
|
|
225
|
+
sheet = factory.create_parser(path, *, clean=True, experiment_id=None, parse=None)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
| Attribute / Method | Returns | Description |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| `.create_parser(path, ...)` | `SampleSheetV1 \| SampleSheetV2` | Auto-detect format and return parser |
|
|
231
|
+
| `.get_umi_length()` | `int` | UMI length from current parser |
|
|
232
|
+
| `.version` | `SampleSheetVersion` | Detected version after `create_parser()` |
|
|
233
|
+
|
|
234
|
+
### Shared interface — `SampleSheetV1` and `SampleSheetV2`
|
|
235
|
+
|
|
236
|
+
| Method / Attribute | Returns | Description |
|
|
237
|
+
|---|---|---|
|
|
238
|
+
| `.parse(do_clean=True)` | `None` | Parse all sections |
|
|
239
|
+
| `.samples()` | `list[dict]` | One record per unique sample |
|
|
240
|
+
| `.index_type()` | `str` | `"dual"`, `"single"`, or `"none"` |
|
|
241
|
+
| `.adapters` | `list[str]` | All configured adapter sequences |
|
|
242
|
+
| `.experiment_name` | `str \| None` | Run or experiment name |
|
|
243
|
+
| `.read_lengths` / `.reads` | `list[int]` / `dict` | Read cycle lengths |
|
|
244
|
+
|
|
245
|
+
### V1-specific
|
|
246
|
+
|
|
247
|
+
| Attribute | Type | Description |
|
|
248
|
+
|---|---|---|
|
|
249
|
+
| `.iem_version` | `str \| None` | e.g. `"5"` |
|
|
250
|
+
| `.instrument_type` | `str \| None` | e.g. `"NovaSeq 6000"`, `"MiSeq"` |
|
|
251
|
+
| `.application` | `str \| None` | e.g. `"FASTQ Only"` |
|
|
252
|
+
| `.assay` | `str \| None` | Library prep kit name |
|
|
253
|
+
| `.index_adapters` | `str \| None` | Illumina index set name |
|
|
254
|
+
| `.chemistry` | `str \| None` | `"Amplicon"` = dual index, `"Default"` = single/no index |
|
|
255
|
+
| `.adapter_read1` | `str` | Read 1 adapter (`Adapter` or `AdapterRead1` key) |
|
|
256
|
+
| `.adapter_read2` | `str` | Read 2 adapter (`AdapterRead2` key) |
|
|
257
|
+
| `.reverse_complement` | `int` | `0` = default, `1` = reverse-complement R2 (Nextera MP only) |
|
|
258
|
+
| `.flowcell_id` | `str \| None` | Parsed from experiment ID run folder name |
|
|
259
|
+
|
|
260
|
+
### V2-specific
|
|
261
|
+
|
|
262
|
+
| Method / Attribute | Returns | Description |
|
|
263
|
+
|---|---|---|
|
|
264
|
+
| `.get_umi_length()` | `int` | UMI length from `OverrideCycles` |
|
|
265
|
+
| `.get_read_structure()` | `ReadStructure` | Full decoded read structure |
|
|
266
|
+
| `.instrument_platform` | `str \| None` | e.g. `"NovaSeqXSeries"` |
|
|
267
|
+
| `.software_version` | `str \| None` | BCLConvert version string |
|
|
268
|
+
| `.custom_fields` | `dict[str, set[str]]` | Non-standard fields by section |
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Example sample sheets
|
|
273
|
+
|
|
274
|
+
The [`examples/sample_sheets/`](examples/sample_sheets/) directory contains ready-to-use reference sheets for every supported configuration:
|
|
275
|
+
|
|
276
|
+
| File | Format | Instrument | UMI | Use case |
|
|
277
|
+
|---|---|---|---|---|
|
|
278
|
+
| `v1_dual_index.csv` | V1 | NovaSeq 6000 | No | Standard WGS, multi-lane |
|
|
279
|
+
| `v1_single_index.csv` | V1 | NextSeq 500 | No | Small RNA |
|
|
280
|
+
| `v1_multi_lane.csv` | V1 | NovaSeq 6000 | No | 4 lanes, mixed projects |
|
|
281
|
+
| `v2_novaseq_x_dual_index.csv` | V2 | NovaSeq X | No | Standard PE150 |
|
|
282
|
+
| `v2_with_index_umi.csv` | V2 | NovaSeq X | Index1 UMI (9 bp) | cfDNA / liquid biopsy |
|
|
283
|
+
| `v2_with_read_umi.csv` | V2 | NovaSeq X | Read UMI (5 bp) | Duplex sequencing |
|
|
284
|
+
| `v2_nextseq_single_index.csv` | V2 | NextSeq 1000/2000 | No | Amplicon panel |
|
|
285
|
+
|
|
286
|
+
Run the demo to parse all of them:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
python examples/parse_examples.py
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## V1 adapter key reference
|
|
295
|
+
|
|
296
|
+
From the [Illumina IEM specification](https://knowledge.illumina.com/software/on-premises-software/software-on-premises-software-reference_material-list/000002204), the correct V1 `[Settings]` adapter keys are:
|
|
297
|
+
|
|
298
|
+
```csv
|
|
299
|
+
[Settings]
|
|
300
|
+
ReverseComplement,0
|
|
301
|
+
Adapter,AGATCGGAAGAGCACACGTCTGAACTCCAGTCA
|
|
302
|
+
AdapterRead2,AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
- `Adapter` = Read 1 adapter (primary key per IEM spec)
|
|
306
|
+
- `AdapterRead2` = Read 2 adapter (explicit separate key)
|
|
307
|
+
- `AdapterRead1` = BCLConvert V1-mode alias for `Adapter` (also accepted)
|
|
308
|
+
- `ReverseComplement,1` = only for Nextera Mate Pair libraries; `0` for everything else
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Project structure
|
|
313
|
+
|
|
314
|
+
```
|
|
315
|
+
samplesheet-parser/
|
|
316
|
+
├── samplesheet_parser/
|
|
317
|
+
│ ├── __init__.py # Public API
|
|
318
|
+
│ ├── factory.py # SampleSheetFactory — auto-detection
|
|
319
|
+
│ ├── enums.py # SampleSheetVersion, IndexType, ...
|
|
320
|
+
│ ├── validators.py # SampleSheetValidator, ValidationResult
|
|
321
|
+
│ └── parsers/
|
|
322
|
+
│ ├── v1.py # IEM V1 parser (bcl2fastq)
|
|
323
|
+
│ └── v2.py # BCLConvert V2 parser (NovaSeq X)
|
|
324
|
+
├── tests/
|
|
325
|
+
│ ├── conftest.py # Shared fixtures
|
|
326
|
+
│ ├── test_factory.py
|
|
327
|
+
│ ├── test_parsers/
|
|
328
|
+
│ │ ├── test_v1.py
|
|
329
|
+
│ │ └── test_v2.py
|
|
330
|
+
│ └── test_validators/
|
|
331
|
+
│ └── test_validators.py
|
|
332
|
+
├── examples/
|
|
333
|
+
│ ├── parse_examples.py # Demo script
|
|
334
|
+
│ └── sample_sheets/ # Reference SampleSheet.csv files
|
|
335
|
+
├── pyproject.toml
|
|
336
|
+
├── LICENSE
|
|
337
|
+
└── README.md
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Development
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
git clone https://github.com/chaitanyakasaraneni/samplesheet-parser
|
|
346
|
+
cd samplesheet-parser
|
|
347
|
+
pip install -e ".[dev]"
|
|
348
|
+
|
|
349
|
+
# Run tests
|
|
350
|
+
pytest tests/ -v
|
|
351
|
+
|
|
352
|
+
# Run example demo
|
|
353
|
+
python examples/parse_examples.py
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Citation
|
|
359
|
+
|
|
360
|
+
If you use this library in a published pipeline or analysis, please cite:
|
|
361
|
+
|
|
362
|
+
```bibtex
|
|
363
|
+
@software{kasaraneni2026samplsheetparser,
|
|
364
|
+
author = {Kasaraneni, Chaitanya},
|
|
365
|
+
title = {samplesheet-parser: Format-agnostic parser for Illumina SampleSheet.csv},
|
|
366
|
+
year = {2026},
|
|
367
|
+
url = {https://github.com/chaitanyakasaraneni/samplesheet-parser},
|
|
368
|
+
version = {0.1.0}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## License
|
|
375
|
+
|
|
376
|
+
Apache 2.0 — see [LICENSE](LICENSE).
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Related resources
|
|
381
|
+
|
|
382
|
+
- [Illumina IEM V1 SampleSheet reference (Knowledge Article #2204)](https://knowledge.illumina.com/software/on-premises-software/software-on-premises-software-reference_material-list/000002204)
|
|
383
|
+
- [BCLConvert Software Guide](https://support.illumina.com/sequencing/sequencing_software/bcl-convert.html)
|
|
384
|
+
- [Upgrading from bcl2fastq to BCLConvert](https://knowledge.illumina.com/software/general/software-general-reference_material-list/000003710)
|