Skip to content

Commit ba87d16

Browse files
authored
feat: Generate the Reads-sections dynamically from the OverrideCycles (#20)
1 parent 1d67359 commit ba87d16

11 files changed

+220
-38
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ See [GitHub releases](https://github.com/mll-lab/php-utils/releases).
99

1010
## Unreleased
1111

12+
## v2.0.0
13+
14+
### Changed
15+
16+
- Generate the Reads-Sections dynamically from the OverrideCycles-part of the Samples-Section for Illumina NovaSeq Sample Sheets (V2)
17+
- OverrideCycles on BclSample not nullable
18+
1219
## v1.14.0
1320

1421
### Added

src/IlluminaSampleSheet/V2/BclConvert/BclConvertSection.php

+38
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert;
44

5+
use Illuminate\Support\Collection;
56
use MLL\Utils\IlluminaSampleSheet\Section;
7+
use MLL\Utils\IlluminaSampleSheet\V2\ReadsSection;
68

79
class BclConvertSection implements Section
810
{
@@ -25,4 +27,40 @@ public function convertSectionToString(): string
2527

2628
return implode("\n", $bclConvertLines);
2729
}
30+
31+
public function makeReadsSection(): ReadsSection
32+
{
33+
$dataRows = new Collection($this->dataSection->dataRows);
34+
35+
$countFromCycleTypeWithCount = fn (CycleTypeWithCount $cycleTypeWithCount): int => $cycleTypeWithCount->count;
36+
37+
$read1Cycles = $dataRows->max(
38+
fn (BclSample $dataRow) => (new Collection($dataRow->overrideCycles->read1->cycles))
39+
->sum($countFromCycleTypeWithCount)
40+
);
41+
$index1Cycles = $dataRows->max(
42+
fn (BclSample $dataRow) => (new Collection($dataRow->overrideCycles->index1->cycles))
43+
->sum($countFromCycleTypeWithCount)
44+
);
45+
46+
$index2Cycles = $dataRows->max(
47+
fn (BclSample $dataRow) => $dataRow->overrideCycles->index2 instanceof OverrideCycle
48+
? (new Collection($dataRow->overrideCycles->index2->cycles))
49+
->sum($countFromCycleTypeWithCount)
50+
: null
51+
);
52+
$read2Cycles = $dataRows->max(
53+
fn (BclSample $dataRow) => $dataRow->overrideCycles->read2 instanceof OverrideCycle
54+
? (new Collection($dataRow->overrideCycles->read2->cycles))
55+
->sum($countFromCycleTypeWithCount)
56+
: null
57+
);
58+
59+
assert(is_int($read1Cycles));
60+
assert(is_int($index1Cycles));
61+
assert(is_int($read2Cycles) || is_null($read2Cycles));
62+
assert(is_int($index2Cycles) || is_null($index2Cycles));
63+
64+
return new ReadsSection($read1Cycles, $index1Cycles, $read2Cycles, $index2Cycles);
65+
}
2866
}

src/IlluminaSampleSheet/V2/BclConvert/BclSample.php

+5-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class BclSample
1313

1414
public ?string $index2 = null;
1515

16-
public ?string $overrideCycles = null;
16+
public OverrideCycles $overrideCycles;
1717

1818
public ?string $adapterRead1 = null;
1919

@@ -26,11 +26,13 @@ class BclSample
2626
public function __construct(
2727
int $lane,
2828
string $sample_ID,
29-
string $index
29+
string $index,
30+
OverrideCycles $overrideCycles
3031
) {
3132
$this->lane = $lane;
3233
$this->sample_ID = $sample_ID;
3334
$this->index = $index;
35+
$this->overrideCycles = $overrideCycles;
3436
}
3537

3638
/** @return array<int|string> */
@@ -41,7 +43,7 @@ public function toArray(): array
4143
$this->sample_ID,
4244
$this->index,
4345
$this->index2,
44-
$this->overrideCycles,
46+
$this->overrideCycles->toString(),
4547
$this->adapterRead1,
4648
$this->adapterRead2,
4749
$this->barcodeMismatchesIndex1,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert;
4+
5+
class CycleType
6+
{
7+
public const READ_CYCLE = 'Y';
8+
public const TRIMMED_CYCLE = 'N';
9+
public const UMI_CYCLE = 'U';
10+
public const INDEX_CYCLE = 'I';
11+
12+
public string $value;
13+
14+
public function __construct(string $value)
15+
{
16+
$this->value = $value;
17+
}
18+
19+
public static function READ_CYCLE(): self
20+
{
21+
return new self(self::READ_CYCLE);
22+
}
23+
24+
public static function TRIMMED_CYCLE(): self
25+
{
26+
return new self(self::TRIMMED_CYCLE);
27+
}
28+
29+
public static function UMI_CYCLE(): self
30+
{
31+
return new self(self::UMI_CYCLE);
32+
}
33+
34+
public static function INDEX_CYCLE(): self
35+
{
36+
return new self(self::INDEX_CYCLE);
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert;
4+
5+
class CycleTypeWithCount
6+
{
7+
protected CycleType $cycleType;
8+
9+
public int $count;
10+
11+
public function __construct(CycleType $cycleType, int $count)
12+
{
13+
$this->cycleType = $cycleType;
14+
$this->count = $count;
15+
}
16+
17+
public function toString(): string
18+
{
19+
return $this->cycleType->value . $this->count;
20+
}
21+
}

src/IlluminaSampleSheet/V2/BclConvert/DataSection.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
class DataSection implements Section
88
{
99
/** @var array<BclSample> */
10-
protected array $dataRows = [];
10+
public array $dataRows = [];
1111

1212
public function addSample(BclSample $bclSample): void
1313
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert;
4+
5+
class OverrideCycle
6+
{
7+
/** @var array<CycleTypeWithCount> */
8+
public array $cycles;
9+
10+
/** @param array<CycleTypeWithCount> $firstCycle */
11+
public function __construct(array $firstCycle)
12+
{
13+
$this->cycles = $firstCycle;
14+
}
15+
16+
public function toString(): string
17+
{
18+
return implode('', array_map(
19+
fn (CycleTypeWithCount $cycle): string => $cycle->toString(),
20+
$this->cycles
21+
));
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert;
4+
5+
use MLL\Utils\IlluminaSampleSheet\IlluminaSampleSheetException;
6+
7+
class OverrideCycles
8+
{
9+
public OverrideCycle $read1;
10+
11+
public OverrideCycle $index1;
12+
13+
public ?OverrideCycle $index2;
14+
15+
public ?OverrideCycle $read2;
16+
17+
public function __construct(string $read1, string $index1, ?string $index2, ?string $read2)
18+
{
19+
$this->read1 = $this->makeOverrideCycle($read1);
20+
$this->index1 = $this->makeOverrideCycle($index1);
21+
$this->index2 = $index2 !== null ? $this->makeOverrideCycle($index2) : null;
22+
$this->read2 = $read2 !== null ? $this->makeOverrideCycle($read2) : null;
23+
}
24+
25+
public function toString(): string
26+
{
27+
return implode(';', array_filter([
28+
$this->read1->toString(),
29+
$this->index1->toString(),
30+
$this->index2 instanceof OverrideCycle
31+
? $this->index2->toString()
32+
: null,
33+
$this->read2 instanceof OverrideCycle
34+
? $this->read2->toString()
35+
: null,
36+
]));
37+
}
38+
39+
public function makeOverrideCycle(string $cycleString): OverrideCycle
40+
{
41+
\Safe\preg_match_all('/([YNUI]+)(\d+)/', $cycleString, $matches, PREG_SET_ORDER);
42+
43+
if (count($matches) > 3) {
44+
throw new IlluminaSampleSheetException("Invalid Override Cycle Part. Should have less than 4 parts: {$cycleString}.");
45+
}
46+
47+
if (count($matches) === 0) {
48+
throw new IlluminaSampleSheetException("Invalid Override Cycle Part. Should have at least 1 part: {$cycleString}.");
49+
}
50+
51+
return new OverrideCycle(
52+
array_map(
53+
fn (array $match) => new CycleTypeWithCount(new CycleType($match[1]), (int) $match[2]),
54+
$matches
55+
)
56+
);
57+
}
58+
}

src/IlluminaSampleSheet/V2/NovaSeqXSampleSheet.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ class NovaSeqXSampleSheet extends SampleSheet
99
{
1010
public function __construct(
1111
HeaderSection $header,
12-
ReadsSection $reads,
1312
?BclConvertSection $bclConvertSection
1413
) {
1514
$this->addSection($header);
16-
$this->addSection($reads);
15+
1716
if (! is_null($bclConvertSection)) {
17+
$this->addSection($bclConvertSection->makeReadsSection());
1818
$this->addSection($bclConvertSection);
1919
}
2020
}

src/IlluminaSampleSheet/V2/ReadsSection.php

+10-10
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,25 @@ class ReadsSection implements Section
99
{
1010
protected int $read1Cycles;
1111

12-
protected ?int $read2Cycles;
12+
protected int $index1Cycles;
1313

14-
protected ?int $index1Cycles;
14+
protected ?int $read2Cycles;
1515

1616
protected ?int $index2Cycles;
1717

18-
public function __construct(int $read1Cycles, ?int $read2Cycles = null, ?int $index1Cycles = null, ?int $index2Cycles = null)
18+
public function __construct(int $read1Cycles, int $index1Cycles, ?int $read2Cycles = null, ?int $index2Cycles = null)
1919
{
2020
if ($read1Cycles < 1) {
2121
throw new IlluminaSampleSheetException('Read1Cycles must be a positive integer.');
2222
}
2323
if ($read2Cycles !== null && $read2Cycles < 1) {
2424
throw new IlluminaSampleSheetException('Read2Cycles must be a positive integer or null.');
2525
}
26-
if ($index1Cycles !== null && ($index1Cycles < 6 || $index1Cycles > 12)) {
27-
throw new IlluminaSampleSheetException('Index1Cycles must be between 6 and 12 or null.');
26+
if ($index1Cycles < 6) {
27+
throw new IlluminaSampleSheetException('Index1Cycles must be at least 6.');
2828
}
29-
if ($index2Cycles !== null && ($index2Cycles < 6 || $index2Cycles > 12)) {
30-
throw new IlluminaSampleSheetException('Index2Cycles must be between 6 and 12 or null.');
29+
if ($index2Cycles !== null && ($index2Cycles < 6)) {
30+
throw new IlluminaSampleSheetException('Index2Cycles must be at least 6.');
3131
}
3232
$this->read1Cycles = $read1Cycles;
3333
$this->read2Cycles = $read2Cycles;
@@ -43,9 +43,9 @@ public function convertSectionToString(): string
4343
if ($this->read2Cycles !== null) {
4444
$readsLines[] = "Read2Cycles,{$this->read2Cycles}";
4545
}
46-
if ($this->index1Cycles !== null) {
47-
$readsLines[] = "Index1Cycles,{$this->index1Cycles}";
48-
}
46+
47+
$readsLines[] = "Index1Cycles,{$this->index1Cycles}";
48+
4949
if ($this->index2Cycles !== null) {
5050
$readsLines[] = "Index2Cycles,{$this->index2Cycles}";
5151
}

0 commit comments

Comments
 (0)