Skip to content

Commit 5c83f9e

Browse files
authored
Illumina sample sheet v1 pairing (#23)
* feature: Add Support Illumina NovaSeq Sample Sheets
1 parent ba87d16 commit 5c83f9e

18 files changed

+815
-5
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ See [GitHub releases](https://github.com/mll-lab/php-utils/releases).
99

1010
## Unreleased
1111

12+
## v2.1.0
13+
14+
### Added
15+
16+
- Support creating Illumina Sample Sheets (V1) for NovaSeq
17+
1218
## v2.0.0
1319

1420
### Changed

src/IlluminaSampleSheet/SampleSheet.php renamed to src/IlluminaSampleSheet/BaseSampleSheet.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace MLL\Utils\IlluminaSampleSheet;
44

5-
abstract class SampleSheet
5+
abstract class BaseSampleSheet
66
{
77
/** @var array<Section> */
88
protected array $sections = [];
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V1;
4+
5+
use Illuminate\Support\Collection;
6+
use MLL\Utils\IlluminaSampleSheet\IlluminaSampleSheetException;
7+
use MLL\Utils\IlluminaSampleSheet\Section;
8+
9+
/** @template TRow of Row */
10+
class DataSection implements Section
11+
{
12+
/** @var Collection<int, TRow> */
13+
public Collection $rows;
14+
15+
/**
16+
* @param class-string<TRow> $rowClass
17+
*
18+
* @phpstan-ignore-next-line As of now, $rowClass is only used to enforce generic type instantiation.
19+
*/
20+
public function __construct(string $rowClass)
21+
{
22+
$this->rows = new Collection([]);
23+
}
24+
25+
/** @param TRow $row */
26+
public function addRow(Row $row): void
27+
{
28+
$this->rows->add($row);
29+
}
30+
31+
public function validate(): void
32+
{
33+
$this->validateDuplicatedSampleIDs();
34+
}
35+
36+
public function convertSectionToString(): string
37+
{
38+
$this->validate();
39+
40+
if ($this->rows->isEmpty()) {
41+
throw new IlluminaSampleSheetException('Data section must contain at least one row.');
42+
}
43+
/** @var Row $firstRow */
44+
$firstRow = $this->rows->first();
45+
46+
$rowsData = $this->rows
47+
->map(fn (Row $row): string => $row->toString())
48+
->implode("\n");
49+
50+
return "[Data]\n{$firstRow->headerLine()}\n{$rowsData}\n";
51+
}
52+
53+
protected function validateDuplicatedSampleIDs(): void
54+
{
55+
$groups = $this->rows
56+
->groupBy(fn (Row $row) => $row->sampleID);
57+
58+
$duplicates = $groups
59+
->filter(fn ($group) => count($group) > 1)
60+
->keys();
61+
$duplicateIDsAsString = $duplicates->implode(', ');
62+
63+
if ($duplicates->isNotEmpty()) {
64+
throw new IlluminaSampleSheetException("Sample_ID values must be distinct. Duplicated SampleIDs: {$duplicateIDsAsString}");
65+
}
66+
}
67+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V1;
4+
5+
class DualIndex extends Index
6+
{
7+
public string $i7IndexID;
8+
9+
public string $index;
10+
11+
public string $i5IndexID;
12+
13+
public string $index2;
14+
15+
public function __construct(string $i7IndexID, string $index, string $i5IndexID, string $index2)
16+
{
17+
$this->i7IndexID = $i7IndexID;
18+
$this->index = $index;
19+
$this->i5IndexID = $i5IndexID;
20+
$this->index2 = $index2;
21+
22+
$this->validate();
23+
}
24+
25+
public function validate(): void
26+
{
27+
$this->validateIndex($this->index);
28+
$this->validateIndex($this->index2);
29+
}
30+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V1;
4+
5+
use MLL\Utils\IlluminaSampleSheet\Section;
6+
7+
class HeaderSection implements Section
8+
{
9+
public ?string $iemFileVersion;
10+
11+
public ?string $experimentName;
12+
13+
public ?string $investigatorName;
14+
15+
public string $date;
16+
17+
public string $workflow;
18+
19+
public string $application;
20+
21+
public string $assay;
22+
23+
public string $description;
24+
25+
public string $chemistry;
26+
27+
public function __construct(
28+
string $iemFileVersion,
29+
string $investigatorName,
30+
string $experimentName,
31+
string $date,
32+
string $workflow,
33+
string $application,
34+
string $assay,
35+
string $description,
36+
string $chemistry
37+
) {
38+
$this->chemistry = $chemistry;
39+
$this->description = $description;
40+
$this->assay = $assay;
41+
$this->application = $application;
42+
$this->workflow = $workflow;
43+
$this->date = $date;
44+
$this->investigatorName = $investigatorName;
45+
$this->experimentName = $experimentName;
46+
$this->iemFileVersion = $iemFileVersion;
47+
}
48+
49+
public function convertSectionToString(): string
50+
{
51+
$headerLines = [
52+
'[Header]',
53+
"IEMFileVersion,{$this->iemFileVersion}",
54+
"Investigator Name,{$this->investigatorName}",
55+
"Experiment Name,{$this->experimentName}",
56+
"Date,{$this->date}",
57+
"Workflow,{$this->workflow}",
58+
"Application,{$this->application}",
59+
"Assay,{$this->assay}",
60+
"Description,{$this->description}",
61+
"Chemistry,{$this->chemistry}",
62+
];
63+
64+
return implode("\n", $headerLines);
65+
}
66+
}

src/IlluminaSampleSheet/V1/Index.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V1;
4+
5+
use MLL\Utils\IlluminaSampleSheet\IlluminaSampleSheetException;
6+
7+
use function Safe\preg_match;
8+
9+
abstract class Index
10+
{
11+
abstract public function validate(): void;
12+
13+
protected function validateIndex(string $index): void
14+
{
15+
if ($index === '') {
16+
throw new IlluminaSampleSheetException('Index must not be an empty string.');
17+
}
18+
19+
if (! (bool) preg_match('/^[ATCGN]+$/', $index)) {
20+
throw new IlluminaSampleSheetException("Index '{$index}' contains invalid characters. Only A, T, C, G, N are allowed.");
21+
}
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V1;
4+
5+
use MLL\Utils\IlluminaSampleSheet\Section;
6+
7+
class ReadsSection implements Section
8+
{
9+
private int $read1Cycles;
10+
11+
private int $read2Cycles;
12+
13+
public function __construct(int $read1Cycles, int $read2Cycles)
14+
{
15+
$this->read1Cycles = $read1Cycles;
16+
$this->read2Cycles = $read2Cycles;
17+
}
18+
19+
public function convertSectionToString(): string
20+
{
21+
return "[Reads]\n" . $this->read1Cycles . "\n" . $this->read2Cycles;
22+
}
23+
}

src/IlluminaSampleSheet/V1/Row.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V1;
4+
5+
abstract class Row
6+
{
7+
public string $sampleID;
8+
9+
abstract public function toString(): string;
10+
11+
abstract public function headerLine(): string;
12+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V1;
4+
5+
class RowForDualIndexWithLane extends Row
6+
{
7+
public DualIndex $dualIndex;
8+
9+
public int $lane;
10+
11+
public string $sampleName;
12+
13+
public string $samplePlate;
14+
15+
public string $sampleWell;
16+
17+
public string $sampleProject;
18+
19+
public string $description;
20+
21+
public function __construct(DualIndex $dualIndex, int $lane, string $sampleID, string $sampleName, string $samplePlate, string $sampleWell, string $sampleProject, string $description)
22+
{
23+
$this->dualIndex = $dualIndex;
24+
$this->lane = $lane;
25+
$this->sampleID = $sampleID;
26+
$this->sampleName = $sampleName;
27+
$this->samplePlate = $samplePlate;
28+
$this->sampleWell = $sampleWell;
29+
$this->sampleProject = $sampleProject;
30+
$this->description = $description;
31+
}
32+
33+
public function toString(): string
34+
{
35+
return implode(',', [
36+
$this->lane,
37+
$this->sampleID,
38+
$this->sampleName,
39+
$this->samplePlate,
40+
$this->sampleWell,
41+
$this->dualIndex->i7IndexID,
42+
$this->dualIndex->index,
43+
$this->dualIndex->i5IndexID,
44+
$this->dualIndex->index2,
45+
$this->sampleProject,
46+
$this->description,
47+
]);
48+
}
49+
50+
public function headerLine(): string
51+
{
52+
return 'Lane,Sample_ID,Sample_Name,Sample_Plate,Sample_Well,I7_Index_ID,Index,I5_Index_ID,Index2,Sample_Project,Description';
53+
}
54+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V1;
4+
5+
class RowForDualIndexWithoutLane extends Row
6+
{
7+
public DualIndex $dualIndex;
8+
9+
public string $sampleName;
10+
11+
public string $samplePlate;
12+
13+
public string $sampleWell;
14+
15+
public string $sampleProject;
16+
17+
public string $description;
18+
19+
public function __construct(DualIndex $dualIndex, string $sampleID, string $sampleName, string $samplePlate, string $sampleWell, string $sampleProject, string $description)
20+
{
21+
$this->dualIndex = $dualIndex;
22+
$this->sampleID = $sampleID;
23+
$this->sampleName = $sampleName;
24+
$this->samplePlate = $samplePlate;
25+
$this->sampleWell = $sampleWell;
26+
$this->sampleProject = $sampleProject;
27+
$this->description = $description;
28+
}
29+
30+
public function toString(): string
31+
{
32+
return implode(',', [
33+
$this->sampleID,
34+
$this->sampleName,
35+
$this->samplePlate,
36+
$this->sampleWell,
37+
$this->dualIndex->i7IndexID,
38+
$this->dualIndex->index,
39+
$this->dualIndex->i5IndexID,
40+
$this->dualIndex->index2,
41+
$this->sampleProject,
42+
$this->description,
43+
]);
44+
}
45+
46+
public function headerLine(): string
47+
{
48+
return 'Sample_ID,Sample_Name,Sample_Plate,Sample_Well,I7_Index_ID,Index,I5_Index_ID,Index2,Sample_Project,Description';
49+
}
50+
}

0 commit comments

Comments
 (0)