From 05a044833f7414945be0d7bc83ae22c15b4c960d Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Tue, 17 Feb 2026 09:22:25 +0100 Subject: [PATCH 01/40] https://mlllab.atlassian.net/browse/AE-4093 --- .../V2/BclConvert/BclConvertSection.php | 2 +- .../V2/BclConvert/DataSection.php | 2 +- .../V2/BclConvert/OverrideCycles.php | 2 +- .../V2/IlluminaSampleSheetVersion2.php | 33 ++++ .../V2/IndexOrientation.php | 8 + .../V2/InstrumentPlatform.php | 9 + .../V2/MissingRequiredFieldException.php | 11 ++ .../V2/NovaSeqXSampleSheet.php | 1 + .../V2/Sections/BclConvertSettingsSection.php | 27 +++ .../V2/Sections/CloudDataItem.php | 21 +++ .../V2/Sections/CloudDataSection.php | 19 ++ .../V2/Sections/CloudSettingsSection.php | 29 +++ .../V2/{ => Sections}/HeaderSection.php | 2 +- .../V2/Sections/HeaderSectionVersion2.php | 48 +++++ .../V2/{ => Sections}/ReadsSection.php | 2 +- .../V2/Sections/SimpleKeyValueSection.php | 57 ++++++ .../V2/IlluminaSampleSheetVersion2Test.php | 165 ++++++++++++++++++ .../V2/NovaSeqXCloudSampleSheetTest.php | 3 +- 18 files changed, 434 insertions(+), 7 deletions(-) create mode 100644 src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php create mode 100644 src/IlluminaSampleSheet/V2/IndexOrientation.php create mode 100644 src/IlluminaSampleSheet/V2/InstrumentPlatform.php create mode 100644 src/IlluminaSampleSheet/V2/MissingRequiredFieldException.php create mode 100644 src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php create mode 100644 src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php create mode 100644 src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php create mode 100644 src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php rename src/IlluminaSampleSheet/V2/{ => Sections}/HeaderSection.php (97%) create mode 100644 src/IlluminaSampleSheet/V2/Sections/HeaderSectionVersion2.php rename src/IlluminaSampleSheet/V2/{ => Sections}/ReadsSection.php (97%) create mode 100644 src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php create mode 100644 tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclConvertSection.php b/src/IlluminaSampleSheet/V2/BclConvert/BclConvertSection.php index e98757ab..257fcd82 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclConvertSection.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclConvertSection.php @@ -3,7 +3,7 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert; use MLL\Utils\IlluminaSampleSheet\Section; -use MLL\Utils\IlluminaSampleSheet\V2\ReadsSection; +use MLL\Utils\IlluminaSampleSheet\V2\Sections\ReadsSection; class BclConvertSection implements Section { diff --git a/src/IlluminaSampleSheet/V2/BclConvert/DataSection.php b/src/IlluminaSampleSheet/V2/BclConvert/DataSection.php index db3fc4f3..f28db947 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/DataSection.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/DataSection.php @@ -5,7 +5,7 @@ use Illuminate\Support\Collection; use MLL\Utils\IlluminaSampleSheet\IlluminaSampleSheetException; use MLL\Utils\IlluminaSampleSheet\Section; -use MLL\Utils\IlluminaSampleSheet\V2\ReadsSection; +use MLL\Utils\IlluminaSampleSheet\V2\Sections\ReadsSection; class DataSection implements Section { diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php index 03715cfd..4cef13d7 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php @@ -3,7 +3,7 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert; use MLL\Utils\IlluminaSampleSheet\IlluminaSampleSheetException; -use MLL\Utils\IlluminaSampleSheet\V2\HeaderSection; +use MLL\Utils\IlluminaSampleSheet\V2\Sections\HeaderSection; class OverrideCycles { diff --git a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php new file mode 100644 index 00000000..3a13e5b4 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php @@ -0,0 +1,33 @@ +addSection($headerSection); + $this->addSection($readsSection); + $this->addSection($bclConvertSettingsSection); + $this->addSection($bclConvertSection); + $this->addSection($cloudSettingsSection); + $this->addSection($cloudDataSection); + } +} diff --git a/src/IlluminaSampleSheet/V2/IndexOrientation.php b/src/IlluminaSampleSheet/V2/IndexOrientation.php new file mode 100644 index 00000000..ec57ed42 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/IndexOrientation.php @@ -0,0 +1,8 @@ + '4.1.23', + 'FastqCompressionFormat' => 'gzip', + ]; + } + + public function requiredFields(): array + { + return [ + 'SoftwareVersion', + 'FastqCompressionFormat', + ]; + } + + public function sectionName(): string + { + return 'BCLConvert_Settings'; + } +} diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php new file mode 100644 index 00000000..4fcd3c15 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php @@ -0,0 +1,21 @@ +bioSampleName, + $this->projectName, + $this->libraryName, + ]); + } +} diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php new file mode 100644 index 00000000..1b2d5049 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php @@ -0,0 +1,19 @@ + $cloudDataItems */ + public function __construct(private readonly Collection $cloudDataItems) {} + + public function convertSectionToString(): string + { + return $this->cloudDataItems + ->map(fn (CloudDataItem $cloudDataItem): string => $cloudDataItem->toString()) + ->join(PHP_EOL); + } +} diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php new file mode 100644 index 00000000..446863e8 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php @@ -0,0 +1,29 @@ + '2.6.0.202308300002', + 'Cloud_Workflow' => 'ica_workflow_1', + 'BCLConvert_Pipeline' => 'urn:ilmn:ica:pipeline:d5c7e407-d439-48c8-bce5-b7aec225f6a7#BclConvert_v4_1_23_patch1', + ]; + } + + public function requiredFields(): array + { + return [ + 'GeneratedVersion', + 'Cloud_Workflow', + 'BCLConvert_Pipeline', + ]; + } + + public function sectionName(): string + { + return 'Cloud_Settings'; + } +} diff --git a/src/IlluminaSampleSheet/V2/HeaderSection.php b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php similarity index 97% rename from src/IlluminaSampleSheet/V2/HeaderSection.php rename to src/IlluminaSampleSheet/V2/Sections/HeaderSection.php index 8731f43c..5686cd67 100644 --- a/src/IlluminaSampleSheet/V2/HeaderSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php @@ -1,6 +1,6 @@ */ + public function headerFields(): array + { + return [ + 'FileFormatVersion' => IlluminaSampleSheetVersion2::SAMPLE_SHEET_VERSION, + 'IndexOrientation' => IndexOrientation::FORWARD->value, + ]; + } + + /** @return string[] */ + public function requiredFields(): array + { + return [ + 'RunName', + 'InstrumentPlatform', + ]; + } + + public function setIndexOrientation(IndexOrientation $indexOrientation): void + { + $this->headerFields['IndexOrientation'] = $indexOrientation->value; + } + + public function setRunName(string $runName): void + { + $this->headerFields['RunName'] = $runName; + } + + public function setInstrumentPlatform(InstrumentPlatform $instrumentPlatform): void + { + $this->headerFields['InstrumentPlatform'] = $instrumentPlatform->value; + } + + public function sectionName(): string + { + return 'Header'; + } +} diff --git a/src/IlluminaSampleSheet/V2/ReadsSection.php b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php similarity index 97% rename from src/IlluminaSampleSheet/V2/ReadsSection.php rename to src/IlluminaSampleSheet/V2/Sections/ReadsSection.php index 6a28e50b..20774d27 100644 --- a/src/IlluminaSampleSheet/V2/ReadsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php @@ -1,6 +1,6 @@ */ + protected array $headerFields = []; + + abstract public function sectionName(): string; + + /** @return array */ + abstract public function headerFields(): array; + + /** @return string[] */ + abstract public function requiredFields(): array; + + public function __construct() + { + $this->headerFields = $this->headerFields(); + } + + public function setCustomAttribute(string $key, string $value): void + { + $this->headerFields[$key] = $value; + } + + public function checkIfAllRequiredFieldsExists(): void + { + foreach ($this->requiredFields() as $requiredField) { + if (! $this->requiredFieldExists($requiredField)) { + throw new MissingRequiredFieldException($requiredField); + } + } + } + + public function requiredFieldExists(string $key): bool + { + return isset($this->headerFields[$key]); + } + + public function convertSectionToString(): string + { + $this->checkIfAllRequiredFieldsExists(); + + return + "[{$this->sectionName()}]" . PHP_EOL + . join(PHP_EOL, array_map( + fn (string $value, string $key): string => "{$key},{$value}", + $this->headerFields, + array_keys($this->headerFields), + )); + } +} diff --git a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php new file mode 100644 index 00000000..180fc088 --- /dev/null +++ b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php @@ -0,0 +1,165 @@ +setRunName('TestRun1'); + $headerSection->setInstrumentPlatform(InstrumentPlatform::NOVASEQ_X_SERIES); + + $expected = "[Header]\nFileFormatVersion,2\nIndexOrientation,Forward\nRunName,TestRun1\nInstrumentPlatform,NovaSeqXSeries"; + + self::assertSame($expected, $headerSection->convertSectionToString()); + } + + public function testHeaderSectionVersion2WithCustomAttribute(): void + { + $headerSection = new class extends HeaderSectionVersion2 {}; + $headerSection->setRunName('TestRun1'); + $headerSection->setInstrumentPlatform(InstrumentPlatform::NOVASEQ_X_SERIES); + $headerSection->setCustomAttribute('Custom_TestKey', 'TestValue'); + + $expected = "[Header]\nFileFormatVersion,2\nIndexOrientation,Forward\nRunName,TestRun1\nInstrumentPlatform,NovaSeqXSeries\nCustom_TestKey,TestValue"; + + self::assertSame($expected, $headerSection->convertSectionToString()); + } + + public function testHeaderSectionVersion2ThrowsOnMissingRunName(): void + { + $headerSection = new class extends HeaderSectionVersion2 {}; + $headerSection->setInstrumentPlatform(InstrumentPlatform::NOVASEQ_X_SERIES); + + $this->expectException(MissingRequiredFieldException::class); + $this->expectExceptionMessage("Missing required field 'RunName'"); + $headerSection->convertSectionToString(); + } + + public function testHeaderSectionVersion2ThrowsOnMissingInstrumentPlatform(): void + { + $headerSection = new class extends HeaderSectionVersion2 {}; + $headerSection->setRunName('TestRun1'); + + $this->expectException(MissingRequiredFieldException::class); + $this->expectExceptionMessage("Missing required field 'InstrumentPlatform'"); + $headerSection->convertSectionToString(); + } + + public function testHeaderSectionVersion2SetInstrumentPlatformMiSeq(): void + { + $headerSection = new class extends HeaderSectionVersion2 {}; + $headerSection->setRunName('TestRun1'); + $headerSection->setInstrumentPlatform(InstrumentPlatform::MISEQ_I100_SERIES); + + self::assertStringContainsString('InstrumentPlatform,MiSeqi100Series', $headerSection->convertSectionToString()); + } + + public function testBclConvertSettingsSectionToStringReturnsExpectedResult(): void + { + $section = new BclConvertSettingsSection(); + + $expected = "[BCLConvert_Settings]\nSoftwareVersion,4.1.23\nFastqCompressionFormat,gzip"; + + self::assertSame($expected, $section->convertSectionToString()); + } + + public function testBclConvertSettingsSectionOverrideDefaultValue(): void + { + $section = new BclConvertSettingsSection(); + $section->setCustomAttribute('SoftwareVersion', '5.0.0'); + + $expected = "[BCLConvert_Settings]\nSoftwareVersion,5.0.0\nFastqCompressionFormat,gzip"; + + self::assertSame($expected, $section->convertSectionToString()); + } + + public function testBclConvertSettingsSectionWithAdditionalAttribute(): void + { + $section = new BclConvertSettingsSection(); + $section->setCustomAttribute('TrimUMI', '1'); + + $expected = "[BCLConvert_Settings]\nSoftwareVersion,4.1.23\nFastqCompressionFormat,gzip\nTrimUMI,1"; + + self::assertSame($expected, $section->convertSectionToString()); + } + + public function testCloudSettingsSectionToStringReturnsExpectedResult(): void + { + $section = new CloudSettingsSection(); + + $expected = '[Cloud_Settings]' . "\n" + . 'GeneratedVersion,2.6.0.202308300002' . "\n" + . 'Cloud_Workflow,ica_workflow_1' . "\n" + . 'BCLConvert_Pipeline,urn:ilmn:ica:pipeline:d5c7e407-d439-48c8-bce5-b7aec225f6a7#BclConvert_v4_1_23_patch1'; + + self::assertSame($expected, $section->convertSectionToString()); + } + + public function testCloudSettingsSectionOverrideDefaultValue(): void + { + $section = new CloudSettingsSection(); + $section->setCustomAttribute('GeneratedVersion', '3.0.0'); + + self::assertStringContainsString('GeneratedVersion,3.0.0', $section->convertSectionToString()); + self::assertStringNotContainsString('2.6.0.202308300002', $section->convertSectionToString()); + } + + public function testCloudDataItemToStringReturnsExpectedResult(): void + { + $item = new CloudDataItem('BioSample1', 'ProjectA', 'LibraryX'); + + self::assertSame('BioSample1,ProjectA,LibraryX', $item->toString()); + } + + public function testCloudDataSectionToStringReturnsExpectedResult(): void + { + $items = new Collection([ + new CloudDataItem('BioSample1', 'ProjectA', 'Library1'), + new CloudDataItem('BioSample2', 'ProjectA', 'Library2'), + ]); + $section = new CloudDataSection($items); + + $expected = "BioSample1,ProjectA,Library1\nBioSample2,ProjectA,Library2"; + + self::assertSame($expected, $section->convertSectionToString()); + } + + public function testCloudDataSectionWithSingleItem(): void + { + $items = new Collection([ + new CloudDataItem('OnlySample', 'OnlyProject', 'OnlyLibrary'), + ]); + $section = new CloudDataSection($items); + + self::assertSame('OnlySample,OnlyProject,OnlyLibrary', $section->convertSectionToString()); + } + + public function testCloudDataSectionEmptyReturnsEmptyString(): void + { + $section = new CloudDataSection(new Collection()); + + self::assertSame('', $section->convertSectionToString()); + } + + public function testMissingRequiredFieldExceptionMessage(): void + { + $exception = new MissingRequiredFieldException('TestField'); + + self::assertSame( + "Missing required field 'TestField', please check the array requiredFields", + $exception->getMessage(), + ); + } +} diff --git a/tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php b/tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php index 774cd125..b6728644 100644 --- a/tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php +++ b/tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php @@ -8,7 +8,6 @@ use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\OverrideCycles; use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\SettingsSection; use MLL\Utils\IlluminaSampleSheet\V2\Enums\FastQCompressionFormat; -use MLL\Utils\IlluminaSampleSheet\V2\HeaderSection; use MLL\Utils\IlluminaSampleSheet\V2\NovaSeqXSampleSheet; use PHPUnit\Framework\TestCase; @@ -16,7 +15,7 @@ final class NovaSeqXCloudSampleSheetTest extends TestCase { public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): void { - $headerSection = new HeaderSection('Run1'); + $headerSection = new \MLL\Utils\IlluminaSampleSheet\V2\Sections\HeaderSection('Run1'); $headerSection->instrumentPlatform = 'NovaSeqXSeries'; $headerSection->setCustomParam('TestKey', 'TestValue'); From dc14cb1c17e521d49b6336ae554caa13571c14f2 Mon Sep 17 00:00:00 2001 From: KingKong1213 <168984406+KingKong1213@users.noreply.github.com> Date: Tue, 17 Feb 2026 08:24:34 +0000 Subject: [PATCH 02/40] Apply php-cs-fixer changes --- src/CSVArray.php | 4 +++- src/FluidXPlate/FluidXScanner.php | 4 +++- src/IlluminaSampleSheet/V1/DataSection.php | 4 +++- src/IlluminaSampleSheet/V1/SampleSheet.php | 4 +++- .../V2/Sections/CloudDataSection.php | 4 +++- .../RelativeQuantificationSheet.php | 4 +++- src/Microplate/AbstractSection.php | 4 +++- src/Microplate/CoordinateSystem12Well.php | 4 +++- src/Microplate/CoordinateSystem48Well.php | 4 +++- src/Microplate/CoordinateSystem96Well.php | 4 +++- src/Microplate/MicroplateSet/Location.php | 4 +++- src/Microplate/MicroplateSet/MicroplateSet.php | 4 +++- src/PHPStan/Rules/IdToIDRule.php | 4 +++- src/PHPStan/Rules/ThrowableClassNameRule.php | 4 +++- src/Tecan/Rack/BaseRack.php | 4 +++- src/TecanScanner/TecanScanner.php | 4 +++- tests/CSVArrayTest.php | 4 +++- .../V2/IlluminaSampleSheetVersion2Test.php | 10 +++++----- tests/Microplate/MicroplateTest.php | 4 +++- 19 files changed, 59 insertions(+), 23 deletions(-) diff --git a/src/CSVArray.php b/src/CSVArray.php index 042a10b3..350e4cd2 100644 --- a/src/CSVArray.php +++ b/src/CSVArray.php @@ -4,7 +4,9 @@ use Illuminate\Support\Arr; -/** @phpstan-type CSVPrimitive bool|float|int|string|\Stringable|null */ +/** + * @phpstan-type CSVPrimitive bool|float|int|string|\Stringable|null + */ class CSVArray { public const DEFAULT_EMPTY_VALUE = ''; diff --git a/src/FluidXPlate/FluidXScanner.php b/src/FluidXPlate/FluidXScanner.php index 664a394b..08a0cdc8 100644 --- a/src/FluidXPlate/FluidXScanner.php +++ b/src/FluidXPlate/FluidXScanner.php @@ -6,7 +6,9 @@ use MLL\Utils\Microplate\Coordinates; use MLL\Utils\StringUtil; -/** Communicates with a FluidX scanner device and fetches results from it. */ +/** + * Communicates with a FluidX scanner device and fetches results from it. + */ class FluidXScanner { private const READING = 'Reading...'; diff --git a/src/IlluminaSampleSheet/V1/DataSection.php b/src/IlluminaSampleSheet/V1/DataSection.php index 3434c22a..a432db5c 100644 --- a/src/IlluminaSampleSheet/V1/DataSection.php +++ b/src/IlluminaSampleSheet/V1/DataSection.php @@ -6,7 +6,9 @@ use MLL\Utils\IlluminaSampleSheet\IlluminaSampleSheetException; use MLL\Utils\IlluminaSampleSheet\Section; -/** @template TRow of Row */ +/** + * @template TRow of Row + */ class DataSection implements Section { /** @var Collection */ diff --git a/src/IlluminaSampleSheet/V1/SampleSheet.php b/src/IlluminaSampleSheet/V1/SampleSheet.php index c03c46fb..23102e8b 100644 --- a/src/IlluminaSampleSheet/V1/SampleSheet.php +++ b/src/IlluminaSampleSheet/V1/SampleSheet.php @@ -4,7 +4,9 @@ use MLL\Utils\IlluminaSampleSheet\BaseSampleSheet; -/** @template TRow of Row */ +/** + * @template TRow of Row + */ class SampleSheet extends BaseSampleSheet { /** @param DataSection $data */ diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php index 1b2d5049..1658af75 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php @@ -8,7 +8,9 @@ final class CloudDataSection implements Section { /** @param Collection $cloudDataItems */ - public function __construct(private readonly Collection $cloudDataItems) {} + public function __construct( + private readonly Collection $cloudDataItems + ) {} public function convertSectionToString(): string { diff --git a/src/LightcyclerSampleSheet/RelativeQuantificationSheet.php b/src/LightcyclerSampleSheet/RelativeQuantificationSheet.php index 89dbe075..40388267 100644 --- a/src/LightcyclerSampleSheet/RelativeQuantificationSheet.php +++ b/src/LightcyclerSampleSheet/RelativeQuantificationSheet.php @@ -5,7 +5,9 @@ use Illuminate\Support\Collection; use MLL\Utils\StringUtil; -/** TODO use CSVArray, @see AbsoluteQuantificationSheet */ +/** + * TODO use CSVArray, @see AbsoluteQuantificationSheet. + */ class RelativeQuantificationSheet { public const HEADER_COLUMNS = [ diff --git a/src/Microplate/AbstractSection.php b/src/Microplate/AbstractSection.php index 8b66b8fd..f216f25d 100644 --- a/src/Microplate/AbstractSection.php +++ b/src/Microplate/AbstractSection.php @@ -4,7 +4,9 @@ use Illuminate\Support\Collection; -/** @template TSectionWell */ +/** + * @template TSectionWell + */ abstract class AbstractSection { /** @var SectionedMicroplate */ diff --git a/src/Microplate/CoordinateSystem12Well.php b/src/Microplate/CoordinateSystem12Well.php index 7986e3bd..69e002af 100644 --- a/src/Microplate/CoordinateSystem12Well.php +++ b/src/Microplate/CoordinateSystem12Well.php @@ -2,5 +2,7 @@ namespace MLL\Utils\Microplate; -/** @deprecated use CoordinateSystem4x3 */ +/** + * @deprecated use CoordinateSystem4x3 + */ class CoordinateSystem12Well extends CoordinateSystem4x3 {} diff --git a/src/Microplate/CoordinateSystem48Well.php b/src/Microplate/CoordinateSystem48Well.php index 97aa3d36..14d95728 100644 --- a/src/Microplate/CoordinateSystem48Well.php +++ b/src/Microplate/CoordinateSystem48Well.php @@ -2,5 +2,7 @@ namespace MLL\Utils\Microplate; -/** @deprecated use CoordinateSystem8x6 */ +/** + * @deprecated use CoordinateSystem8x6 + */ class CoordinateSystem48Well extends CoordinateSystem8x6 {} diff --git a/src/Microplate/CoordinateSystem96Well.php b/src/Microplate/CoordinateSystem96Well.php index 6f13eaa4..3f11021c 100644 --- a/src/Microplate/CoordinateSystem96Well.php +++ b/src/Microplate/CoordinateSystem96Well.php @@ -2,5 +2,7 @@ namespace MLL\Utils\Microplate; -/** @deprecated use CoordinateSystem12x8 */ +/** + * @deprecated use CoordinateSystem12x8 + */ class CoordinateSystem96Well extends CoordinateSystem12x8 {} diff --git a/src/Microplate/MicroplateSet/Location.php b/src/Microplate/MicroplateSet/Location.php index ff28f51e..cfa8eca3 100644 --- a/src/Microplate/MicroplateSet/Location.php +++ b/src/Microplate/MicroplateSet/Location.php @@ -5,7 +5,9 @@ use MLL\Utils\Microplate\Coordinates; use MLL\Utils\Microplate\CoordinateSystem; -/** @template TCoordinateSystem of CoordinateSystem */ +/** + * @template TCoordinateSystem of CoordinateSystem + */ class Location { public string $plateID; diff --git a/src/Microplate/MicroplateSet/MicroplateSet.php b/src/Microplate/MicroplateSet/MicroplateSet.php index f00b2497..3aacf7ef 100644 --- a/src/Microplate/MicroplateSet/MicroplateSet.php +++ b/src/Microplate/MicroplateSet/MicroplateSet.php @@ -6,7 +6,9 @@ use MLL\Utils\Microplate\CoordinateSystem; use MLL\Utils\Microplate\Enums\FlowDirection; -/** @template TCoordinateSystem of CoordinateSystem */ +/** + * @template TCoordinateSystem of CoordinateSystem + */ abstract class MicroplateSet { /** @var TCoordinateSystem */ diff --git a/src/PHPStan/Rules/IdToIDRule.php b/src/PHPStan/Rules/IdToIDRule.php index 2ac45ced..e3b880a8 100644 --- a/src/PHPStan/Rules/IdToIDRule.php +++ b/src/PHPStan/Rules/IdToIDRule.php @@ -8,7 +8,9 @@ use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; -/** @implements Rule */ +/** + * @implements Rule + */ abstract class IdToIDRule implements Rule { /** @var array */ diff --git a/src/PHPStan/Rules/ThrowableClassNameRule.php b/src/PHPStan/Rules/ThrowableClassNameRule.php index 30c211f1..883a262c 100644 --- a/src/PHPStan/Rules/ThrowableClassNameRule.php +++ b/src/PHPStan/Rules/ThrowableClassNameRule.php @@ -10,7 +10,9 @@ use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; -/** @implements Rule */ +/** + * @implements Rule + */ class ThrowableClassNameRule implements Rule { private ReflectionProvider $reflectionProvider; diff --git a/src/Tecan/Rack/BaseRack.php b/src/Tecan/Rack/BaseRack.php index 9f0e9e84..8732f243 100644 --- a/src/Tecan/Rack/BaseRack.php +++ b/src/Tecan/Rack/BaseRack.php @@ -5,7 +5,9 @@ use Illuminate\Support\Collection; use MLL\Utils\Microplate\CoordinateSystem; -/** @template TContent */ +/** + * @template TContent + */ abstract class BaseRack implements Rack { public const EMPTY_POSITION = null; diff --git a/src/TecanScanner/TecanScanner.php b/src/TecanScanner/TecanScanner.php index ef9edcbd..007ab442 100644 --- a/src/TecanScanner/TecanScanner.php +++ b/src/TecanScanner/TecanScanner.php @@ -8,7 +8,9 @@ use MLL\Utils\Microplate\Coordinates; use MLL\Utils\StringUtil; -/** The plate scanner on a tecan worktable. */ +/** + * The plate scanner on a tecan worktable. + */ class TecanScanner { public const NO_READ = 'NO READ'; diff --git a/tests/CSVArrayTest.php b/tests/CSVArrayTest.php index 4066ceb0..9b6e79e3 100644 --- a/tests/CSVArrayTest.php +++ b/tests/CSVArrayTest.php @@ -6,7 +6,9 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -/** @phpstan-import-type CSVPrimitive from CSVArray */ +/** + * @phpstan-import-type CSVPrimitive from CSVArray + */ final class CSVArrayTest extends TestCase { /** @return iterable>}> */ diff --git a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php index 180fc088..e47e23d2 100644 --- a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php +++ b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php @@ -16,7 +16,7 @@ final class IlluminaSampleSheetVersion2Test extends TestCase { public function testHeaderSectionVersion2ToStringReturnsExpectedResult(): void { - $headerSection = new class extends HeaderSectionVersion2 {}; + $headerSection = new class() extends HeaderSectionVersion2 {}; $headerSection->setRunName('TestRun1'); $headerSection->setInstrumentPlatform(InstrumentPlatform::NOVASEQ_X_SERIES); @@ -27,7 +27,7 @@ public function testHeaderSectionVersion2ToStringReturnsExpectedResult(): void public function testHeaderSectionVersion2WithCustomAttribute(): void { - $headerSection = new class extends HeaderSectionVersion2 {}; + $headerSection = new class() extends HeaderSectionVersion2 {}; $headerSection->setRunName('TestRun1'); $headerSection->setInstrumentPlatform(InstrumentPlatform::NOVASEQ_X_SERIES); $headerSection->setCustomAttribute('Custom_TestKey', 'TestValue'); @@ -39,7 +39,7 @@ public function testHeaderSectionVersion2WithCustomAttribute(): void public function testHeaderSectionVersion2ThrowsOnMissingRunName(): void { - $headerSection = new class extends HeaderSectionVersion2 {}; + $headerSection = new class() extends HeaderSectionVersion2 {}; $headerSection->setInstrumentPlatform(InstrumentPlatform::NOVASEQ_X_SERIES); $this->expectException(MissingRequiredFieldException::class); @@ -49,7 +49,7 @@ public function testHeaderSectionVersion2ThrowsOnMissingRunName(): void public function testHeaderSectionVersion2ThrowsOnMissingInstrumentPlatform(): void { - $headerSection = new class extends HeaderSectionVersion2 {}; + $headerSection = new class() extends HeaderSectionVersion2 {}; $headerSection->setRunName('TestRun1'); $this->expectException(MissingRequiredFieldException::class); @@ -59,7 +59,7 @@ public function testHeaderSectionVersion2ThrowsOnMissingInstrumentPlatform(): vo public function testHeaderSectionVersion2SetInstrumentPlatformMiSeq(): void { - $headerSection = new class extends HeaderSectionVersion2 {}; + $headerSection = new class() extends HeaderSectionVersion2 {}; $headerSection->setRunName('TestRun1'); $headerSection->setInstrumentPlatform(InstrumentPlatform::MISEQ_I100_SERIES); diff --git a/tests/Microplate/MicroplateTest.php b/tests/Microplate/MicroplateTest.php index a71c3fbc..e571901c 100644 --- a/tests/Microplate/MicroplateTest.php +++ b/tests/Microplate/MicroplateTest.php @@ -12,7 +12,9 @@ use MLL\Utils\Microplate\WellWithCoordinates; use PHPUnit\Framework\TestCase; -/** @phpstan-import-type WellData from CoordinatesTest */ +/** + * @phpstan-import-type WellData from CoordinatesTest + */ final class MicroplateTest extends TestCase { public function testCanAddAndRetrieveWellBasedOnCoordinateSystem(): void From 9a1fccd143a271bbf7aa64102e0aaa9f3bc9940f Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Wed, 18 Feb 2026 15:48:07 +0100 Subject: [PATCH 03/40] Current state --- src/IlluminaSampleSheet/BaseSampleSheet.php | 2 +- src/IlluminaSampleSheet/Section.php | 1 + src/IlluminaSampleSheet/V1/DataSection.php | 7 +- src/IlluminaSampleSheet/V1/HeaderSection.php | 6 +- src/IlluminaSampleSheet/V1/ReadsSection.php | 7 +- .../V1/SettingsSection.php | 7 +- .../V2/BclConvert/BclConvertSection.php | 34 --- .../V2/BclConvert/BclSample.php | 90 +++--- .../V2/BclConvert/CycleType.php | 37 +-- .../V2/BclConvert/CycleTypeWithCount.php | 11 +- .../V2/BclConvert/DataSection.php | 131 --------- .../FlowcellLaneNotExistsException.php | 6 + .../V2/BclConvert/FlowcellLanes.php | 30 ++ .../V2/BclConvert/NucleotideType.php | 11 + .../V2/BclConvert/OverrideCycle.php | 80 ++++-- .../V2/BclConvert/OverrideCycleCounter.php | 60 ++++ .../V2/BclConvert/OverrideCycles.php | 106 +++---- .../V2/BclConvert/SettingsSection.php | 42 --- .../V2/BclConvertSoftwareVersion.php | 8 + .../V2/Enums/FastQCompressionFormat.php | 23 +- .../V2/IlluminaSampleSheetVersion2.php | 31 +- .../V2/IndexOrientation.php | 1 + .../V2/NovaSeqXSampleSheet.php | 22 -- .../V2/Sections/BclConvertDataSection.php | 41 +++ .../V2/Sections/BclConvertSettingsSection.php | 27 +- .../V2/Sections/CloudDataItem.php | 2 + .../V2/Sections/CloudDataSection.php | 8 +- .../V2/Sections/CloudSettingsSection.php | 26 +- .../V2/Sections/HeaderSection.php | 73 ++--- .../V2/Sections/HeaderSectionVersion2.php | 48 ---- .../V2/Sections/ReadsSection.php | 68 ++--- .../V2/Sections/SimpleKeyValueSection.php | 53 +--- tests/IlluminaSampleSheet/SampleSheetTest.php | 4 +- .../V2/BclConvertSettingsSectionTest.php | 37 +++ .../V2/DataSectionTest.php | 58 ---- .../V2/HeaderSectionTest.php | 30 ++ .../V2/IlluminaSampleSheetVersion2Test.php | 270 +++++++++--------- .../V2/NovaSeqXCloudSampleSheetTest.php | 104 ------- .../V2/OverrideCycleTest.php | 75 +++++ .../V2/OverrideCyclesTest.php | 87 ++++++ 40 files changed, 816 insertions(+), 948 deletions(-) delete mode 100644 src/IlluminaSampleSheet/V2/BclConvert/BclConvertSection.php delete mode 100644 src/IlluminaSampleSheet/V2/BclConvert/DataSection.php create mode 100644 src/IlluminaSampleSheet/V2/BclConvert/FlowcellLaneNotExistsException.php create mode 100644 src/IlluminaSampleSheet/V2/BclConvert/FlowcellLanes.php create mode 100644 src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php create mode 100644 src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php delete mode 100644 src/IlluminaSampleSheet/V2/BclConvert/SettingsSection.php create mode 100644 src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php delete mode 100644 src/IlluminaSampleSheet/V2/NovaSeqXSampleSheet.php create mode 100644 src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php delete mode 100644 src/IlluminaSampleSheet/V2/Sections/HeaderSectionVersion2.php create mode 100644 tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php delete mode 100644 tests/IlluminaSampleSheet/V2/DataSectionTest.php create mode 100644 tests/IlluminaSampleSheet/V2/HeaderSectionTest.php delete mode 100644 tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php create mode 100644 tests/IlluminaSampleSheet/V2/OverrideCycleTest.php create mode 100644 tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php diff --git a/src/IlluminaSampleSheet/BaseSampleSheet.php b/src/IlluminaSampleSheet/BaseSampleSheet.php index 0d00a28e..17c61d29 100644 --- a/src/IlluminaSampleSheet/BaseSampleSheet.php +++ b/src/IlluminaSampleSheet/BaseSampleSheet.php @@ -15,7 +15,7 @@ public function addSection(Section $section): void public function toString(): string { $sectionStrings = array_map( - fn (Section $section): string => $section->convertSectionToString(), + fn (Section $section): string => "[{$section->sectionName()}]".PHP_EOL.$section->convertSectionToString(), $this->sections ); diff --git a/src/IlluminaSampleSheet/Section.php b/src/IlluminaSampleSheet/Section.php index c5711296..a6806f76 100644 --- a/src/IlluminaSampleSheet/Section.php +++ b/src/IlluminaSampleSheet/Section.php @@ -5,4 +5,5 @@ interface Section { public function convertSectionToString(): string; + public function sectionName(): string; } diff --git a/src/IlluminaSampleSheet/V1/DataSection.php b/src/IlluminaSampleSheet/V1/DataSection.php index a432db5c..7fceb791 100644 --- a/src/IlluminaSampleSheet/V1/DataSection.php +++ b/src/IlluminaSampleSheet/V1/DataSection.php @@ -48,7 +48,7 @@ public function convertSectionToString(): string ->map(fn (Row $row): string => $row->toString()) ->implode("\n"); - return "[Data]\n{$firstRow->headerLine()}\n{$rowsData}\n"; + return "{$firstRow->headerLine()}\n{$rowsData}\n"; } protected function validateDuplicatedSampleIDs(): void @@ -65,4 +65,9 @@ protected function validateDuplicatedSampleIDs(): void throw new IlluminaSampleSheetException("Sample_ID values must be distinct. Duplicated SampleIDs: {$duplicateIDsAsString}"); } } + + public function sectionName(): string + { + return 'Data'; + } } diff --git a/src/IlluminaSampleSheet/V1/HeaderSection.php b/src/IlluminaSampleSheet/V1/HeaderSection.php index 208d8201..01d55ae3 100644 --- a/src/IlluminaSampleSheet/V1/HeaderSection.php +++ b/src/IlluminaSampleSheet/V1/HeaderSection.php @@ -49,7 +49,6 @@ public function __construct( public function convertSectionToString(): string { $headerLines = [ - '[Header]', "IEMFileVersion,{$this->iemFileVersion}", "Investigator Name,{$this->investigatorName}", "Experiment Name,{$this->experimentName}", @@ -63,4 +62,9 @@ public function convertSectionToString(): string return implode("\n", $headerLines); } + + public function sectionName(): string + { + return 'Header'; + } } diff --git a/src/IlluminaSampleSheet/V1/ReadsSection.php b/src/IlluminaSampleSheet/V1/ReadsSection.php index fcde2071..e8833e91 100644 --- a/src/IlluminaSampleSheet/V1/ReadsSection.php +++ b/src/IlluminaSampleSheet/V1/ReadsSection.php @@ -18,6 +18,11 @@ public function __construct(int $read1Cycles, int $read2Cycles) public function convertSectionToString(): string { - return "[Reads]\n" . $this->read1Cycles . "\n" . $this->read2Cycles; + return $this->read1Cycles . "\n" . $this->read2Cycles; + } + + public function sectionName(): string + { + return 'Reads'; } } diff --git a/src/IlluminaSampleSheet/V1/SettingsSection.php b/src/IlluminaSampleSheet/V1/SettingsSection.php index 6fc8b614..1970a807 100644 --- a/src/IlluminaSampleSheet/V1/SettingsSection.php +++ b/src/IlluminaSampleSheet/V1/SettingsSection.php @@ -18,7 +18,7 @@ public function __construct(?string $adapter = null, ?string $adapterRead2 = nul public function convertSectionToString(): string { - $settingsLines = ['[Settings]']; + $settingsLines = []; if ($this->adapter !== null) { $settingsLines[] = 'Adapter,' . $this->adapter; @@ -30,4 +30,9 @@ public function convertSectionToString(): string return implode("\n", $settingsLines); } + + public function sectionName(): string + { + return 'Settings'; + } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclConvertSection.php b/src/IlluminaSampleSheet/V2/BclConvert/BclConvertSection.php deleted file mode 100644 index 257fcd82..00000000 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclConvertSection.php +++ /dev/null @@ -1,34 +0,0 @@ -settingsSection = $settingsSection; - $this->dataSection = $dataSection; - } - - public function convertSectionToString(): string - { - $bclConvertLines = [ - $this->settingsSection->convertSectionToString(), - $this->dataSection->convertSectionToString(), - ]; - - return implode("\n", $bclConvertLines); - } - - public function makeReadsSection(): ReadsSection - { - return $this->dataSection->makeReadsSection(); - } -} diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php index d9419ee9..fe88f248 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -4,53 +4,51 @@ class BclSample { - public int $lane; - - /** Not using camelCase because the property names of this class must match the CSV file. */ - public string $sample_ID; - - public string $index; - - public ?string $index2 = null; - - public OverrideCycles $overrideCycles; - - public ?string $adapterRead1 = null; - - public ?string $adapterRead2 = null; - - public ?string $barcodeMismatchesIndex1 = null; - - public ?string $barcodeMismatchesIndex2 = null; - - public ?string $project = null; - + /** @var string */ + public const HEADER_ROW = 'Lane,Sample_ID,Index,Index2,OverrideCycles,AdapterRead1,AdapterRead2,BarcodeMismatchesIndex1,BarcodeMismatchesIndex2'; + + /** + * @param array $lanes + * @param string $sampleID + * @param string $indexRead1 + * @param string $indexRead2 + * @param OverrideCycles $overrideCycles + * @param string $adapterRead1 + * @param string $adapterRead2 + * @param string $barcodeMismatchesIndex1 + * @param string $barcodeMismatchesIndex2 + */ public function __construct( - int $lane, - string $sample_ID, - string $index, - OverrideCycles $overrideCycles - ) { - $this->lane = $lane; - $this->sample_ID = $sample_ID; - $this->index = $index; - $this->overrideCycles = $overrideCycles; - } - - /** @return array */ - public function toArray(): array + public array $lanes, + public string $sampleID, + public string $indexRead1, + public string $indexRead2, + public OverrideCycles $overrideCycles, + public string $adapterRead1, + public string $adapterRead2, + public string $barcodeMismatchesIndex1, + public string $barcodeMismatchesIndex2, + ) {} + + public function toString(OverrideCycleCounter $overrideCycleCounter): string { - return array_filter([ // @phpstan-ignore arrayFilter.strict (we want truthy comparison) - $this->lane, - $this->sample_ID, - $this->index, - $this->index2, - $this->overrideCycles->toString(), - $this->adapterRead1, - $this->adapterRead2, - $this->barcodeMismatchesIndex1, - $this->barcodeMismatchesIndex2, - $this->project, - ]); + $content = []; + foreach($this->lanes as $lane) { + $content[] = join( + ',', + [ + $lane, + $this->sampleID, + $this->indexRead1, + $this->indexRead2, + $this->overrideCycles->toString($overrideCycleCounter), + $this->adapterRead1, + $this->adapterRead2, + $this->barcodeMismatchesIndex1, + $this->barcodeMismatchesIndex2, + ] + ); + } + return implode(PHP_EOL, $content); } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php b/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php index f26983dd..c538fd93 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php @@ -2,37 +2,10 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert; -class CycleType +enum CycleType: string { - public const READ_CYCLE = 'Y'; - public const TRIMMED_CYCLE = 'N'; - public const UMI_CYCLE = 'U'; - public const INDEX_CYCLE = 'I'; - - public string $value; - - public function __construct(string $value) - { - $this->value = $value; - } - - public static function READ_CYCLE(): self - { - return new static(self::READ_CYCLE); - } - - public static function TRIMMED_CYCLE(): self - { - return new static(self::TRIMMED_CYCLE); - } - - public static function UMI_CYCLE(): self - { - return new static(self::UMI_CYCLE); - } - - public static function INDEX_CYCLE(): self - { - return new static(self::INDEX_CYCLE); - } + case READ_CYCLE = 'Y'; + case TRIMMED_CYCLE = 'N'; + case UMI_CYCLE = 'U'; + case INDEX_CYCLE = 'I'; } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php b/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php index 039da119..7374544b 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php @@ -4,15 +4,8 @@ class CycleTypeWithCount { - protected CycleType $cycleType; - - public int $count; - - public function __construct(CycleType $cycleType, int $count) - { - $this->cycleType = $cycleType; - $this->count = $count; - } + public function __construct(public CycleType $cycleType, public int $count) + {} public function toString(): string { diff --git a/src/IlluminaSampleSheet/V2/BclConvert/DataSection.php b/src/IlluminaSampleSheet/V2/BclConvert/DataSection.php deleted file mode 100644 index f28db947..00000000 --- a/src/IlluminaSampleSheet/V2/BclConvert/DataSection.php +++ /dev/null @@ -1,131 +0,0 @@ - */ - public Collection $dataRows; - - public function __construct() - { - $this->dataRows = new Collection(); - } - - public function addSample(BclSample $bclSample): void - { - $this->dataRows->add($bclSample); - } - - public function convertSectionToString(): string - { - $this->assertNotEmpty(); - - $object = $this->dataRows[0]; - if (! is_object($object)) { - throw new IlluminaSampleSheetException('Trying to convert empty data section to string.'); - } - /** @var array $samplePropertiesOfFirstSample */ - $samplePropertiesOfFirstSample = array_keys(get_object_vars($object)); - foreach ($this->dataRows as $sample) { - $actualProperties = array_keys(get_object_vars($sample)); - if ($samplePropertiesOfFirstSample !== $actualProperties) { - throw new IlluminaSampleSheetException('All samples must have the same properties. Expected: ' . \Safe\json_encode($samplePropertiesOfFirstSample) . ', Actual: ' . \Safe\json_encode($actualProperties)); - } - } - - $bclConvertDataHeaderLines = $this->generateDataHeaderByProperties($samplePropertiesOfFirstSample); - - $bclConvertDataLines = [ - '[BCLConvert_Data]', - $bclConvertDataHeaderLines, - ]; - - foreach ($this->dataRows as $dataRow) { - $bclConvertDataLines[] = implode(',', $dataRow->toArray()); - } - - return implode("\n", $bclConvertDataLines) . "\n"; - } - - /** @param array $samplePropertiesOfFirstSample */ - protected function generateDataHeaderByProperties(array $samplePropertiesOfFirstSample): string - { - $samplePropertiesOfFirstSample = array_filter($samplePropertiesOfFirstSample, fn (string $value): bool // @phpstan-ignore-next-line Variable property access on a non-object required here - => $this->dataRows[0]->$value !== null); - - $samplePropertiesOfFirstSample = array_map(fn (string $value): string => ucfirst($value), $samplePropertiesOfFirstSample); - - return implode(',', $samplePropertiesOfFirstSample); - } - - public function makeReadsSection(): ReadsSection - { - return new ReadsSection( - $this->maxRead1Cycles(), - $this->maxIndex1Cycles(), - $this->maxRead2Cycles(), - $this->maxIndex2Cycles() - ); - } - - public function maxRead1Cycles(): int - { - $max = $this->dataRows - ->max(fn (BclSample $dataRow): int => $dataRow - ->overrideCycles - ->read1 - ->sumCountOfAllCycles()); - assert(is_int($max)); - - return $max; - } - - public function maxRead2Cycles(): ?int - { - $max = $this->dataRows->max( - fn (BclSample $dataRow): ?int => $dataRow->overrideCycles->read2 instanceof OverrideCycle - ? $dataRow->overrideCycles->read2->sumCountOfAllCycles() - : null - ); - assert(is_int($max) || is_null($max)); - - return $max; - } - - public function maxIndex1Cycles(): int - { - $index1Cycles = $this->dataRows - ->max(fn (BclSample $dataRow): int => $dataRow - ->overrideCycles - ->index1 - ->sumCountOfAllCycles()); - assert(is_int($index1Cycles)); - - return $index1Cycles; - } - - public function maxIndex2Cycles(): ?int - { - $index2Cycles = $this->dataRows->max( - fn (BclSample $dataRow): ?int => $dataRow->overrideCycles->index2 instanceof OverrideCycle - ? $dataRow->overrideCycles->index2->sumCountOfAllCycles() - : null - ); - assert(is_int($index2Cycles) || is_null($index2Cycles)); - - return $index2Cycles; - } - - public function assertNotEmpty(): void - { - if ($this->dataRows->isEmpty()) { - throw new IlluminaSampleSheetException('At least one sample must be added to the DataSection.'); - } - } -} diff --git a/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLaneNotExistsException.php b/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLaneNotExistsException.php new file mode 100644 index 00000000..f1c1cdf7 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLaneNotExistsException.php @@ -0,0 +1,6 @@ + + */ + public function all(): array + { + return range(1,$this->value); + } + + /** + * @param array $specificLanes + * @return array + */ + public function select(array $specificLanes): array + { + if(count(array_intersect($specificLanes, $this->all())) !== count($specificLanes)){ + throw new FlowcellLaneNotExistsException( + "Der FlowcellTyp: '{$this->name}' besitzt keine Lane: ".implode(', ', array_diff($specificLanes, $this->all())) + ); + } + return $specificLanes; + } +} \ No newline at end of file diff --git a/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php b/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php new file mode 100644 index 00000000..9e6880c9 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php @@ -0,0 +1,11 @@ + */ - public array $cycles; + /** + * @param array $cycleTypeWithCountList + */ + public function __construct(public NucleotideType $nucleotideType, public array $cycleTypeWithCountList, public IndexOrientation $indexOrientation) + {} - /** @param array $cycles */ - public function __construct(array $cycles) + public static function fromString( + string $nucleotideAndCycleString, + IndexOrientation $indexOrientation + ): self { - $this->cycles = $cycles; + [$nucleotideTypeAsString, $cycleString] = explode(':', $nucleotideAndCycleString); + \Safe\preg_match_all('/([YNUI]+)(\d+)/', $cycleString, $matches, PREG_SET_ORDER); + + if (count($matches) > 4) { + throw new IlluminaSampleSheetException("Invalid Override Cycle Part. Should have less than 4 parts: {$cycleString}."); + } + + if (count($matches) === 0) { + throw new IlluminaSampleSheetException("Invalid Override Cycle Part. Should have at least 1 part: {$cycleString}."); + } + + $nucleotideType = NucleotideType::from($nucleotideTypeAsString); + + return new self( + $nucleotideType, + array_map( + fn (array $match): CycleTypeWithCount => new CycleTypeWithCount(CycleType::from($match[1]), (int) $match[2]), + $matches + ), + $indexOrientation + ); } - /** @param bool|null $isSecondIndexAndForwardDirection null if it is not the secondIndex, true if it is the secondIndex and forwardDirection, false if it is the secondIndex and reverseDirection */ - public function toString(int $fillUpToMax, ?bool $isSecondIndexAndForwardDirection): string + public function fillUpTo(int $fillUpToMaxNucleotideCount): self { $countOfAllCycleTypes = $this->sumCountOfAllCycles(); - if ($countOfAllCycleTypes > $fillUpToMax) { - throw new IlluminaSampleSheetException("The sum of all cycle types must be less than or equal to the fill up to max value. \$countOfAllCycleTypes: {$countOfAllCycleTypes} > \$fillUpToMax: {$fillUpToMax}"); + if ($countOfAllCycleTypes > $fillUpToMaxNucleotideCount) { + throw new IlluminaSampleSheetException("The sum of all cycle types must be less than or equal to the fill up to max value. \$countOfAllCycleTypes: {$countOfAllCycleTypes} > \$fillUpToMax: {$fillUpToMaxNucleotideCount}"); } - $rawOverrideCycle = implode('', array_map( - fn (CycleTypeWithCount $cycle): string => $cycle->toString(), - $this->cycles - )); - if ($countOfAllCycleTypes === $fillUpToMax) { - return $rawOverrideCycle; + if ($countOfAllCycleTypes === $fillUpToMaxNucleotideCount) { + return $this; } - $remainingCycles = 'N' . ($fillUpToMax - $countOfAllCycleTypes); + $trimmedCycle = new CycleTypeWithCount( + CycleType::TRIMMED_CYCLE, + ($fillUpToMaxNucleotideCount - $countOfAllCycleTypes) + ); + + if($this->nucleotideType === NucleotideType::I2 && $this->indexOrientation === IndexOrientation::FORWARD){ + array_unshift($this->cycleTypeWithCountList, $trimmedCycle); + } + else{ + $this->cycleTypeWithCountList[] = $trimmedCycle; + } - return (bool) $isSecondIndexAndForwardDirection - ? $remainingCycles . $rawOverrideCycle - : $rawOverrideCycle . $remainingCycles; + return $this; } public function sumCountOfAllCycles(): int @@ -43,8 +72,19 @@ public function sumCountOfAllCycles(): int return array_sum( array_map( fn (CycleTypeWithCount $cycleTypeWithCount): int => $cycleTypeWithCount->count, - $this->cycles + $this->cycleTypeWithCountList ) ); } + + public function toString(): string + { + + return + "{$this->nucleotideType->value}:" + .implode('', array_map( + fn(CycleTypeWithCount $cycle): string => $cycle->toString(), + $this->cycleTypeWithCountList + )); + } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php new file mode 100644 index 00000000..8d76e290 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php @@ -0,0 +1,60 @@ + $overrideCyclesList + */ + public function __construct(public Collection $overrideCyclesList) + {} + + public function maxRead1CycleCount(): int + { + $max = $this->overrideCyclesList + ->max(fn (OverrideCycles $overrideCycles): int => $overrideCycles + ->overrideCycleRead1 + ->sumCountOfAllCycles()); + assert(is_int($max)); + + return $max; + } + + public function maxIndex1CycleCount(): int + { + $max = $this->overrideCyclesList + ->max(fn (OverrideCycles $overrideCycles): int => $overrideCycles + ->overrideCycleIndex1 + ->sumCountOfAllCycles()); + assert(is_int($max)); + + return $max; + } + + public function maxIndex2CycleCount(): int + { + $max = $this->overrideCyclesList + ->max(fn (OverrideCycles $overrideCycles): int => $overrideCycles + ->overrideCycleIndex2 + ?->sumCountOfAllCycles() ?? 0 + ); + assert(is_int($max)); + + return $max; + } + + public function maxRead2CycleCount(): int + { + $max = $this->overrideCyclesList + ->max(fn (OverrideCycles $overrideCycles): int => $overrideCycles + ->overrideCycleRead2 + ?->sumCountOfAllCycles() ?? 0 + ); + assert(is_int($max)); + + return $max; + } +} \ No newline at end of file diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php index 4cef13d7..5c194edd 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php @@ -2,88 +2,50 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert; -use MLL\Utils\IlluminaSampleSheet\IlluminaSampleSheetException; -use MLL\Utils\IlluminaSampleSheet\V2\Sections\HeaderSection; +use MLL\Utils\IlluminaSampleSheet\V2\IndexOrientation; class OverrideCycles { - public OverrideCycle $read1; - - public OverrideCycle $index1; - - public ?OverrideCycle $index2; - - public ?OverrideCycle $read2; - - private DataSection $dataSection; - - public function __construct(DataSection $dataSection, string $read1, string $index1, ?string $index2, ?string $read2) + public function __construct( + public OverrideCycle $overrideCycleRead1, + public OverrideCycle $overrideCycleIndex1, + public ?OverrideCycle $overrideCycleIndex2, + public ?OverrideCycle $overrideCycleRead2 + ) + {} + + public static function fromString(string $overrideCyclesAsString, IndexOrientation $indexOrientation): self { - $this->read1 = $this->makeOverrideCycle($read1); - $this->index1 = $this->makeOverrideCycle($index1); - $this->index2 = $index2 !== null ? $this->makeOverrideCycle($index2) : null; - $this->read2 = $read2 !== null ? $this->makeOverrideCycle($read2) : null; - $this->dataSection = $dataSection; + $overrideCyclesAsArray = explode(";", $overrideCyclesAsString); + return new self( + OverrideCycle::fromString($overrideCyclesAsArray[0], $indexOrientation), + OverrideCycle::fromString($overrideCyclesAsArray[1], $indexOrientation), + isset($overrideCyclesAsArray[2]) + ? OverrideCycle::fromString($overrideCyclesAsArray[2], $indexOrientation) + : null, + isset($overrideCyclesAsArray[3]) + ? OverrideCycle::fromString($overrideCyclesAsArray[3], $indexOrientation) + : null, + ); } - public function toString(): string + public function toString(OverrideCycleCounter $overrideCycleCounter): string { - $dataSection = $this->dataSection; - $dataSection->assertNotEmpty(); - $filledParts = array_filter([ // @phpstan-ignore arrayFilter.strict (we want truthy comparison) - $this->read1->toString($dataSection->maxRead1Cycles(), null), - $this->index1->toString($dataSection->maxIndex1Cycles(), null), - $this->index2(), - $this->read2(), + $this->overrideCycleRead1 + ->fillUpTo($overrideCycleCounter->maxRead1CycleCount()) + ->toString(), + $this->overrideCycleIndex1 + ->fillUpTo($overrideCycleCounter->maxIndex1CycleCount()) + ->toString(), + $this->overrideCycleIndex2 + ?->fillUpTo($overrideCycleCounter->maxIndex2CycleCount()) + ?->toString() ?? null, + $this->overrideCycleRead2 + ?->fillUpTo($overrideCycleCounter->maxRead2CycleCount()) + ?->toString() ?? null, ]); return implode(';', $filledParts); } - - public function makeOverrideCycle(string $cycleString): OverrideCycle - { - \Safe\preg_match_all('/([YNUI]+)(\d+)/', $cycleString, $matches, PREG_SET_ORDER); - - if (count($matches) > 3) { - throw new IlluminaSampleSheetException("Invalid Override Cycle Part. Should have less than 4 parts: {$cycleString}."); - } - - if (count($matches) === 0) { - throw new IlluminaSampleSheetException("Invalid Override Cycle Part. Should have at least 1 part: {$cycleString}."); - } - - return new OverrideCycle( - array_map( - fn (array $match): CycleTypeWithCount => new CycleTypeWithCount(new CycleType($match[1]), (int) $match[2]), - $matches - ) - ); - } - - private function index2(): ?string - { - if (! $this->index2 instanceof OverrideCycle) { - return null; - } - $maxIndex2Cycles = $this->dataSection->maxIndex2Cycles(); - if ($maxIndex2Cycles === null) { - throw new IlluminaSampleSheetException('MaxIndex2Cycles is required when Index2 is set.'); - } - - return $this->index2->toString($maxIndex2Cycles, HeaderSection::isForwardIndexOrientation()); - } - - private function read2(): ?string - { - if (! $this->read2 instanceof OverrideCycle) { - return null; - } - $maxIndex2Cycles = $this->dataSection->maxRead2Cycles(); - if ($maxIndex2Cycles === null) { - throw new IlluminaSampleSheetException('MaxRead2Cycles is required when Read2 is set.'); - } - - return $this->read2->toString($maxIndex2Cycles, null); - } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/SettingsSection.php b/src/IlluminaSampleSheet/V2/BclConvert/SettingsSection.php deleted file mode 100644 index 7279c24f..00000000 --- a/src/IlluminaSampleSheet/V2/BclConvert/SettingsSection.php +++ /dev/null @@ -1,42 +0,0 @@ -softwareVersion = $softwareVersion; - $this->fastqCompressionFormat = $fastqCompressionFormat; - } - - public function convertSectionToString(): string - { - $bclConvertSettingsLines = [ - '[BCLConvert_Settings]', - "SoftwareVersion,{$this->softwareVersion}", - "FastqCompressionFormat,{$this->fastqCompressionFormat->value}", - ]; - - if (! is_null($this->trimUMI)) { - $trimUMIAsString = $this->trimUMI - ? '1' - : '0'; - - $bclConvertSettingsLines[] = "TrimUMI,{$trimUMIAsString}"; - } - - return implode("\n", $bclConvertSettingsLines) . "\n"; - } -} diff --git a/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php b/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php new file mode 100644 index 00000000..1761aad7 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php @@ -0,0 +1,8 @@ +value = $value; - } - - public static function GZIP(): self - { - return new static(self::GZIP); - } - - public static function DRAGEN(): self - { - return new static(self::DRAGEN); - } + case GZIP = 'gzip'; + case DRAGEN = 'dragen'; } diff --git a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php index 3a13e5b4..9ba3be9f 100644 --- a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php +++ b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php @@ -3,31 +3,36 @@ namespace MLL\Utils\IlluminaSampleSheet\V2; use MLL\Utils\IlluminaSampleSheet\BaseSampleSheet; -use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\BclConvertSection; +use MLL\Utils\IlluminaSampleSheet\V2\Sections\BclConvertDataSection; use MLL\Utils\IlluminaSampleSheet\V2\Sections\BclConvertSettingsSection; use MLL\Utils\IlluminaSampleSheet\V2\Sections\CloudDataSection; use MLL\Utils\IlluminaSampleSheet\V2\Sections\CloudSettingsSection; -use MLL\Utils\IlluminaSampleSheet\V2\Sections\HeaderSectionVersion2; +use MLL\Utils\IlluminaSampleSheet\V2\Sections\HeaderSection; use MLL\Utils\IlluminaSampleSheet\V2\Sections\ReadsSection; +/** + * Illumina SampleSheet Documentation + * https://help.connected.illumina.com/run-set-up/overview/sample-sheet-structure/bcl-convert-interactive-sample-sheet + */ final class IlluminaSampleSheetVersion2 extends BaseSampleSheet { - /** @var string */ - public const SAMPLE_SHEET_VERSION = '2'; - public function __construct( - HeaderSectionVersion2 $headerSection, - ReadsSection $readsSection, + HeaderSection $headerSection, + ReadsSection $readsSection, BclConvertSettingsSection $bclConvertSettingsSection, - BclConvertSection $bclConvertSection, - CloudSettingsSection $cloudSettingsSection, - CloudDataSection $cloudDataSection, + BclConvertDataSection $bclConvertDataSection, + ?CloudSettingsSection $cloudSettingsSection, + ?CloudDataSection $cloudDataSection, ) { $this->addSection($headerSection); $this->addSection($readsSection); $this->addSection($bclConvertSettingsSection); - $this->addSection($bclConvertSection); - $this->addSection($cloudSettingsSection); - $this->addSection($cloudDataSection); + $this->addSection($bclConvertDataSection); + if($cloudSettingsSection instanceof CloudSettingsSection){ + $this->addSection($cloudSettingsSection); + } + if($cloudDataSection instanceof CloudDataSection){ + $this->addSection($cloudDataSection); + } } } diff --git a/src/IlluminaSampleSheet/V2/IndexOrientation.php b/src/IlluminaSampleSheet/V2/IndexOrientation.php index ec57ed42..1257f360 100644 --- a/src/IlluminaSampleSheet/V2/IndexOrientation.php +++ b/src/IlluminaSampleSheet/V2/IndexOrientation.php @@ -5,4 +5,5 @@ enum IndexOrientation: string { case FORWARD = 'Forward'; + case REVERSE = 'Reverse'; } diff --git a/src/IlluminaSampleSheet/V2/NovaSeqXSampleSheet.php b/src/IlluminaSampleSheet/V2/NovaSeqXSampleSheet.php deleted file mode 100644 index 76f89084..00000000 --- a/src/IlluminaSampleSheet/V2/NovaSeqXSampleSheet.php +++ /dev/null @@ -1,22 +0,0 @@ -addSection($header); - - if (! is_null($bclConvertSection)) { - $this->addSection($bclConvertSection->makeReadsSection()); - $this->addSection($bclConvertSection); - } - } -} diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php new file mode 100644 index 00000000..9513f6a9 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php @@ -0,0 +1,41 @@ + $dataRows + */ + public function __construct(public Collection $dataRows, public OverrideCycleCounter $overrideCycleCounter) + {} + + public function convertSectionToString(): string + { + $this->assertNotEmpty(); + + return + BclSample::HEADER_ROW . PHP_EOL . + $this->dataRows + ->map(fn (BclSample $bclSample): string => $bclSample->toString($this->overrideCycleCounter)) + ->join(PHP_EOL) . PHP_EOL; + } + + public function assertNotEmpty(): void + { + if ($this->dataRows->isEmpty()) { + throw new IlluminaSampleSheetException('At least one sample must be added to the DataSection.'); + } + } + + public function sectionName(): string + { + return 'BCLConvert_Data'; + } +} diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php index 922f3daf..8eac0e37 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php @@ -2,22 +2,25 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\Sections; +use Illuminate\Support\Collection; +use MLL\Utils\IlluminaSampleSheet\V2\BclConvertSoftwareVersion; +use MLL\Utils\IlluminaSampleSheet\V2\Enums\FastQCompressionFormat; + final class BclConvertSettingsSection extends SimpleKeyValueSection { - public function headerFields(): array + public function __construct( + ?BclConvertSoftwareVersion $bclConvertSoftwareVersion + ) { - return [ - 'SoftwareVersion' => '4.1.23', - 'FastqCompressionFormat' => 'gzip', - ]; - } + $fields = new Collection([ + 'FastqCompressionFormat' => FastQCompressionFormat::GZIP->value, + 'GenerateFastqcMetrics' => 'true' + ]); + if($bclConvertSoftwareVersion instanceof BclConvertSoftwareVersion) { + $fields->put('SoftwareVersion', $bclConvertSoftwareVersion->value); + } - public function requiredFields(): array - { - return [ - 'SoftwareVersion', - 'FastqCompressionFormat', - ]; + parent::__construct($fields); } public function sectionName(): string diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php index 4fcd3c15..600d2dee 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php @@ -2,6 +2,8 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\Sections; +use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\BclSample; + final class CloudDataItem { public function __construct( diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php index 1658af75..613be2db 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php @@ -4,6 +4,7 @@ use Illuminate\Support\Collection; use MLL\Utils\IlluminaSampleSheet\Section; +use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\BclSample; final class CloudDataSection implements Section { @@ -16,6 +17,11 @@ public function convertSectionToString(): string { return $this->cloudDataItems ->map(fn (CloudDataItem $cloudDataItem): string => $cloudDataItem->toString()) - ->join(PHP_EOL); + ->join(PHP_EOL) . PHP_EOL; + } + + public function sectionName(): string + { + return 'Cloud_Data'; } } diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php index 446863e8..3689881e 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php @@ -2,24 +2,22 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\Sections; +use Illuminate\Support\Collection; + final class CloudSettingsSection extends SimpleKeyValueSection { - public function headerFields(): array - { - return [ - 'GeneratedVersion' => '2.6.0.202308300002', - 'Cloud_Workflow' => 'ica_workflow_1', - 'BCLConvert_Pipeline' => 'urn:ilmn:ica:pipeline:d5c7e407-d439-48c8-bce5-b7aec225f6a7#BclConvert_v4_1_23_patch1', - ]; - } + /** @var string */ + public const GENERATED_VERSION = '2.6.0.202308300002'; + public const CLOUD_WORKFLOW = 'ica_workflow_1'; + public const BCL_CONVERT_PIPELINE = 'urn:ilmn:ica:pipeline:d5c7e407-d439-48c8-bce5-b7aec225f6a7#BclConvert_v4_1_23_patch1'; - public function requiredFields(): array + public function __construct() { - return [ - 'GeneratedVersion', - 'Cloud_Workflow', - 'BCLConvert_Pipeline', - ]; + parent::__construct(new Collection([ + 'GeneratedVersion' => self::GENERATED_VERSION, + 'Cloud_Workflow' => self::CLOUD_WORKFLOW, + 'BCLConvert_Pipeline' => self::BCL_CONVERT_PIPELINE, + ])); } public function sectionName(): string diff --git a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php index 5686cd67..4a3ab9cb 100644 --- a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php @@ -2,70 +2,37 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\Sections; -use MLL\Utils\IlluminaSampleSheet\Section; +use Illuminate\Support\Collection; +use MLL\Utils\IlluminaSampleSheet\V2\IndexOrientation; +use MLL\Utils\IlluminaSampleSheet\V2\InstrumentPlatform; -class HeaderSection implements Section +class HeaderSection extends SimpleKeyValueSection { protected const FILE_FORMAT_VERSION = '2'; - public const INDEX_ORIENTATION_FORWARD = 'Forward'; - protected string $fileFormatVersion = self::FILE_FORMAT_VERSION; - - public string $runName; - - public ?string $runDescription = null; - - public ?string $instrumentType = null; - - public ?string $instrumentPlatform = null; - - /** @var array */ - protected array $customParams = []; - - public function __construct(string $runName) + public function __construct( + public string $runName, + public IndexOrientation $indexOrientation, + public InstrumentPlatform $instrumentPlatform, + public ?string $runDescription + ) { - $this->runName = $runName; - } + $fields = new Collection([ + 'FileFormatVersion' => self::FILE_FORMAT_VERSION, + 'RunName' => $this->runName, + 'IndexOrientation' => $this->indexOrientation->value, + 'InstrumentPlatform' => $this->instrumentPlatform->value, + ]); - public static function isForwardIndexOrientation(): bool - { - // Added this static method to explicitly display that this flag influences Index2 in OverrideCycles. - return HeaderSection::indexOrientation() === HeaderSection::INDEX_ORIENTATION_FORWARD; - } - - public function setCustomParam(string $paramName, string $paramValue): void - { - $this->customParams['Custom_' . $paramName] = $paramValue; - } - - public function convertSectionToString(): string - { - $indexOrientation = self::indexOrientation(); - $headerLines = [ - '[Header]', - "FileFormatVersion,{$this->fileFormatVersion}", - "RunName,{$this->runName}", - "IndexOrientation,{$indexOrientation}", - ]; if (! is_null($this->runDescription)) { - $headerLines[] = "RunDescription,{$this->runDescription}"; - } - if (! is_null($this->instrumentType)) { - $headerLines[] = "InstrumentType,{$this->instrumentType}"; - } - if (! is_null($this->instrumentPlatform)) { - $headerLines[] = "InstrumentPlatform,{$this->instrumentPlatform}"; - } - foreach ($this->customParams as $paramName => $paramValue) { - $headerLines[] = "{$paramName},{$paramValue}"; + $fields->put('RunDescription', $this->runDescription); } - return implode("\n", $headerLines) . "\n"; + parent::__construct($fields); } - public static function indexOrientation(): string + public function sectionName(): string { - // Only support the default IndexOrientation (Forward) for now. - return self::INDEX_ORIENTATION_FORWARD; + return 'Header'; } } diff --git a/src/IlluminaSampleSheet/V2/Sections/HeaderSectionVersion2.php b/src/IlluminaSampleSheet/V2/Sections/HeaderSectionVersion2.php deleted file mode 100644 index d65a44a2..00000000 --- a/src/IlluminaSampleSheet/V2/Sections/HeaderSectionVersion2.php +++ /dev/null @@ -1,48 +0,0 @@ - */ - public function headerFields(): array - { - return [ - 'FileFormatVersion' => IlluminaSampleSheetVersion2::SAMPLE_SHEET_VERSION, - 'IndexOrientation' => IndexOrientation::FORWARD->value, - ]; - } - - /** @return string[] */ - public function requiredFields(): array - { - return [ - 'RunName', - 'InstrumentPlatform', - ]; - } - - public function setIndexOrientation(IndexOrientation $indexOrientation): void - { - $this->headerFields['IndexOrientation'] = $indexOrientation->value; - } - - public function setRunName(string $runName): void - { - $this->headerFields['RunName'] = $runName; - } - - public function setInstrumentPlatform(InstrumentPlatform $instrumentPlatform): void - { - $this->headerFields['InstrumentPlatform'] = $instrumentPlatform->value; - } - - public function sectionName(): string - { - return 'Header'; - } -} diff --git a/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php index 20774d27..d414d6dc 100644 --- a/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php @@ -2,54 +2,58 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\Sections; +use Illuminate\Support\Collection; use MLL\Utils\IlluminaSampleSheet\IlluminaSampleSheetException; -use MLL\Utils\IlluminaSampleSheet\Section; +use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\OverrideCycleCounter; -class ReadsSection implements Section +class ReadsSection extends SimpleKeyValueSection { - protected int $read1Cycles; - - protected int $index1Cycles; - - protected ?int $read2Cycles; - - protected ?int $index2Cycles; - - public function __construct(int $read1Cycles, int $index1Cycles, ?int $read2Cycles = null, ?int $index2Cycles = null) - { - if ($read1Cycles < 1) { + public function __construct( + int $read1CycleCount, + int $index1CycleCount, + ?int $read2CycleCount, + ?int $index2CycleCount + ){ + if ($read1CycleCount < 1) { throw new IlluminaSampleSheetException('Read1Cycles must be a positive integer.'); } - if ($read2Cycles !== null && $read2Cycles < 1) { + if ($read2CycleCount !== null && $read2CycleCount < 1) { throw new IlluminaSampleSheetException('Read2Cycles must be a positive integer or null.'); } - if ($index1Cycles < 6) { + if ($index1CycleCount < 6) { throw new IlluminaSampleSheetException('Index1Cycles must be at least 6.'); } - if ($index2Cycles !== null && ($index2Cycles < 6)) { + if ($index2CycleCount !== null && ($index2CycleCount < 6)) { throw new IlluminaSampleSheetException('Index2Cycles must be at least 6.'); } - $this->read1Cycles = $read1Cycles; - $this->read2Cycles = $read2Cycles; - $this->index1Cycles = $index1Cycles; - $this->index2Cycles = $index2Cycles; - } - public function convertSectionToString(): string - { - $readsLines = ['[Reads]']; - $readsLines[] = "Read1Cycles,{$this->read1Cycles}"; + $fields = new Collection(); - if ($this->read2Cycles !== null) { - $readsLines[] = "Read2Cycles,{$this->read2Cycles}"; + $fields->put('Read1Cycles', $read1CycleCount); + if(is_int($read2CycleCount)){ + $fields->put('Read2Cycles', $read2CycleCount); } - $readsLines[] = "Index1Cycles,{$this->index1Cycles}"; - - if ($this->index2Cycles !== null) { - $readsLines[] = "Index2Cycles,{$this->index2Cycles}"; + $fields->put('Index1Cycles', $index1CycleCount); + if(is_int($index2CycleCount)){ + $fields->put('Index2Cycles', $index2CycleCount); } - return implode("\n", $readsLines) . "\n"; + parent::__construct($fields); + } + + public static function fromOverrideCycleCounter(OverrideCycleCounter $overrideCycleCounter): self + { + return new self( + read1CycleCount: $overrideCycleCounter->maxRead1CycleCount(), + index1CycleCount: $overrideCycleCounter->maxIndex1CycleCount(), + read2CycleCount: $overrideCycleCounter->maxRead2CycleCount(), + index2CycleCount: $overrideCycleCounter->maxIndex2CycleCount() + ); + } + + public function sectionName(): string + { + return 'Reads'; } } diff --git a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php index 020062a4..5789a902 100644 --- a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php @@ -2,56 +2,23 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\Sections; +use Illuminate\Support\Collection; use MLL\Utils\IlluminaSampleSheet\Section; -use MLL\Utils\IlluminaSampleSheet\V2\MissingRequiredFieldException; abstract class SimpleKeyValueSection implements Section { - /** @var array */ - protected array $headerFields = []; - - abstract public function sectionName(): string; - - /** @return array */ - abstract public function headerFields(): array; - - /** @return string[] */ - abstract public function requiredFields(): array; - - public function __construct() - { - $this->headerFields = $this->headerFields(); - } - - public function setCustomAttribute(string $key, string $value): void - { - $this->headerFields[$key] = $value; - } - - public function checkIfAllRequiredFieldsExists(): void - { - foreach ($this->requiredFields() as $requiredField) { - if (! $this->requiredFieldExists($requiredField)) { - throw new MissingRequiredFieldException($requiredField); - } - } - } - - public function requiredFieldExists(string $key): bool - { - return isset($this->headerFields[$key]); - } + /** + * @param Collection $keyValues + */ + public function __construct(private readonly Collection $keyValues) + {} public function convertSectionToString(): string { - $this->checkIfAllRequiredFieldsExists(); - return - "[{$this->sectionName()}]" . PHP_EOL - . join(PHP_EOL, array_map( - fn (string $value, string $key): string => "{$key},{$value}", - $this->headerFields, - array_keys($this->headerFields), - )); + $this->keyValues + ->map(fn (string $value, string $key): string => "{$key},{$value}") + ->join(PHP_EOL) + . PHP_EOL; } } diff --git a/tests/IlluminaSampleSheet/SampleSheetTest.php b/tests/IlluminaSampleSheet/SampleSheetTest.php index 0f5f38b8..8415de0e 100644 --- a/tests/IlluminaSampleSheet/SampleSheetTest.php +++ b/tests/IlluminaSampleSheet/SampleSheetTest.php @@ -12,14 +12,16 @@ public function testSampleSheetToStringReturnsCorrectFormat(): void { $sectionMock1 = $this->createMock(Section::class); $sectionMock1->method('convertSectionToString')->willReturn('section1'); + $sectionMock1->method('sectionName')->willReturn('section1'); $sectionMock2 = $this->createMock(Section::class); $sectionMock2->method('convertSectionToString')->willReturn('section2'); + $sectionMock2->method('sectionName')->willReturn('section2'); $sampleSheet = $this->createPartialMock(BaseSampleSheet::class, []); $sampleSheet->addSection($sectionMock1); $sampleSheet->addSection($sectionMock2); - self::assertSame("section1\nsection2", $sampleSheet->toString()); + self::assertSame("[section1]\nsection1\n[section2]\nsection2", $sampleSheet->toString()); } } diff --git a/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php b/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php new file mode 100644 index 00000000..2b83d3d0 --- /dev/null +++ b/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php @@ -0,0 +1,37 @@ +convertSectionToString()); + } + + public function testToStringWithoutSoftwareVersion(): void + { + $bclConvertSettingsSection = new BclConvertSettingsSection(null); + + $expected = <<<'CSV' +FastqCompressionFormat,gzip +GenerateFastqcMetrics,true + +CSV; + self::assertSame($expected, $bclConvertSettingsSection->convertSectionToString()); + } +} diff --git a/tests/IlluminaSampleSheet/V2/DataSectionTest.php b/tests/IlluminaSampleSheet/V2/DataSectionTest.php deleted file mode 100644 index af44ba2e..00000000 --- a/tests/IlluminaSampleSheet/V2/DataSectionTest.php +++ /dev/null @@ -1,58 +0,0 @@ -addSample(new BclSample(100, 'Sample1', 'Index1', $overrideCycles)); - - $overrideCycles = new OverrideCycles($dataSection, 'Y100', 'I11', 'I7', 'Y151'); - $dataSection->addSample(new BclSample(101, 'Sample2', 'Index3', $overrideCycles)); - - $expected = <<<'CSV' -[BCLConvert_Data] -Lane,Sample_ID,Index,OverrideCycles -100,Sample1,Index1,Y130;I8N3;I10;Y100N51 -101,Sample2,Index3,Y100N30;I11;N3I7;Y151 - -CSV; - self::assertSame($expected, $dataSection->convertSectionToString()); - } - - public function testThrowsExceptionIfDataSectionIsEmpty(): void - { - $dataSection = new DataSection(); - - $this->expectException(IlluminaSampleSheetException::class); - $this->expectExceptionMessage('At least one sample must be added to the DataSection.'); - $dataSection->convertSectionToString(); - } - - public function testToStringWithProject(): void - { - $dataSection = new DataSection(); - $overrideCycles = new OverrideCycles($dataSection, 'Y130', 'I8', 'I10', 'Y100'); - $bclSample = new BclSample(100, 'Sample1', 'Index1', $overrideCycles); - $bclSample->project = 'foo'; - $dataSection->addSample($bclSample); - - $expected = <<<'CSV' -[BCLConvert_Data] -Lane,Sample_ID,Index,OverrideCycles,Project -100,Sample1,Index1,Y130;I8;I10;Y100,foo - -CSV; - - self::assertSame($expected, $dataSection->convertSectionToString()); - } -} diff --git a/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php b/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php new file mode 100644 index 00000000..ec95f1f9 --- /dev/null +++ b/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php @@ -0,0 +1,30 @@ +convertSectionToString()); + } +} diff --git a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php index e47e23d2..26b2f5c6 100644 --- a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php +++ b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php @@ -3,163 +3,161 @@ namespace MLL\Utils\Tests\IlluminaSampleSheet\V2; use Illuminate\Support\Collection; +use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\BclSample; +use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\OverrideCycleCounter; +use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\OverrideCycles; +use MLL\Utils\IlluminaSampleSheet\V2\BclConvertSoftwareVersion; +use MLL\Utils\IlluminaSampleSheet\V2\IlluminaSampleSheetVersion2; +use MLL\Utils\IlluminaSampleSheet\V2\IndexOrientation; use MLL\Utils\IlluminaSampleSheet\V2\InstrumentPlatform; -use MLL\Utils\IlluminaSampleSheet\V2\MissingRequiredFieldException; +use MLL\Utils\IlluminaSampleSheet\V2\Sections\BclConvertDataSection; use MLL\Utils\IlluminaSampleSheet\V2\Sections\BclConvertSettingsSection; use MLL\Utils\IlluminaSampleSheet\V2\Sections\CloudDataItem; use MLL\Utils\IlluminaSampleSheet\V2\Sections\CloudDataSection; use MLL\Utils\IlluminaSampleSheet\V2\Sections\CloudSettingsSection; -use MLL\Utils\IlluminaSampleSheet\V2\Sections\HeaderSectionVersion2; +use MLL\Utils\IlluminaSampleSheet\V2\Sections\HeaderSection; +use MLL\Utils\IlluminaSampleSheet\V2\Sections\ReadsSection; use PHPUnit\Framework\TestCase; final class IlluminaSampleSheetVersion2Test extends TestCase { - public function testHeaderSectionVersion2ToStringReturnsExpectedResult(): void + public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): void { - $headerSection = new class() extends HeaderSectionVersion2 {}; - $headerSection->setRunName('TestRun1'); - $headerSection->setInstrumentPlatform(InstrumentPlatform::NOVASEQ_X_SERIES); - - $expected = "[Header]\nFileFormatVersion,2\nIndexOrientation,Forward\nRunName,TestRun1\nInstrumentPlatform,NovaSeqXSeries"; - - self::assertSame($expected, $headerSection->convertSectionToString()); - } - - public function testHeaderSectionVersion2WithCustomAttribute(): void - { - $headerSection = new class() extends HeaderSectionVersion2 {}; - $headerSection->setRunName('TestRun1'); - $headerSection->setInstrumentPlatform(InstrumentPlatform::NOVASEQ_X_SERIES); - $headerSection->setCustomAttribute('Custom_TestKey', 'TestValue'); - - $expected = "[Header]\nFileFormatVersion,2\nIndexOrientation,Forward\nRunName,TestRun1\nInstrumentPlatform,NovaSeqXSeries\nCustom_TestKey,TestValue"; - - self::assertSame($expected, $headerSection->convertSectionToString()); - } - - public function testHeaderSectionVersion2ThrowsOnMissingRunName(): void - { - $headerSection = new class() extends HeaderSectionVersion2 {}; - $headerSection->setInstrumentPlatform(InstrumentPlatform::NOVASEQ_X_SERIES); - - $this->expectException(MissingRequiredFieldException::class); - $this->expectExceptionMessage("Missing required field 'RunName'"); - $headerSection->convertSectionToString(); - } - - public function testHeaderSectionVersion2ThrowsOnMissingInstrumentPlatform(): void - { - $headerSection = new class() extends HeaderSectionVersion2 {}; - $headerSection->setRunName('TestRun1'); - - $this->expectException(MissingRequiredFieldException::class); - $this->expectExceptionMessage("Missing required field 'InstrumentPlatform'"); - $headerSection->convertSectionToString(); - } - - public function testHeaderSectionVersion2SetInstrumentPlatformMiSeq(): void - { - $headerSection = new class() extends HeaderSectionVersion2 {}; - $headerSection->setRunName('TestRun1'); - $headerSection->setInstrumentPlatform(InstrumentPlatform::MISEQ_I100_SERIES); - - self::assertStringContainsString('InstrumentPlatform,MiSeqi100Series', $headerSection->convertSectionToString()); - } - - public function testBclConvertSettingsSectionToStringReturnsExpectedResult(): void - { - $section = new BclConvertSettingsSection(); - - $expected = "[BCLConvert_Settings]\nSoftwareVersion,4.1.23\nFastqCompressionFormat,gzip"; - - self::assertSame($expected, $section->convertSectionToString()); - } - - public function testBclConvertSettingsSectionOverrideDefaultValue(): void - { - $section = new BclConvertSettingsSection(); - $section->setCustomAttribute('SoftwareVersion', '5.0.0'); - - $expected = "[BCLConvert_Settings]\nSoftwareVersion,5.0.0\nFastqCompressionFormat,gzip"; - - self::assertSame($expected, $section->convertSectionToString()); - } - - public function testBclConvertSettingsSectionWithAdditionalAttribute(): void - { - $section = new BclConvertSettingsSection(); - $section->setCustomAttribute('TrimUMI', '1'); - - $expected = "[BCLConvert_Settings]\nSoftwareVersion,4.1.23\nFastqCompressionFormat,gzip\nTrimUMI,1"; - - self::assertSame($expected, $section->convertSectionToString()); - } - - public function testCloudSettingsSectionToStringReturnsExpectedResult(): void - { - $section = new CloudSettingsSection(); - - $expected = '[Cloud_Settings]' . "\n" - . 'GeneratedVersion,2.6.0.202308300002' . "\n" - . 'Cloud_Workflow,ica_workflow_1' . "\n" - . 'BCLConvert_Pipeline,urn:ilmn:ica:pipeline:d5c7e407-d439-48c8-bce5-b7aec225f6a7#BclConvert_v4_1_23_patch1'; - - self::assertSame($expected, $section->convertSectionToString()); - } - - public function testCloudSettingsSectionOverrideDefaultValue(): void - { - $section = new CloudSettingsSection(); - $section->setCustomAttribute('GeneratedVersion', '3.0.0'); + $indexOrientation = IndexOrientation::FORWARD; + + $overrideCycles0 = OverrideCycles::fromString('R1:U7N1Y143;I1:I8;I2:I8;R2:U7N1Y143', $indexOrientation); + + $bclSample0 = new BclSample( + lanes: [1], + sampleID: 'Sample1', + indexRead1: 'Index1', + indexRead2: 'Index2', + overrideCycles: $overrideCycles0, + adapterRead1: 'Adapter1', + adapterRead2: 'Adapter2', + barcodeMismatchesIndex1: '0', + barcodeMismatchesIndex2: '0' + ); - self::assertStringContainsString('GeneratedVersion,3.0.0', $section->convertSectionToString()); - self::assertStringNotContainsString('2.6.0.202308300002', $section->convertSectionToString()); - } + $overrideCycles1 = OverrideCycles::fromString('R1:Y151;I1:I8;I2:I10;R2:Y151', $indexOrientation); + $bclSample1 = new BclSample( + lanes: [2], + sampleID: 'Sample2', + indexRead1: 'Index3', indexRead2: 'Index4', + overrideCycles: $overrideCycles1, + adapterRead1: 'Adapter3', + adapterRead2: 'Adapter4', + barcodeMismatchesIndex1: '0', + barcodeMismatchesIndex2: '0' + ); - public function testCloudDataItemToStringReturnsExpectedResult(): void - { - $item = new CloudDataItem('BioSample1', 'ProjectA', 'LibraryX'); + $overrideCycles2 = OverrideCycles::fromString('R1:Y151;I1:I8;I2:I8;R2:U10N12Y127', $indexOrientation); + $bclSample2 = new BclSample( + lanes: [1,2], + sampleID: 'Sample3', + indexRead1: 'Index5', + indexRead2: 'Index6', + overrideCycles: $overrideCycles2, + adapterRead1: 'Adapter5', + adapterRead2: 'Adapter6', + barcodeMismatchesIndex1: '0', + barcodeMismatchesIndex2: '0' + ); - self::assertSame('BioSample1,ProjectA,LibraryX', $item->toString()); - } + $overrideCycles3 = OverrideCycles::fromString('R1:Y101;I1:I8;I2:I8;R2:Y101', $indexOrientation); + $bclSample3 = new BclSample( + lanes: [7,8], + sampleID: 'Sample4', + indexRead1: 'Index5', + indexRead2: 'Index6', + overrideCycles: $overrideCycles3, + adapterRead1: 'Adapter5', + adapterRead2: 'Adapter6', + barcodeMismatchesIndex1: '1', + barcodeMismatchesIndex2: '1' + ); - public function testCloudDataSectionToStringReturnsExpectedResult(): void - { - $items = new Collection([ - new CloudDataItem('BioSample1', 'ProjectA', 'Library1'), - new CloudDataItem('BioSample2', 'ProjectA', 'Library2'), + $bclSampleList = new Collection([ + $bclSample0, + $bclSample1, + $bclSample2, + $bclSample3, ]); - $section = new CloudDataSection($items); - - $expected = "BioSample1,ProjectA,Library1\nBioSample2,ProjectA,Library2"; - self::assertSame($expected, $section->convertSectionToString()); - } - - public function testCloudDataSectionWithSingleItem(): void - { - $items = new Collection([ - new CloudDataItem('OnlySample', 'OnlyProject', 'OnlyLibrary'), - ]); - $section = new CloudDataSection($items); + $headerSection = new HeaderSection( + runName: 'Run1', + indexOrientation: $indexOrientation, + instrumentPlatform: InstrumentPlatform::NOVASEQ_X_SERIES, + runDescription: null + ); - self::assertSame('OnlySample,OnlyProject,OnlyLibrary', $section->convertSectionToString()); - } + $overrideCycleCounter = new OverrideCycleCounter(new Collection([ + $overrideCycles0, + $overrideCycles1, + $overrideCycles2, + $overrideCycles3, + ])); - public function testCloudDataSectionEmptyReturnsEmptyString(): void - { - $section = new CloudDataSection(new Collection()); + $readsSection = ReadsSection::fromOverrideCycleCounter($overrideCycleCounter); - self::assertSame('', $section->convertSectionToString()); - } + $bclConvertSettingsSection = new BclConvertSettingsSection(bclConvertSoftwareVersion: BclConvertSoftwareVersion::V4_1_23); - public function testMissingRequiredFieldExceptionMessage(): void - { - $exception = new MissingRequiredFieldException('TestField'); + $bclConvertDataSection = new BclConvertDataSection( + dataRows: $bclSampleList, + overrideCycleCounter: $overrideCycleCounter + ); - self::assertSame( - "Missing required field 'TestField', please check the array requiredFields", - $exception->getMessage(), + $sampleSheet = new IlluminaSampleSheetVersion2( + headerSection: $headerSection, + readsSection: $readsSection, + bclConvertSettingsSection: $bclConvertSettingsSection, + bclConvertDataSection: $bclConvertDataSection, + cloudSettingsSection: new CloudSettingsSection(), + cloudDataSection: new CloudDataSection(new Collection([ + new CloudDataItem(bioSampleName: $bclSample0->sampleID, projectName: "test", libraryName: "foo"), + new CloudDataItem(bioSampleName: $bclSample1->sampleID, projectName: "test", libraryName: "foo"), + new CloudDataItem(bioSampleName: $bclSample2->sampleID, projectName: "test", libraryName: "foo"), + new CloudDataItem(bioSampleName: $bclSample3->sampleID, projectName: "test", libraryName: "foo"), + ])), ); + + $expected = '[Header] +FileFormatVersion,2 +RunName,Run1 +IndexOrientation,Forward +InstrumentPlatform,NovaSeqXSeries + +[Reads] +Read1Cycles,151 +Read2Cycles,151 +Index1Cycles,8 +Index2Cycles,10 + +[BCLConvert_Settings] +FastqCompressionFormat,gzip +GenerateFastqcMetrics,true +SoftwareVersion,4.1.23 + +[BCLConvert_Data] +Lane,Sample_ID,Index,Index2,OverrideCycles,AdapterRead1,AdapterRead2,BarcodeMismatchesIndex1,BarcodeMismatchesIndex2 +1,Sample1,Index1,Index2,R1:U7N1Y143;I1:I8;I2:N2I8;R2:U7N1Y143,Adapter1,Adapter2,0,0 +2,Sample2,Index3,Index4,R1:Y151;I1:I8;I2:I10;R2:Y151,Adapter3,Adapter4,0,0 +1,Sample3,Index5,Index6,R1:Y151;I1:I8;I2:N2I8;R2:U10N12Y127N2,Adapter5,Adapter6,0,0 +2,Sample3,Index5,Index6,R1:Y151;I1:I8;I2:N2I8;R2:U10N12Y127N2,Adapter5,Adapter6,0,0 +7,Sample4,Index5,Index6,R1:Y101N50;I1:I8;I2:N2I8;R2:Y101N50,Adapter5,Adapter6,1,1 +8,Sample4,Index5,Index6,R1:Y101N50;I1:I8;I2:N2I8;R2:Y101N50,Adapter5,Adapter6,1,1 + +[Cloud_Settings] +GeneratedVersion,2.6.0.202308300002 +Cloud_Workflow,ica_workflow_1 +BCLConvert_Pipeline,urn:ilmn:ica:pipeline:d5c7e407-d439-48c8-bce5-b7aec225f6a7#BclConvert_v4_1_23_patch1 + +[Cloud_Data] +Sample1,test,foo +Sample2,test,foo +Sample3,test,foo +Sample4,test,foo +'; + self::assertSame($expected, $sampleSheet->toString()); } } diff --git a/tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php b/tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php deleted file mode 100644 index b6728644..00000000 --- a/tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php +++ /dev/null @@ -1,104 +0,0 @@ -instrumentPlatform = 'NovaSeqXSeries'; - $headerSection->setCustomParam('TestKey', 'TestValue'); - - $bclConvertSettingsSection = new SettingsSection('1.0.0', FastQCompressionFormat::GZIP()); - $bclConvertSettingsSection->trimUMI = false; - - $bclConvertDataSection = new DataSection(); - - $overrideCycles = new OverrideCycles($bclConvertDataSection, 'U7N1Y143', 'I8', 'I8', 'U7N1Y143'); - $bclSample = new BclSample(1, 'Sample1', 'Index1', $overrideCycles); - $bclSample->index2 = 'Index2'; - - $bclSample->adapterRead1 = 'Adapter1'; - $bclSample->adapterRead2 = 'Adapter2'; - - $overrideCycles1 = new OverrideCycles($bclConvertDataSection, 'Y151', 'I8', 'U10', 'Y151'); - $bclSample1 = new BclSample(2, 'Sample2', 'Index3', $overrideCycles1); - $bclSample1->index2 = 'Index4'; - - $bclSample1->adapterRead1 = 'Adapter3'; - $bclSample1->adapterRead2 = 'Adapter4'; - - $overrideCycles2 = new OverrideCycles($bclConvertDataSection, 'Y151', 'I8', 'I8', 'U10N12Y127'); - $bclSample2 = new BclSample(3, 'Sample3', 'Index5', $overrideCycles2); - $bclSample2->index2 = 'Index6'; - - $bclSample2->adapterRead1 = 'Adapter5'; - $bclSample2->adapterRead2 = 'Adapter6'; - - $overrideCycles3 = new OverrideCycles($bclConvertDataSection, 'Y101', 'I8', 'I8', 'Y101'); - $bclSample3 = new BclSample(8, 'Sample4', 'Index5', $overrideCycles3); - $bclSample3->index2 = 'Index6'; - - $bclSample3->adapterRead1 = 'Adapter5'; - $bclSample3->adapterRead2 = 'Adapter6'; - - $overrideCycles4 = new OverrideCycles($bclConvertDataSection, 'U5N2Y94', 'I6', 'I8', 'U5N2Y94'); - $bclSample4 = new BclSample(6, 'Sample5', 'Index5', $overrideCycles4); - $bclSample4->index2 = 'Index6'; - - $bclSample4->adapterRead1 = 'Adapter5'; - $bclSample4->adapterRead2 = 'Adapter6'; - - $bclConvertDataSection->addSample($bclSample); - $bclConvertDataSection->addSample($bclSample1); - $bclConvertDataSection->addSample($bclSample2); - $bclConvertDataSection->addSample($bclSample3); - $bclConvertDataSection->addSample($bclSample4); - - $bclConvertSection = new BclConvertSection($bclConvertSettingsSection, $bclConvertDataSection); - - $novaSeqXCloudSampleSheet = new NovaSeqXSampleSheet( - $headerSection, - $bclConvertSection, - ); - - $expected = '[Header] -FileFormatVersion,2 -RunName,Run1 -IndexOrientation,Forward -InstrumentPlatform,NovaSeqXSeries -Custom_TestKey,TestValue - -[Reads] -Read1Cycles,151 -Read2Cycles,151 -Index1Cycles,8 -Index2Cycles,10 - -[BCLConvert_Settings] -SoftwareVersion,1.0.0 -FastqCompressionFormat,gzip -TrimUMI,0 - -[BCLConvert_Data] -Lane,Sample_ID,Index,Index2,OverrideCycles,AdapterRead1,AdapterRead2 -1,Sample1,Index1,Index2,U7N1Y143;I8;N2I8;U7N1Y143,Adapter1,Adapter2 -2,Sample2,Index3,Index4,Y151;I8;U10;Y151,Adapter3,Adapter4 -3,Sample3,Index5,Index6,Y151;I8;N2I8;U10N12Y127N2,Adapter5,Adapter6 -8,Sample4,Index5,Index6,Y101N50;I8;N2I8;Y101N50,Adapter5,Adapter6 -6,Sample5,Index5,Index6,U5N2Y94N50;I6N2;N2I8;U5N2Y94N50,Adapter5,Adapter6 -'; - - self::assertSame($expected, $novaSeqXCloudSampleSheet->toString()); - } -} diff --git a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php new file mode 100644 index 00000000..885db274 --- /dev/null +++ b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php @@ -0,0 +1,75 @@ +cycleTypeWithCountList); + self::assertSame(251, $overrideCycle->cycleTypeWithCountList[0]->count); + + $overrideCycle = OverrideCycle::fromString('I2:I6', IndexOrientation::FORWARD); + self::assertCount(1, $overrideCycle->cycleTypeWithCountList); + self::assertSame(6, $overrideCycle->cycleTypeWithCountList[0]->count); + + $overrideCycle = OverrideCycle::fromString('R1:U5N2Y94', IndexOrientation::FORWARD); + self::assertCount(3, $overrideCycle->cycleTypeWithCountList); + self::assertSame(5, $overrideCycle->cycleTypeWithCountList[0]->count); + self::assertSame(2, $overrideCycle->cycleTypeWithCountList[1]->count); + self::assertSame(94, $overrideCycle->cycleTypeWithCountList[2]->count); + } + + /** + * @dataProvider provideCasesForFillUpTest + */ + #[DataProvider('provideCasesForFillUpTest')] + public function testFillUp(string $overrideCycleAsString, int $diff, IndexOrientation $indexOrientation, string $expected): void + { + $overrideCycle = OverrideCycle::fromString($overrideCycleAsString, $indexOrientation); + $total = $overrideCycle->sumCountOfAllCycles() + $diff; + + self::assertSame( + $expected, + $overrideCycle + ->fillUpTo($total) + ->toString() + ); + } + + /** + * @return iterable + */ + public static function provideCasesForFillUpTest(): iterable + { + yield "R1 diff in length" => [ + "overrideCycleAsString" => 'R1:U5N2Y94', 'diff' => 4, 'indexOrientation' => IndexOrientation::FORWARD, "expected" => 'R1:U5N2Y94N4' + ]; + yield "I1 diff in length" => [ + "overrideCycleAsString" => 'I1:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::FORWARD, "expected" => 'I1:I6N2' + ]; + yield "R1 UMI diff in length" => [ + "overrideCycleAsString" => 'R1:U4N2Y98', 'diff' => 1, 'indexOrientation' => IndexOrientation::FORWARD, "expected" => 'R1:U4N2Y98N1' + ]; + yield "R2 diff in length" => [ + "overrideCycleAsString" => 'R2:Y241', 'diff' => 10, 'indexOrientation' => IndexOrientation::FORWARD, "expected" => 'R2:Y241N10' + ]; + yield "I2 diff in length - IndexOrientation Forward" => [ + "overrideCycleAsString" => 'I2:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::FORWARD, "expected" => 'I2:N2I6' + ]; + yield "I2 diff in length - IndexOrientation Reverse" => [ + "overrideCycleAsString" => 'I2:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::REVERSE, "expected" => 'I2:I6N2' + ]; + } +} diff --git a/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php b/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php new file mode 100644 index 00000000..d06bce6d --- /dev/null +++ b/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php @@ -0,0 +1,87 @@ + $overrideCyclesList + * @dataProvider provideCasesForFromStringToString + */ + #[DataProvider('provideCasesForFromStringToString')] + public function testFromStringToString(string $overrideCyclesAsString, Collection $overrideCyclesList, IndexOrientation $indexOrientation, string $expected): void + { + $overrideCycleCounter = new OverrideCycleCounter( + $overrideCyclesList->map(fn(string $overrideCycleAsString): OverrideCycles => OverrideCycles::fromString($overrideCycleAsString, $indexOrientation)) + ); + $overrideCycles = OverrideCycles::fromString($overrideCyclesAsString, $indexOrientation); + self::assertSame($expected, $overrideCycles->toString($overrideCycleCounter)); + } + + /** + * @return iterable, + * indexOrientation: IndexOrientation, + * expected: string + * }> + */ + public static function provideCasesForFromStringToString(): iterable + { + yield "L1 diff in length" => [ + "overrideCyclesAsString" => 'R1:U5N2Y94;I1:I6;I2:I8;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U5N2Y94;I1:I6;I2:I8;R2:Y251','R1:U5N2Y94;I1:I8;I2:I8;R2:Y251']), + 'indexOrientation' => IndexOrientation::FORWARD, + "expected" => 'R1:U5N2Y94;I1:I6N2;I2:I8;R2:Y251' + ]; + + yield "R1 read diff in length" => [ + "overrideCyclesAsString" => 'R1:U5N2Y94;I1:I8;I2:I8;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U5N2Y94;I1:I8;I2:I8;R2:Y251','R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + 'indexOrientation' => IndexOrientation::FORWARD, + "expected" => 'R1:U5N2Y94N4;I1:I8;I2:I8;R2:Y251' + ]; + + yield "R1 UMI diff in length" => [ + "overrideCyclesAsString" => 'R1:U4N2Y98;I1:I8;I2:I8;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U4N2Y98;I1:I8;I2:I8;R2:Y251','R1:U5N2Y98;I1:I6;I2:I8;R2:Y251']), + 'indexOrientation' => IndexOrientation::FORWARD, + "expected" => 'R1:U4N2Y98N1;I1:I8;I2:I8;R2:Y251' + ]; + + yield "R2 read diff in length" => [ + "overrideCyclesAsString" => 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241', + 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I8;R2:Y241','R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + 'indexOrientation' => IndexOrientation::FORWARD, + "expected" => 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241N10' + ]; + + yield "I2 Changed - Index Forward" => [ + "overrideCyclesAsString" => 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251','R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + 'indexOrientation' => IndexOrientation::FORWARD, + "expected" => 'R1:U5N2Y98;I1:I8;I2:N2I6;R2:Y251' + ]; + + yield "I2 Changed - Index Reverse" => [ + "overrideCyclesAsString" => 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251','R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + 'indexOrientation' => IndexOrientation::REVERSE, + "expected" => 'R1:U5N2Y98;I1:I8;I2:I6N2;R2:Y251' + ]; + + yield "R1 changed, I1 Changed, I2 Changed, R2 Changed" => [ + "overrideCyclesAsString" => 'R1:U4N2Y98;I1:I6;I2:I6;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U4N2Y98;I1:I6;I2:I8;R2:Y251','R1:U5N2Y100;I1:I8;I2:I6;R2:Y241']), + 'indexOrientation' => IndexOrientation::REVERSE, + "expected" => 'R1:U4N2Y98N3;I1:I6N2;I2:I6N2;R2:Y251' + ]; + } +} From d820a1c9a7c5674aaaf0dd060e89d21dfff9262c Mon Sep 17 00:00:00 2001 From: KingKong1213 <168984406+KingKong1213@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:48:42 +0000 Subject: [PATCH 04/40] Apply php-cs-fixer changes --- src/IlluminaSampleSheet/BaseSampleSheet.php | 2 +- src/IlluminaSampleSheet/Section.php | 1 + .../V2/BclConvert/BclSample.php | 29 ++++----- .../V2/BclConvert/CycleTypeWithCount.php | 6 +- .../FlowcellLaneNotExistsException.php | 5 +- .../V2/BclConvert/FlowcellLanes.php | 20 +++---- .../V2/BclConvert/NucleotideType.php | 4 +- .../V2/BclConvert/OverrideCycle.php | 26 ++++---- .../V2/BclConvert/OverrideCycleCounter.php | 27 +++++---- .../V2/BclConvert/OverrideCycles.php | 6 +- .../V2/BclConvertSoftwareVersion.php | 4 +- .../V2/IlluminaSampleSheetVersion2.php | 16 ++--- .../V2/Sections/BclConvertDataSection.php | 18 +++--- .../V2/Sections/BclConvertSettingsSection.php | 7 +-- .../V2/Sections/CloudDataItem.php | 2 - .../V2/Sections/CloudDataSection.php | 1 - .../V2/Sections/CloudSettingsSection.php | 2 +- .../V2/Sections/HeaderSection.php | 3 +- .../V2/Sections/ReadsSection.php | 10 ++-- .../V2/Sections/SimpleKeyValueSection.php | 9 ++- .../V2/HeaderSectionTest.php | 2 +- .../V2/IlluminaSampleSheetVersion2Test.php | 15 ++--- .../V2/OverrideCycleTest.php | 30 +++++----- .../V2/OverrideCyclesTest.php | 59 ++++++++++--------- 24 files changed, 145 insertions(+), 159 deletions(-) diff --git a/src/IlluminaSampleSheet/BaseSampleSheet.php b/src/IlluminaSampleSheet/BaseSampleSheet.php index 17c61d29..4fae0d9b 100644 --- a/src/IlluminaSampleSheet/BaseSampleSheet.php +++ b/src/IlluminaSampleSheet/BaseSampleSheet.php @@ -15,7 +15,7 @@ public function addSection(Section $section): void public function toString(): string { $sectionStrings = array_map( - fn (Section $section): string => "[{$section->sectionName()}]".PHP_EOL.$section->convertSectionToString(), + fn (Section $section): string => "[{$section->sectionName()}]" . PHP_EOL . $section->convertSectionToString(), $this->sections ); diff --git a/src/IlluminaSampleSheet/Section.php b/src/IlluminaSampleSheet/Section.php index a6806f76..fa61108f 100644 --- a/src/IlluminaSampleSheet/Section.php +++ b/src/IlluminaSampleSheet/Section.php @@ -5,5 +5,6 @@ interface Section { public function convertSectionToString(): string; + public function sectionName(): string; } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php index fe88f248..bd5a376a 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -4,36 +4,28 @@ class BclSample { - /** @var string */ + /** @var string */ public const HEADER_ROW = 'Lane,Sample_ID,Index,Index2,OverrideCycles,AdapterRead1,AdapterRead2,BarcodeMismatchesIndex1,BarcodeMismatchesIndex2'; /** * @param array $lanes - * @param string $sampleID - * @param string $indexRead1 - * @param string $indexRead2 - * @param OverrideCycles $overrideCycles - * @param string $adapterRead1 - * @param string $adapterRead2 - * @param string $barcodeMismatchesIndex1 - * @param string $barcodeMismatchesIndex2 */ public function __construct( - public array $lanes, - public string $sampleID, - public string $indexRead1, - public string $indexRead2, + public array $lanes, + public string $sampleID, + public string $indexRead1, + public string $indexRead2, public OverrideCycles $overrideCycles, - public string $adapterRead1, - public string $adapterRead2, - public string $barcodeMismatchesIndex1, - public string $barcodeMismatchesIndex2, + public string $adapterRead1, + public string $adapterRead2, + public string $barcodeMismatchesIndex1, + public string $barcodeMismatchesIndex2, ) {} public function toString(OverrideCycleCounter $overrideCycleCounter): string { $content = []; - foreach($this->lanes as $lane) { + foreach ($this->lanes as $lane) { $content[] = join( ',', [ @@ -49,6 +41,7 @@ public function toString(OverrideCycleCounter $overrideCycleCounter): string ] ); } + return implode(PHP_EOL, $content); } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php b/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php index 7374544b..78e9ddbf 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php @@ -4,8 +4,10 @@ class CycleTypeWithCount { - public function __construct(public CycleType $cycleType, public int $count) - {} + public function __construct( + public CycleType $cycleType, + public int $count + ) {} public function toString(): string { diff --git a/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLaneNotExistsException.php b/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLaneNotExistsException.php index f1c1cdf7..ea54384f 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLaneNotExistsException.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLaneNotExistsException.php @@ -1,6 +1,5 @@ - - */ + /** @return array */ public function all(): array { - return range(1,$this->value); + return range(1, $this->value); } /** * @param array $specificLanes + * * @return array */ public function select(array $specificLanes): array { - if(count(array_intersect($specificLanes, $this->all())) !== count($specificLanes)){ - throw new FlowcellLaneNotExistsException( - "Der FlowcellTyp: '{$this->name}' besitzt keine Lane: ".implode(', ', array_diff($specificLanes, $this->all())) - ); + if (count(array_intersect($specificLanes, $this->all())) !== count($specificLanes)) { + throw new FlowcellLaneNotExistsException("Der FlowcellTyp: '{$this->name}' besitzt keine Lane: " . implode(', ', array_diff($specificLanes, $this->all()))); } + return $specificLanes; } -} \ No newline at end of file +} diff --git a/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php b/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php index 9e6880c9..98c34567 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php @@ -1,4 +1,4 @@ - $cycleTypeWithCountList - */ - public function __construct(public NucleotideType $nucleotideType, public array $cycleTypeWithCountList, public IndexOrientation $indexOrientation) - {} + /** @param array $cycleTypeWithCountList */ + public function __construct( + public NucleotideType $nucleotideType, + public array $cycleTypeWithCountList, + public IndexOrientation $indexOrientation + ) {} public static function fromString( string $nucleotideAndCycleString, IndexOrientation $indexOrientation - ): self - { + ): self { [$nucleotideTypeAsString, $cycleString] = explode(':', $nucleotideAndCycleString); \Safe\preg_match_all('/([YNUI]+)(\d+)/', $cycleString, $matches, PREG_SET_ORDER); @@ -54,13 +54,12 @@ public function fillUpTo(int $fillUpToMaxNucleotideCount): self $trimmedCycle = new CycleTypeWithCount( CycleType::TRIMMED_CYCLE, - ($fillUpToMaxNucleotideCount - $countOfAllCycleTypes) + $fillUpToMaxNucleotideCount - $countOfAllCycleTypes ); - if($this->nucleotideType === NucleotideType::I2 && $this->indexOrientation === IndexOrientation::FORWARD){ + if ($this->nucleotideType === NucleotideType::I2 && $this->indexOrientation === IndexOrientation::FORWARD) { array_unshift($this->cycleTypeWithCountList, $trimmedCycle); - } - else{ + } else { $this->cycleTypeWithCountList[] = $trimmedCycle; } @@ -79,11 +78,10 @@ public function sumCountOfAllCycles(): int public function toString(): string { - return "{$this->nucleotideType->value}:" - .implode('', array_map( - fn(CycleTypeWithCount $cycle): string => $cycle->toString(), + . implode('', array_map( + fn (CycleTypeWithCount $cycle): string => $cycle->toString(), $this->cycleTypeWithCountList )); } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php index 8d76e290..c84993f8 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php @@ -1,4 +1,4 @@ - $overrideCyclesList - */ - public function __construct(public Collection $overrideCyclesList) - {} + /** @param Collection $overrideCyclesList */ + public function __construct( + public Collection $overrideCyclesList + ) {} public function maxRead1CycleCount(): int { @@ -37,9 +36,10 @@ public function maxIndex1CycleCount(): int public function maxIndex2CycleCount(): int { $max = $this->overrideCyclesList - ->max(fn (OverrideCycles $overrideCycles): int => $overrideCycles - ->overrideCycleIndex2 - ?->sumCountOfAllCycles() ?? 0 + ->max( + fn (OverrideCycles $overrideCycles): int => $overrideCycles + ->overrideCycleIndex2 + ?->sumCountOfAllCycles() ?? 0 ); assert(is_int($max)); @@ -49,12 +49,13 @@ public function maxIndex2CycleCount(): int public function maxRead2CycleCount(): int { $max = $this->overrideCyclesList - ->max(fn (OverrideCycles $overrideCycles): int => $overrideCycles - ->overrideCycleRead2 - ?->sumCountOfAllCycles() ?? 0 + ->max( + fn (OverrideCycles $overrideCycles): int => $overrideCycles + ->overrideCycleRead2 + ?->sumCountOfAllCycles() ?? 0 ); assert(is_int($max)); return $max; } -} \ No newline at end of file +} diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php index 5c194edd..ada44a59 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php @@ -11,12 +11,12 @@ public function __construct( public OverrideCycle $overrideCycleIndex1, public ?OverrideCycle $overrideCycleIndex2, public ?OverrideCycle $overrideCycleRead2 - ) - {} + ) {} public static function fromString(string $overrideCyclesAsString, IndexOrientation $indexOrientation): self { - $overrideCyclesAsArray = explode(";", $overrideCyclesAsString); + $overrideCyclesAsArray = explode(';', $overrideCyclesAsString); + return new self( OverrideCycle::fromString($overrideCyclesAsArray[0], $indexOrientation), OverrideCycle::fromString($overrideCyclesAsArray[1], $indexOrientation), diff --git a/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php b/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php index 1761aad7..22a08852 100644 --- a/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php +++ b/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php @@ -1,8 +1,8 @@ -addSection($headerSection); $this->addSection($readsSection); $this->addSection($bclConvertSettingsSection); $this->addSection($bclConvertDataSection); - if($cloudSettingsSection instanceof CloudSettingsSection){ + if ($cloudSettingsSection instanceof CloudSettingsSection) { $this->addSection($cloudSettingsSection); } - if($cloudDataSection instanceof CloudDataSection){ + if ($cloudDataSection instanceof CloudDataSection) { $this->addSection($cloudDataSection); } } diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php index 9513f6a9..24d858b4 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php @@ -10,21 +10,21 @@ class BclConvertDataSection implements Section { - /** - * @param Collection $dataRows - */ - public function __construct(public Collection $dataRows, public OverrideCycleCounter $overrideCycleCounter) - {} + /** @param Collection $dataRows */ + public function __construct( + public Collection $dataRows, + public OverrideCycleCounter $overrideCycleCounter + ) {} public function convertSectionToString(): string { $this->assertNotEmpty(); return - BclSample::HEADER_ROW . PHP_EOL . - $this->dataRows - ->map(fn (BclSample $bclSample): string => $bclSample->toString($this->overrideCycleCounter)) - ->join(PHP_EOL) . PHP_EOL; + BclSample::HEADER_ROW . PHP_EOL + . $this->dataRows + ->map(fn (BclSample $bclSample): string => $bclSample->toString($this->overrideCycleCounter)) + ->join(PHP_EOL) . PHP_EOL; } public function assertNotEmpty(): void diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php index 8eac0e37..c9a2a676 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php @@ -10,13 +10,12 @@ final class BclConvertSettingsSection extends SimpleKeyValueSection { public function __construct( ?BclConvertSoftwareVersion $bclConvertSoftwareVersion - ) - { + ) { $fields = new Collection([ 'FastqCompressionFormat' => FastQCompressionFormat::GZIP->value, - 'GenerateFastqcMetrics' => 'true' + 'GenerateFastqcMetrics' => 'true', ]); - if($bclConvertSoftwareVersion instanceof BclConvertSoftwareVersion) { + if ($bclConvertSoftwareVersion instanceof BclConvertSoftwareVersion) { $fields->put('SoftwareVersion', $bclConvertSoftwareVersion->value); } diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php index 600d2dee..4fcd3c15 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php @@ -2,8 +2,6 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\Sections; -use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\BclSample; - final class CloudDataItem { public function __construct( diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php index 613be2db..a05a38ba 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php @@ -4,7 +4,6 @@ use Illuminate\Support\Collection; use MLL\Utils\IlluminaSampleSheet\Section; -use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\BclSample; final class CloudDataSection implements Section { diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php index 3689881e..69e7017e 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudSettingsSection.php @@ -6,7 +6,7 @@ final class CloudSettingsSection extends SimpleKeyValueSection { - /** @var string */ + /** @var string */ public const GENERATED_VERSION = '2.6.0.202308300002'; public const CLOUD_WORKFLOW = 'ica_workflow_1'; public const BCL_CONVERT_PIPELINE = 'urn:ilmn:ica:pipeline:d5c7e407-d439-48c8-bce5-b7aec225f6a7#BclConvert_v4_1_23_patch1'; diff --git a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php index 4a3ab9cb..809c96b3 100644 --- a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php @@ -15,8 +15,7 @@ public function __construct( public IndexOrientation $indexOrientation, public InstrumentPlatform $instrumentPlatform, public ?string $runDescription - ) - { + ) { $fields = new Collection([ 'FileFormatVersion' => self::FILE_FORMAT_VERSION, 'RunName' => $this->runName, diff --git a/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php index d414d6dc..4527654b 100644 --- a/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php @@ -9,11 +9,11 @@ class ReadsSection extends SimpleKeyValueSection { public function __construct( - int $read1CycleCount, - int $index1CycleCount, + int $read1CycleCount, + int $index1CycleCount, ?int $read2CycleCount, ?int $index2CycleCount - ){ + ) { if ($read1CycleCount < 1) { throw new IlluminaSampleSheetException('Read1Cycles must be a positive integer.'); } @@ -30,12 +30,12 @@ public function __construct( $fields = new Collection(); $fields->put('Read1Cycles', $read1CycleCount); - if(is_int($read2CycleCount)){ + if (is_int($read2CycleCount)) { $fields->put('Read2Cycles', $read2CycleCount); } $fields->put('Index1Cycles', $index1CycleCount); - if(is_int($index2CycleCount)){ + if (is_int($index2CycleCount)) { $fields->put('Index2Cycles', $index2CycleCount); } diff --git a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php index 5789a902..56180f54 100644 --- a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php @@ -7,11 +7,10 @@ abstract class SimpleKeyValueSection implements Section { - /** - * @param Collection $keyValues - */ - public function __construct(private readonly Collection $keyValues) - {} + /** @param Collection $keyValues */ + public function __construct( + private readonly Collection $keyValues + ) {} public function convertSectionToString(): string { diff --git a/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php b/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php index ec95f1f9..2a5d7803 100644 --- a/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php +++ b/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php @@ -12,7 +12,7 @@ final class HeaderSectionTest extends TestCase public function testToString(): void { $headerSection = new HeaderSection( - runName: "Test1234", + runName: 'Test1234', indexOrientation: IndexOrientation::FORWARD, instrumentPlatform: InstrumentPlatform::NOVASEQ_X_SERIES, runDescription: null diff --git a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php index 26b2f5c6..eb27ae2a 100644 --- a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php +++ b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php @@ -43,7 +43,8 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi $bclSample1 = new BclSample( lanes: [2], sampleID: 'Sample2', - indexRead1: 'Index3', indexRead2: 'Index4', + indexRead1: 'Index3', + indexRead2: 'Index4', overrideCycles: $overrideCycles1, adapterRead1: 'Adapter3', adapterRead2: 'Adapter4', @@ -53,7 +54,7 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi $overrideCycles2 = OverrideCycles::fromString('R1:Y151;I1:I8;I2:I8;R2:U10N12Y127', $indexOrientation); $bclSample2 = new BclSample( - lanes: [1,2], + lanes: [1, 2], sampleID: 'Sample3', indexRead1: 'Index5', indexRead2: 'Index6', @@ -66,7 +67,7 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi $overrideCycles3 = OverrideCycles::fromString('R1:Y101;I1:I8;I2:I8;R2:Y101', $indexOrientation); $bclSample3 = new BclSample( - lanes: [7,8], + lanes: [7, 8], sampleID: 'Sample4', indexRead1: 'Index5', indexRead2: 'Index6', @@ -114,10 +115,10 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi bclConvertDataSection: $bclConvertDataSection, cloudSettingsSection: new CloudSettingsSection(), cloudDataSection: new CloudDataSection(new Collection([ - new CloudDataItem(bioSampleName: $bclSample0->sampleID, projectName: "test", libraryName: "foo"), - new CloudDataItem(bioSampleName: $bclSample1->sampleID, projectName: "test", libraryName: "foo"), - new CloudDataItem(bioSampleName: $bclSample2->sampleID, projectName: "test", libraryName: "foo"), - new CloudDataItem(bioSampleName: $bclSample3->sampleID, projectName: "test", libraryName: "foo"), + new CloudDataItem(bioSampleName: $bclSample0->sampleID, projectName: 'test', libraryName: 'foo'), + new CloudDataItem(bioSampleName: $bclSample1->sampleID, projectName: 'test', libraryName: 'foo'), + new CloudDataItem(bioSampleName: $bclSample2->sampleID, projectName: 'test', libraryName: 'foo'), + new CloudDataItem(bioSampleName: $bclSample3->sampleID, projectName: 'test', libraryName: 'foo'), ])), ); diff --git a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php index 885db274..2c7781de 100644 --- a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php +++ b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php @@ -26,9 +26,7 @@ public function testFromString(): void self::assertSame(94, $overrideCycle->cycleTypeWithCountList[2]->count); } - /** - * @dataProvider provideCasesForFillUpTest - */ + /** @dataProvider provideCasesForFillUpTest */ #[DataProvider('provideCasesForFillUpTest')] public function testFillUp(string $overrideCycleAsString, int $diff, IndexOrientation $indexOrientation, string $expected): void { @@ -37,7 +35,7 @@ public function testFillUp(string $overrideCycleAsString, int $diff, IndexOrient self::assertSame( $expected, - $overrideCycle + $overrideCycle ->fillUpTo($total) ->toString() ); @@ -53,23 +51,23 @@ public function testFillUp(string $overrideCycleAsString, int $diff, IndexOrient */ public static function provideCasesForFillUpTest(): iterable { - yield "R1 diff in length" => [ - "overrideCycleAsString" => 'R1:U5N2Y94', 'diff' => 4, 'indexOrientation' => IndexOrientation::FORWARD, "expected" => 'R1:U5N2Y94N4' + yield 'R1 diff in length' => [ + 'overrideCycleAsString' => 'R1:U5N2Y94', 'diff' => 4, 'indexOrientation' => IndexOrientation::FORWARD, 'expected' => 'R1:U5N2Y94N4', ]; - yield "I1 diff in length" => [ - "overrideCycleAsString" => 'I1:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::FORWARD, "expected" => 'I1:I6N2' + yield 'I1 diff in length' => [ + 'overrideCycleAsString' => 'I1:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::FORWARD, 'expected' => 'I1:I6N2', ]; - yield "R1 UMI diff in length" => [ - "overrideCycleAsString" => 'R1:U4N2Y98', 'diff' => 1, 'indexOrientation' => IndexOrientation::FORWARD, "expected" => 'R1:U4N2Y98N1' + yield 'R1 UMI diff in length' => [ + 'overrideCycleAsString' => 'R1:U4N2Y98', 'diff' => 1, 'indexOrientation' => IndexOrientation::FORWARD, 'expected' => 'R1:U4N2Y98N1', ]; - yield "R2 diff in length" => [ - "overrideCycleAsString" => 'R2:Y241', 'diff' => 10, 'indexOrientation' => IndexOrientation::FORWARD, "expected" => 'R2:Y241N10' + yield 'R2 diff in length' => [ + 'overrideCycleAsString' => 'R2:Y241', 'diff' => 10, 'indexOrientation' => IndexOrientation::FORWARD, 'expected' => 'R2:Y241N10', ]; - yield "I2 diff in length - IndexOrientation Forward" => [ - "overrideCycleAsString" => 'I2:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::FORWARD, "expected" => 'I2:N2I6' + yield 'I2 diff in length - IndexOrientation Forward' => [ + 'overrideCycleAsString' => 'I2:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::FORWARD, 'expected' => 'I2:N2I6', ]; - yield "I2 diff in length - IndexOrientation Reverse" => [ - "overrideCycleAsString" => 'I2:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::REVERSE, "expected" => 'I2:I6N2' + yield 'I2 diff in length - IndexOrientation Reverse' => [ + 'overrideCycleAsString' => 'I2:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::REVERSE, 'expected' => 'I2:I6N2', ]; } } diff --git a/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php b/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php index d06bce6d..120d9d90 100644 --- a/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php +++ b/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php @@ -13,13 +13,14 @@ final class OverrideCyclesTest extends TestCase { /** * @param Collection $overrideCyclesList + * * @dataProvider provideCasesForFromStringToString */ #[DataProvider('provideCasesForFromStringToString')] public function testFromStringToString(string $overrideCyclesAsString, Collection $overrideCyclesList, IndexOrientation $indexOrientation, string $expected): void { $overrideCycleCounter = new OverrideCycleCounter( - $overrideCyclesList->map(fn(string $overrideCycleAsString): OverrideCycles => OverrideCycles::fromString($overrideCycleAsString, $indexOrientation)) + $overrideCyclesList->map(fn (string $overrideCycleAsString): OverrideCycles => OverrideCycles::fromString($overrideCycleAsString, $indexOrientation)) ); $overrideCycles = OverrideCycles::fromString($overrideCyclesAsString, $indexOrientation); self::assertSame($expected, $overrideCycles->toString($overrideCycleCounter)); @@ -35,53 +36,53 @@ public function testFromStringToString(string $overrideCyclesAsString, Collectio */ public static function provideCasesForFromStringToString(): iterable { - yield "L1 diff in length" => [ - "overrideCyclesAsString" => 'R1:U5N2Y94;I1:I6;I2:I8;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U5N2Y94;I1:I6;I2:I8;R2:Y251','R1:U5N2Y94;I1:I8;I2:I8;R2:Y251']), + yield 'L1 diff in length' => [ + 'overrideCyclesAsString' => 'R1:U5N2Y94;I1:I6;I2:I8;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U5N2Y94;I1:I6;I2:I8;R2:Y251', 'R1:U5N2Y94;I1:I8;I2:I8;R2:Y251']), 'indexOrientation' => IndexOrientation::FORWARD, - "expected" => 'R1:U5N2Y94;I1:I6N2;I2:I8;R2:Y251' + 'expected' => 'R1:U5N2Y94;I1:I6N2;I2:I8;R2:Y251', ]; - yield "R1 read diff in length" => [ - "overrideCyclesAsString" => 'R1:U5N2Y94;I1:I8;I2:I8;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U5N2Y94;I1:I8;I2:I8;R2:Y251','R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + yield 'R1 read diff in length' => [ + 'overrideCyclesAsString' => 'R1:U5N2Y94;I1:I8;I2:I8;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U5N2Y94;I1:I8;I2:I8;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), 'indexOrientation' => IndexOrientation::FORWARD, - "expected" => 'R1:U5N2Y94N4;I1:I8;I2:I8;R2:Y251' + 'expected' => 'R1:U5N2Y94N4;I1:I8;I2:I8;R2:Y251', ]; - yield "R1 UMI diff in length" => [ - "overrideCyclesAsString" => 'R1:U4N2Y98;I1:I8;I2:I8;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U4N2Y98;I1:I8;I2:I8;R2:Y251','R1:U5N2Y98;I1:I6;I2:I8;R2:Y251']), + yield 'R1 UMI diff in length' => [ + 'overrideCyclesAsString' => 'R1:U4N2Y98;I1:I8;I2:I8;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U4N2Y98;I1:I8;I2:I8;R2:Y251', 'R1:U5N2Y98;I1:I6;I2:I8;R2:Y251']), 'indexOrientation' => IndexOrientation::FORWARD, - "expected" => 'R1:U4N2Y98N1;I1:I8;I2:I8;R2:Y251' + 'expected' => 'R1:U4N2Y98N1;I1:I8;I2:I8;R2:Y251', ]; - yield "R2 read diff in length" => [ - "overrideCyclesAsString" => 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241', - 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I8;R2:Y241','R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + yield 'R2 read diff in length' => [ + 'overrideCyclesAsString' => 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241', + 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I8;R2:Y241', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), 'indexOrientation' => IndexOrientation::FORWARD, - "expected" => 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241N10' + 'expected' => 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241N10', ]; - yield "I2 Changed - Index Forward" => [ - "overrideCyclesAsString" => 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251','R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + yield 'I2 Changed - Index Forward' => [ + 'overrideCyclesAsString' => 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), 'indexOrientation' => IndexOrientation::FORWARD, - "expected" => 'R1:U5N2Y98;I1:I8;I2:N2I6;R2:Y251' + 'expected' => 'R1:U5N2Y98;I1:I8;I2:N2I6;R2:Y251', ]; - yield "I2 Changed - Index Reverse" => [ - "overrideCyclesAsString" => 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251','R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + yield 'I2 Changed - Index Reverse' => [ + 'overrideCyclesAsString' => 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), 'indexOrientation' => IndexOrientation::REVERSE, - "expected" => 'R1:U5N2Y98;I1:I8;I2:I6N2;R2:Y251' + 'expected' => 'R1:U5N2Y98;I1:I8;I2:I6N2;R2:Y251', ]; - yield "R1 changed, I1 Changed, I2 Changed, R2 Changed" => [ - "overrideCyclesAsString" => 'R1:U4N2Y98;I1:I6;I2:I6;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U4N2Y98;I1:I6;I2:I8;R2:Y251','R1:U5N2Y100;I1:I8;I2:I6;R2:Y241']), + yield 'R1 changed, I1 Changed, I2 Changed, R2 Changed' => [ + 'overrideCyclesAsString' => 'R1:U4N2Y98;I1:I6;I2:I6;R2:Y251', + 'overrideCyclesList' => new Collection(['R1:U4N2Y98;I1:I6;I2:I8;R2:Y251', 'R1:U5N2Y100;I1:I8;I2:I6;R2:Y241']), 'indexOrientation' => IndexOrientation::REVERSE, - "expected" => 'R1:U4N2Y98N3;I1:I6N2;I2:I6N2;R2:Y251' + 'expected' => 'R1:U4N2Y98N3;I1:I6N2;I2:I6N2;R2:Y251', ]; } } From 2fc42cfd80acb5272d025deda23f1c0fa2563b99 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Thu, 19 Feb 2026 11:31:59 +0100 Subject: [PATCH 05/40] refactor code to be usable for PHP 7.4 --- .../V2/BclConvert/BclSample.php | 63 ++++++--- .../V2/BclConvert/CycleType.php | 23 +++- .../V2/BclConvert/CycleTypeWithCount.php | 15 ++- .../V2/BclConvert/FlowcellLanes.php | 28 ---- .../V2/BclConvert/FlowcellType.php | 36 +++++ .../V2/BclConvert/NovaSeqX1_5B.php | 16 +++ .../V2/BclConvert/NucleotideType.php | 23 +++- .../V2/BclConvert/OverrideCycle.php | 30 +++-- .../V2/BclConvert/OverrideCycleCounter.php | 28 ++-- .../V2/BclConvert/OverrideCycles.php | 44 ++++-- .../V2/BclConvertSoftwareVersion.php | 17 ++- .../V2/Enums/FastQCompressionFormat.php | 14 +- .../V2/IlluminaSampleSheetVersion2.php | 16 +-- .../V2/IndexOrientation.php | 24 +++- .../V2/InstrumentPlatform.php | 24 +++- .../V2/Sections/BclConvertDataSection.php | 32 +++-- .../V2/Sections/BclConvertSettingsSection.php | 2 +- .../V2/Sections/CloudDataItem.php | 21 ++- .../V2/Sections/CloudDataSection.php | 14 +- .../V2/Sections/HeaderSection.php | 25 +++- .../V2/Sections/ReadsSection.php | 8 +- .../V2/Sections/SimpleKeyValueSection.php | 14 +- .../V2/BclConvertSettingsSectionTest.php | 2 +- .../V2/HeaderSectionTest.php | 8 +- .../V2/IlluminaSampleSheetVersion2Test.php | 126 ++++++++---------- .../V2/OverrideCycleTest.php | 31 ++--- .../V2/OverrideCyclesTest.php | 64 ++++----- 27 files changed, 481 insertions(+), 267 deletions(-) delete mode 100644 src/IlluminaSampleSheet/V2/BclConvert/FlowcellLanes.php create mode 100644 src/IlluminaSampleSheet/V2/BclConvert/FlowcellType.php create mode 100644 src/IlluminaSampleSheet/V2/BclConvert/NovaSeqX1_5B.php diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php index bd5a376a..a08c0faf 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -7,25 +7,59 @@ class BclSample /** @var string */ public const HEADER_ROW = 'Lane,Sample_ID,Index,Index2,OverrideCycles,AdapterRead1,AdapterRead2,BarcodeMismatchesIndex1,BarcodeMismatchesIndex2'; - /** - * @param array $lanes - */ + /** @var FlowcellType */ + public $flowcellType; + + /** @var string */ + public $sampleID; + + /** @var string */ + public $indexRead1; + + /** @var string */ + public $indexRead2; + + /** @var OverrideCycles */ + public $overrideCycles; + + /** @var string */ + public $adapterRead1; + + /** @var string */ + public $adapterRead2; + + /** @var string */ + public $barcodeMismatchesIndex1; + + /** @var string */ + public $barcodeMismatchesIndex2; + public function __construct( - public array $lanes, - public string $sampleID, - public string $indexRead1, - public string $indexRead2, - public OverrideCycles $overrideCycles, - public string $adapterRead1, - public string $adapterRead2, - public string $barcodeMismatchesIndex1, - public string $barcodeMismatchesIndex2, - ) {} + FlowcellType $flowcellType, + string $sampleID, + string $indexRead1, + string $indexRead2, + OverrideCycles $overrideCycles, + string $adapterRead1, + string $adapterRead2, + string $barcodeMismatchesIndex1, + string $barcodeMismatchesIndex2 + ) { + $this->flowcellType = $flowcellType; + $this->sampleID = $sampleID; + $this->indexRead1 = $indexRead1; + $this->indexRead2 = $indexRead2; + $this->overrideCycles = $overrideCycles; + $this->adapterRead1 = $adapterRead1; + $this->adapterRead2 = $adapterRead2; + $this->barcodeMismatchesIndex1 = $barcodeMismatchesIndex1; + $this->barcodeMismatchesIndex2 = $barcodeMismatchesIndex2; + } public function toString(OverrideCycleCounter $overrideCycleCounter): string { $content = []; - foreach ($this->lanes as $lane) { + foreach ($this->flowcellType->lanes as $lane) { $content[] = join( ',', [ @@ -41,7 +75,6 @@ public function toString(OverrideCycleCounter $overrideCycleCounter): string ] ); } - return implode(PHP_EOL, $content); } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php b/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php index c538fd93..922f15fc 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php @@ -2,10 +2,23 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert; -enum CycleType: string +class CycleType { - case READ_CYCLE = 'Y'; - case TRIMMED_CYCLE = 'N'; - case UMI_CYCLE = 'U'; - case INDEX_CYCLE = 'I'; + public const READ_CYCLE = 'Y'; + public const TRIMMED_CYCLE = 'N'; + public const UMI_CYCLE = 'U'; + public const INDEX_CYCLE = 'I'; + + /** @var string */ + public $value; + + public function __construct(string $value) + { + $this->value = $value; + } + + public static function from(string $value): self + { + return new self($value); + } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php b/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php index 78e9ddbf..e21365b4 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php @@ -4,10 +4,17 @@ class CycleTypeWithCount { - public function __construct( - public CycleType $cycleType, - public int $count - ) {} + /** @var CycleType */ + public $cycleType; + + /** @var int */ + public $count; + + public function __construct(CycleType $cycleType, int $count) + { + $this->cycleType = $cycleType; + $this->count = $count; + } public function toString(): string { diff --git a/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLanes.php b/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLanes.php deleted file mode 100644 index 308c44e5..00000000 --- a/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLanes.php +++ /dev/null @@ -1,28 +0,0 @@ - */ - public function all(): array - { - return range(1, $this->value); - } - - /** - * @param array $specificLanes - * - * @return array - */ - public function select(array $specificLanes): array - { - if (count(array_intersect($specificLanes, $this->all())) !== count($specificLanes)) { - throw new FlowcellLaneNotExistsException("Der FlowcellTyp: '{$this->name}' besitzt keine Lane: " . implode(', ', array_diff($specificLanes, $this->all()))); - } - - return $specificLanes; - } -} diff --git a/src/IlluminaSampleSheet/V2/BclConvert/FlowcellType.php b/src/IlluminaSampleSheet/V2/BclConvert/FlowcellType.php new file mode 100644 index 00000000..3c1b342c --- /dev/null +++ b/src/IlluminaSampleSheet/V2/BclConvert/FlowcellType.php @@ -0,0 +1,36 @@ + */ + public array $lanes; + + /** + * @param array|null $lanes + */ + public function __construct(?array $lanes) + { + if($lanes === null){ + $lanes = range(1, $this->totalLaneCount()); + } + $this->validate($lanes); + $this->lanes = $lanes; + } + + /** + * @param array $specificLanes + */ + public function validate(array $specificLanes): void + { + if(count(array_intersect($specificLanes, range(1, $this->totalLaneCount()))) !== count($specificLanes)){ + throw new FlowcellLaneNotExistsException( + "Der FlowcellTyp: '{$this->name()}' besitzt keine Lane: ".implode(', ', array_diff($specificLanes, range(1, $this->totalLaneCount()))) + ); + } + } +} \ No newline at end of file diff --git a/src/IlluminaSampleSheet/V2/BclConvert/NovaSeqX1_5B.php b/src/IlluminaSampleSheet/V2/BclConvert/NovaSeqX1_5B.php new file mode 100644 index 00000000..b7af8b74 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/BclConvert/NovaSeqX1_5B.php @@ -0,0 +1,16 @@ +value = $value; + } + + public static function from(string $value): self + { + return new self($value); + } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php index b1c7bbaf..15919379 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php @@ -7,12 +7,24 @@ class OverrideCycle { - /** @param array $cycleTypeWithCountList */ - public function __construct( - public NucleotideType $nucleotideType, - public array $cycleTypeWithCountList, - public IndexOrientation $indexOrientation - ) {} + /** @var NucleotideType */ + public $nucleotideType; + + /** @var array */ + public $cycleTypeWithCountList; + + /** @var IndexOrientation */ + public $indexOrientation; + + /** + * @param array $cycleTypeWithCountList + */ + public function __construct(NucleotideType $nucleotideType, array $cycleTypeWithCountList, IndexOrientation $indexOrientation) + { + $this->nucleotideType = $nucleotideType; + $this->cycleTypeWithCountList = $cycleTypeWithCountList; + $this->indexOrientation = $indexOrientation; + } public static function fromString( string $nucleotideAndCycleString, @@ -53,11 +65,11 @@ public function fillUpTo(int $fillUpToMaxNucleotideCount): self } $trimmedCycle = new CycleTypeWithCount( - CycleType::TRIMMED_CYCLE, - $fillUpToMaxNucleotideCount - $countOfAllCycleTypes + new CycleType(CycleType::TRIMMED_CYCLE), + ($fillUpToMaxNucleotideCount - $countOfAllCycleTypes) ); - if ($this->nucleotideType === NucleotideType::I2 && $this->indexOrientation === IndexOrientation::FORWARD) { + if ($this->nucleotideType->value === NucleotideType::I2 && $this->indexOrientation->value === IndexOrientation::FORWARD) { array_unshift($this->cycleTypeWithCountList, $trimmedCycle); } else { $this->cycleTypeWithCountList[] = $trimmedCycle; diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php index c84993f8..8d4cbf57 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php @@ -6,10 +6,16 @@ class OverrideCycleCounter { - /** @param Collection $overrideCyclesList */ - public function __construct( - public Collection $overrideCyclesList - ) {} + /** @var Collection */ + public $overrideCyclesList; + + /** + * @param Collection $overrideCyclesList + */ + public function __construct(Collection $overrideCyclesList) + { + $this->overrideCyclesList = $overrideCyclesList; + } public function maxRead1CycleCount(): int { @@ -36,10 +42,9 @@ public function maxIndex1CycleCount(): int public function maxIndex2CycleCount(): int { $max = $this->overrideCyclesList - ->max( - fn (OverrideCycles $overrideCycles): int => $overrideCycles - ->overrideCycleIndex2 - ?->sumCountOfAllCycles() ?? 0 + ->max(fn (OverrideCycles $overrideCycles): int => $overrideCycles->overrideCycleIndex2 !== null + ? $overrideCycles->overrideCycleIndex2->sumCountOfAllCycles() + : 0 ); assert(is_int($max)); @@ -49,10 +54,9 @@ public function maxIndex2CycleCount(): int public function maxRead2CycleCount(): int { $max = $this->overrideCyclesList - ->max( - fn (OverrideCycles $overrideCycles): int => $overrideCycles - ->overrideCycleRead2 - ?->sumCountOfAllCycles() ?? 0 + ->max(fn (OverrideCycles $overrideCycles): int => $overrideCycles->overrideCycleRead2 !== null + ? $overrideCycles->overrideCycleRead2->sumCountOfAllCycles() + : 0 ); assert(is_int($max)); diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php index ada44a59..e6683a1a 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php @@ -6,17 +6,33 @@ class OverrideCycles { + /** @var OverrideCycle */ + public $overrideCycleRead1; + + /** @var OverrideCycle */ + public $overrideCycleIndex1; + + /** @var OverrideCycle|null */ + public $overrideCycleIndex2; + + /** @var OverrideCycle|null */ + public $overrideCycleRead2; + public function __construct( - public OverrideCycle $overrideCycleRead1, - public OverrideCycle $overrideCycleIndex1, - public ?OverrideCycle $overrideCycleIndex2, - public ?OverrideCycle $overrideCycleRead2 - ) {} + OverrideCycle $overrideCycleRead1, + OverrideCycle $overrideCycleIndex1, + ?OverrideCycle $overrideCycleIndex2, + ?OverrideCycle $overrideCycleRead2 + ) { + $this->overrideCycleRead1 = $overrideCycleRead1; + $this->overrideCycleIndex1 = $overrideCycleIndex1; + $this->overrideCycleIndex2 = $overrideCycleIndex2; + $this->overrideCycleRead2 = $overrideCycleRead2; + } public static function fromString(string $overrideCyclesAsString, IndexOrientation $indexOrientation): self { - $overrideCyclesAsArray = explode(';', $overrideCyclesAsString); - + $overrideCyclesAsArray = explode(";", $overrideCyclesAsString); return new self( OverrideCycle::fromString($overrideCyclesAsArray[0], $indexOrientation), OverrideCycle::fromString($overrideCyclesAsArray[1], $indexOrientation), @@ -25,7 +41,7 @@ public static function fromString(string $overrideCyclesAsString, IndexOrientati : null, isset($overrideCyclesAsArray[3]) ? OverrideCycle::fromString($overrideCyclesAsArray[3], $indexOrientation) - : null, + : null ); } @@ -38,12 +54,12 @@ public function toString(OverrideCycleCounter $overrideCycleCounter): string $this->overrideCycleIndex1 ->fillUpTo($overrideCycleCounter->maxIndex1CycleCount()) ->toString(), - $this->overrideCycleIndex2 - ?->fillUpTo($overrideCycleCounter->maxIndex2CycleCount()) - ?->toString() ?? null, - $this->overrideCycleRead2 - ?->fillUpTo($overrideCycleCounter->maxRead2CycleCount()) - ?->toString() ?? null, + $this->overrideCycleIndex2 !== null + ? $this->overrideCycleIndex2->fillUpTo($overrideCycleCounter->maxIndex2CycleCount())->toString() + : null, + $this->overrideCycleRead2 !== null + ? $this->overrideCycleRead2->fillUpTo($overrideCycleCounter->maxRead2CycleCount())->toString() + : null, ]); return implode(';', $filledParts); diff --git a/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php b/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php index 22a08852..d819b3a5 100644 --- a/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php +++ b/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php @@ -2,7 +2,20 @@ namespace MLL\Utils\IlluminaSampleSheet\V2; -enum BclConvertSoftwareVersion: string +class BclConvertSoftwareVersion { - case V4_1_23 = '4.1.23'; + public const V4_1_23 = '4.1.23'; + + /** @var string */ + public $value; + + public function __construct(string $value) + { + $this->value = $value; + } + + public static function V4_1_23(): self + { + return new self(self::V4_1_23); + } } diff --git a/src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php b/src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php index 7cf8e490..7f21d94c 100644 --- a/src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php +++ b/src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php @@ -2,8 +2,16 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\Enums; -enum FastQCompressionFormat: string +class FastQCompressionFormat { - case GZIP = 'gzip'; - case DRAGEN = 'dragen'; + public const GZIP = 'gzip'; + public const DRAGEN = 'dragen'; + + /** @var string */ + public $value; + + public function __construct(string $value) + { + $this->value = $value; + } } diff --git a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php index 54d13ed5..ea1be133 100644 --- a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php +++ b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php @@ -12,26 +12,26 @@ /** * Illumina SampleSheet Documentation - * https://help.connected.illumina.com/run-set-up/overview/sample-sheet-structure/bcl-convert-interactive-sample-sheet. + * https://help.connected.illumina.com/run-set-up/overview/sample-sheet-structure/bcl-convert-interactive-sample-sheet */ final class IlluminaSampleSheetVersion2 extends BaseSampleSheet { public function __construct( - HeaderSection $headerSection, - ReadsSection $readsSection, + HeaderSection $headerSection, + ReadsSection $readsSection, BclConvertSettingsSection $bclConvertSettingsSection, - BclConvertDataSection $bclConvertDataSection, - ?CloudSettingsSection $cloudSettingsSection, - ?CloudDataSection $cloudDataSection, + BclConvertDataSection $bclConvertDataSection, + ?CloudSettingsSection $cloudSettingsSection, + ?CloudDataSection $cloudDataSection ) { $this->addSection($headerSection); $this->addSection($readsSection); $this->addSection($bclConvertSettingsSection); $this->addSection($bclConvertDataSection); - if ($cloudSettingsSection instanceof CloudSettingsSection) { + if($cloudSettingsSection instanceof CloudSettingsSection){ $this->addSection($cloudSettingsSection); } - if ($cloudDataSection instanceof CloudDataSection) { + if($cloudDataSection instanceof CloudDataSection){ $this->addSection($cloudDataSection); } } diff --git a/src/IlluminaSampleSheet/V2/IndexOrientation.php b/src/IlluminaSampleSheet/V2/IndexOrientation.php index 1257f360..0da152c6 100644 --- a/src/IlluminaSampleSheet/V2/IndexOrientation.php +++ b/src/IlluminaSampleSheet/V2/IndexOrientation.php @@ -2,8 +2,26 @@ namespace MLL\Utils\IlluminaSampleSheet\V2; -enum IndexOrientation: string +class IndexOrientation { - case FORWARD = 'Forward'; - case REVERSE = 'Reverse'; + public const FORWARD = 'Forward'; + public const REVERSE = 'Reverse'; + + /** @var string */ + public $value; + + public function __construct(string $value) + { + $this->value = $value; + } + + public static function FORWARD(): self + { + return new self(self::FORWARD); + } + + public static function REVERSE(): self + { + return new self(self::REVERSE); + } } diff --git a/src/IlluminaSampleSheet/V2/InstrumentPlatform.php b/src/IlluminaSampleSheet/V2/InstrumentPlatform.php index 233e9d74..35a5825b 100644 --- a/src/IlluminaSampleSheet/V2/InstrumentPlatform.php +++ b/src/IlluminaSampleSheet/V2/InstrumentPlatform.php @@ -2,8 +2,26 @@ namespace MLL\Utils\IlluminaSampleSheet\V2; -enum InstrumentPlatform: string +class InstrumentPlatform { - case NOVASEQ_X_SERIES = 'NovaSeqXSeries'; - case MISEQ_I100_SERIES = 'MiSeqi100Series'; + public const NOVASEQ_X_SERIES = 'NovaSeqXSeries'; + public const MISEQ_I100_SERIES = 'MiSeqi100Series'; + + /** @var string */ + public $value; + + public function __construct(string $value) + { + $this->value = $value; + } + + public static function NOVASEQ_X_SERIES(): self + { + return new self(self::NOVASEQ_X_SERIES); + } + + public static function MISEQ_I100_SERIES(): self + { + return new self(self::MISEQ_I100_SERIES); + } } diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php index 24d858b4..4452b849 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php @@ -7,29 +7,41 @@ use MLL\Utils\IlluminaSampleSheet\Section; use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\BclSample; use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\OverrideCycleCounter; +use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\OverrideCycles; class BclConvertDataSection implements Section { - /** @param Collection $dataRows */ - public function __construct( - public Collection $dataRows, - public OverrideCycleCounter $overrideCycleCounter - ) {} + /** @var Collection */ + public $bclSampleList; + + /** @var OverrideCycleCounter */ + public $overrideCycleCounter; + + /** + * @param Collection $bclSampleList + */ + public function __construct(Collection $bclSampleList) + { + $this->bclSampleList = $bclSampleList; + $this->overrideCycleCounter = new OverrideCycleCounter( + $this->bclSampleList->map(fn (BclSample $bclSample): OverrideCycles => $bclSample->overrideCycles) + ); + } public function convertSectionToString(): string { $this->assertNotEmpty(); return - BclSample::HEADER_ROW . PHP_EOL - . $this->dataRows - ->map(fn (BclSample $bclSample): string => $bclSample->toString($this->overrideCycleCounter)) - ->join(PHP_EOL) . PHP_EOL; + BclSample::HEADER_ROW . PHP_EOL . + $this->bclSampleList + ->map(fn (BclSample $bclSample): string => $bclSample->toString($this->overrideCycleCounter)) + ->join(PHP_EOL) . PHP_EOL; } public function assertNotEmpty(): void { - if ($this->dataRows->isEmpty()) { + if ($this->bclSampleList->isEmpty()) { throw new IlluminaSampleSheetException('At least one sample must be added to the DataSection.'); } } diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php index c9a2a676..df677866 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php @@ -12,7 +12,7 @@ public function __construct( ?BclConvertSoftwareVersion $bclConvertSoftwareVersion ) { $fields = new Collection([ - 'FastqCompressionFormat' => FastQCompressionFormat::GZIP->value, + 'FastqCompressionFormat' => FastQCompressionFormat::GZIP, 'GenerateFastqcMetrics' => 'true', ]); if ($bclConvertSoftwareVersion instanceof BclConvertSoftwareVersion) { diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php index 4fcd3c15..f0210325 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php @@ -4,11 +4,24 @@ final class CloudDataItem { + /** @var string */ + public $bioSampleName; + + /** @var string */ + public $projectName; + + /** @var string */ + public $libraryName; + public function __construct( - public string $bioSampleName, - public string $projectName, - public string $libraryName, - ) {} + string $bioSampleName, + string $projectName, + string $libraryName + ) { + $this->bioSampleName = $bioSampleName; + $this->projectName = $projectName; + $this->libraryName = $libraryName; + } public function toString(): string { diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php index a05a38ba..df40f929 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php @@ -7,10 +7,16 @@ final class CloudDataSection implements Section { - /** @param Collection $cloudDataItems */ - public function __construct( - private readonly Collection $cloudDataItems - ) {} + /** @var Collection */ + private $cloudDataItems; + + /** + * @param Collection $cloudDataItems + */ + public function __construct(Collection $cloudDataItems) + { + $this->cloudDataItems = $cloudDataItems; + } public function convertSectionToString(): string { diff --git a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php index 809c96b3..382b15f1 100644 --- a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php @@ -10,12 +10,29 @@ class HeaderSection extends SimpleKeyValueSection { protected const FILE_FORMAT_VERSION = '2'; + /** @var string */ + public $runName; + + /** @var IndexOrientation */ + public $indexOrientation; + + /** @var InstrumentPlatform */ + public $instrumentPlatform; + + /** @var string|null */ + public $runDescription; + public function __construct( - public string $runName, - public IndexOrientation $indexOrientation, - public InstrumentPlatform $instrumentPlatform, - public ?string $runDescription + string $runName, + IndexOrientation $indexOrientation, + InstrumentPlatform $instrumentPlatform, + ?string $runDescription ) { + $this->runName = $runName; + $this->indexOrientation = $indexOrientation; + $this->instrumentPlatform = $instrumentPlatform; + $this->runDescription = $runDescription; + $fields = new Collection([ 'FileFormatVersion' => self::FILE_FORMAT_VERSION, 'RunName' => $this->runName, diff --git a/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php index 4527654b..a63a5102 100644 --- a/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php @@ -45,10 +45,10 @@ public function __construct( public static function fromOverrideCycleCounter(OverrideCycleCounter $overrideCycleCounter): self { return new self( - read1CycleCount: $overrideCycleCounter->maxRead1CycleCount(), - index1CycleCount: $overrideCycleCounter->maxIndex1CycleCount(), - read2CycleCount: $overrideCycleCounter->maxRead2CycleCount(), - index2CycleCount: $overrideCycleCounter->maxIndex2CycleCount() + $overrideCycleCounter->maxRead1CycleCount(), + $overrideCycleCounter->maxIndex1CycleCount(), + $overrideCycleCounter->maxRead2CycleCount(), + $overrideCycleCounter->maxIndex2CycleCount() ); } diff --git a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php index 56180f54..eb356779 100644 --- a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php @@ -7,10 +7,16 @@ abstract class SimpleKeyValueSection implements Section { - /** @param Collection $keyValues */ - public function __construct( - private readonly Collection $keyValues - ) {} + /** @var Collection */ + private $keyValues; + + /** + * @param Collection $keyValues + */ + public function __construct(Collection $keyValues) + { + $this->keyValues = $keyValues; + } public function convertSectionToString(): string { diff --git a/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php b/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php index 2b83d3d0..e08a81bd 100644 --- a/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php +++ b/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php @@ -11,7 +11,7 @@ final class BclConvertSettingsSectionTest extends TestCase public function testToStringWithSoftwareVersion(): void { $bclConvertSettingsSection = new BclConvertSettingsSection( - BclConvertSoftwareVersion::V4_1_23 + BclConvertSoftwareVersion::V4_1_23() ); $expected = <<<'CSV' diff --git a/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php b/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php index 2a5d7803..5a08f4aa 100644 --- a/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php +++ b/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php @@ -12,10 +12,10 @@ final class HeaderSectionTest extends TestCase public function testToString(): void { $headerSection = new HeaderSection( - runName: 'Test1234', - indexOrientation: IndexOrientation::FORWARD, - instrumentPlatform: InstrumentPlatform::NOVASEQ_X_SERIES, - runDescription: null + 'Test1234', + IndexOrientation::FORWARD(), + InstrumentPlatform::NOVASEQ_X_SERIES(), + null ); $expected = <<<'CSV' diff --git a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php index eb27ae2a..2f35c751 100644 --- a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php +++ b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php @@ -4,7 +4,7 @@ use Illuminate\Support\Collection; use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\BclSample; -use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\OverrideCycleCounter; +use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\NovaSeqX1_5B; use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\OverrideCycles; use MLL\Utils\IlluminaSampleSheet\V2\BclConvertSoftwareVersion; use MLL\Utils\IlluminaSampleSheet\V2\IlluminaSampleSheetVersion2; @@ -23,59 +23,59 @@ final class IlluminaSampleSheetVersion2Test extends TestCase { public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): void { - $indexOrientation = IndexOrientation::FORWARD; + $indexOrientation = IndexOrientation::FORWARD(); $overrideCycles0 = OverrideCycles::fromString('R1:U7N1Y143;I1:I8;I2:I8;R2:U7N1Y143', $indexOrientation); $bclSample0 = new BclSample( - lanes: [1], - sampleID: 'Sample1', - indexRead1: 'Index1', - indexRead2: 'Index2', - overrideCycles: $overrideCycles0, - adapterRead1: 'Adapter1', - adapterRead2: 'Adapter2', - barcodeMismatchesIndex1: '0', - barcodeMismatchesIndex2: '0' + new NovaSeqX1_5B([1]), + 'Sample1', + 'Index1', + 'Index2', + $overrideCycles0, + 'Adapter1', + 'Adapter2', + '0', + '0' ); $overrideCycles1 = OverrideCycles::fromString('R1:Y151;I1:I8;I2:I10;R2:Y151', $indexOrientation); $bclSample1 = new BclSample( - lanes: [2], - sampleID: 'Sample2', - indexRead1: 'Index3', - indexRead2: 'Index4', - overrideCycles: $overrideCycles1, - adapterRead1: 'Adapter3', - adapterRead2: 'Adapter4', - barcodeMismatchesIndex1: '0', - barcodeMismatchesIndex2: '0' + new NovaSeqX1_5B([2]), + 'Sample2', + 'Index3', + 'Index4', + $overrideCycles1, + 'Adapter3', + 'Adapter4', + '0', + '0' ); $overrideCycles2 = OverrideCycles::fromString('R1:Y151;I1:I8;I2:I8;R2:U10N12Y127', $indexOrientation); $bclSample2 = new BclSample( - lanes: [1, 2], - sampleID: 'Sample3', - indexRead1: 'Index5', - indexRead2: 'Index6', - overrideCycles: $overrideCycles2, - adapterRead1: 'Adapter5', - adapterRead2: 'Adapter6', - barcodeMismatchesIndex1: '0', - barcodeMismatchesIndex2: '0' + new NovaSeqX1_5B(null), + 'Sample3', + 'Index5', + 'Index6', + $overrideCycles2, + 'Adapter5', + 'Adapter6', + '0', + '0' ); $overrideCycles3 = OverrideCycles::fromString('R1:Y101;I1:I8;I2:I8;R2:Y101', $indexOrientation); $bclSample3 = new BclSample( - lanes: [7, 8], - sampleID: 'Sample4', - indexRead1: 'Index5', - indexRead2: 'Index6', - overrideCycles: $overrideCycles3, - adapterRead1: 'Adapter5', - adapterRead2: 'Adapter6', - barcodeMismatchesIndex1: '1', - barcodeMismatchesIndex2: '1' + new NovaSeqX1_5B(null), + 'Sample4', + 'Index5', + 'Index6', + $overrideCycles3, + 'Adapter5', + 'Adapter6', + '1', + '1' ); $bclSampleList = new Collection([ @@ -86,40 +86,30 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi ]); $headerSection = new HeaderSection( - runName: 'Run1', - indexOrientation: $indexOrientation, - instrumentPlatform: InstrumentPlatform::NOVASEQ_X_SERIES, - runDescription: null + 'Run1', + $indexOrientation, + InstrumentPlatform::NOVASEQ_X_SERIES(), + null ); - $overrideCycleCounter = new OverrideCycleCounter(new Collection([ - $overrideCycles0, - $overrideCycles1, - $overrideCycles2, - $overrideCycles3, - ])); - - $readsSection = ReadsSection::fromOverrideCycleCounter($overrideCycleCounter); + $bclConvertSettingsSection = new BclConvertSettingsSection(BclConvertSoftwareVersion::V4_1_23()); - $bclConvertSettingsSection = new BclConvertSettingsSection(bclConvertSoftwareVersion: BclConvertSoftwareVersion::V4_1_23); + $bclConvertDataSection = new BclConvertDataSection($bclSampleList); - $bclConvertDataSection = new BclConvertDataSection( - dataRows: $bclSampleList, - overrideCycleCounter: $overrideCycleCounter - ); + $readsSection = ReadsSection::fromOverrideCycleCounter($bclConvertDataSection->overrideCycleCounter); $sampleSheet = new IlluminaSampleSheetVersion2( - headerSection: $headerSection, - readsSection: $readsSection, - bclConvertSettingsSection: $bclConvertSettingsSection, - bclConvertDataSection: $bclConvertDataSection, - cloudSettingsSection: new CloudSettingsSection(), - cloudDataSection: new CloudDataSection(new Collection([ - new CloudDataItem(bioSampleName: $bclSample0->sampleID, projectName: 'test', libraryName: 'foo'), - new CloudDataItem(bioSampleName: $bclSample1->sampleID, projectName: 'test', libraryName: 'foo'), - new CloudDataItem(bioSampleName: $bclSample2->sampleID, projectName: 'test', libraryName: 'foo'), - new CloudDataItem(bioSampleName: $bclSample3->sampleID, projectName: 'test', libraryName: 'foo'), - ])), + $headerSection, + $readsSection, + $bclConvertSettingsSection, + $bclConvertDataSection, + new CloudSettingsSection(), + new CloudDataSection(new Collection([ + new CloudDataItem($bclSample0->sampleID, 'test', 'foo'), + new CloudDataItem($bclSample1->sampleID, 'test', 'foo'), + new CloudDataItem($bclSample2->sampleID, 'test', 'foo'), + new CloudDataItem($bclSample3->sampleID, 'test', 'foo'), + ])) ); $expected = '[Header] @@ -145,8 +135,8 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi 2,Sample2,Index3,Index4,R1:Y151;I1:I8;I2:I10;R2:Y151,Adapter3,Adapter4,0,0 1,Sample3,Index5,Index6,R1:Y151;I1:I8;I2:N2I8;R2:U10N12Y127N2,Adapter5,Adapter6,0,0 2,Sample3,Index5,Index6,R1:Y151;I1:I8;I2:N2I8;R2:U10N12Y127N2,Adapter5,Adapter6,0,0 -7,Sample4,Index5,Index6,R1:Y101N50;I1:I8;I2:N2I8;R2:Y101N50,Adapter5,Adapter6,1,1 -8,Sample4,Index5,Index6,R1:Y101N50;I1:I8;I2:N2I8;R2:Y101N50,Adapter5,Adapter6,1,1 +1,Sample4,Index5,Index6,R1:Y101N50;I1:I8;I2:N2I8;R2:Y101N50,Adapter5,Adapter6,1,1 +2,Sample4,Index5,Index6,R1:Y101N50;I1:I8;I2:N2I8;R2:Y101N50,Adapter5,Adapter6,1,1 [Cloud_Settings] GeneratedVersion,2.6.0.202308300002 diff --git a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php index 2c7781de..b898d545 100644 --- a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php +++ b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php @@ -11,22 +11,24 @@ final class OverrideCycleTest extends TestCase { public function testFromString(): void { - $overrideCycle = OverrideCycle::fromString('R1:Y251', IndexOrientation::FORWARD); + $overrideCycle = OverrideCycle::fromString('R1:Y251', IndexOrientation::FORWARD()); self::assertCount(1, $overrideCycle->cycleTypeWithCountList); self::assertSame(251, $overrideCycle->cycleTypeWithCountList[0]->count); - $overrideCycle = OverrideCycle::fromString('I2:I6', IndexOrientation::FORWARD); + $overrideCycle = OverrideCycle::fromString('I2:I6', IndexOrientation::FORWARD()); self::assertCount(1, $overrideCycle->cycleTypeWithCountList); self::assertSame(6, $overrideCycle->cycleTypeWithCountList[0]->count); - $overrideCycle = OverrideCycle::fromString('R1:U5N2Y94', IndexOrientation::FORWARD); + $overrideCycle = OverrideCycle::fromString('R1:U5N2Y94', IndexOrientation::FORWARD()); self::assertCount(3, $overrideCycle->cycleTypeWithCountList); self::assertSame(5, $overrideCycle->cycleTypeWithCountList[0]->count); self::assertSame(2, $overrideCycle->cycleTypeWithCountList[1]->count); self::assertSame(94, $overrideCycle->cycleTypeWithCountList[2]->count); } - /** @dataProvider provideCasesForFillUpTest */ + /** + * @dataProvider provideCasesForFillUpTest + */ #[DataProvider('provideCasesForFillUpTest')] public function testFillUp(string $overrideCycleAsString, int $diff, IndexOrientation $indexOrientation, string $expected): void { @@ -35,39 +37,34 @@ public function testFillUp(string $overrideCycleAsString, int $diff, IndexOrient self::assertSame( $expected, - $overrideCycle + $overrideCycle ->fillUpTo($total) ->toString() ); } /** - * @return iterable + * @return iterable */ public static function provideCasesForFillUpTest(): iterable { yield 'R1 diff in length' => [ - 'overrideCycleAsString' => 'R1:U5N2Y94', 'diff' => 4, 'indexOrientation' => IndexOrientation::FORWARD, 'expected' => 'R1:U5N2Y94N4', + 'R1:U5N2Y94', 4, IndexOrientation::FORWARD(), 'R1:U5N2Y94N4', ]; yield 'I1 diff in length' => [ - 'overrideCycleAsString' => 'I1:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::FORWARD, 'expected' => 'I1:I6N2', + 'I1:I6', 2, IndexOrientation::FORWARD(), 'I1:I6N2', ]; yield 'R1 UMI diff in length' => [ - 'overrideCycleAsString' => 'R1:U4N2Y98', 'diff' => 1, 'indexOrientation' => IndexOrientation::FORWARD, 'expected' => 'R1:U4N2Y98N1', + 'R1:U4N2Y98', 1, IndexOrientation::FORWARD(), 'R1:U4N2Y98N1', ]; yield 'R2 diff in length' => [ - 'overrideCycleAsString' => 'R2:Y241', 'diff' => 10, 'indexOrientation' => IndexOrientation::FORWARD, 'expected' => 'R2:Y241N10', + 'R2:Y241', 10, IndexOrientation::FORWARD(), 'R2:Y241N10', ]; yield 'I2 diff in length - IndexOrientation Forward' => [ - 'overrideCycleAsString' => 'I2:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::FORWARD, 'expected' => 'I2:N2I6', + 'I2:I6', 2, IndexOrientation::FORWARD(), 'I2:N2I6', ]; yield 'I2 diff in length - IndexOrientation Reverse' => [ - 'overrideCycleAsString' => 'I2:I6', 'diff' => 2, 'indexOrientation' => IndexOrientation::REVERSE, 'expected' => 'I2:I6N2', + 'I2:I6', 2, IndexOrientation::REVERSE(), 'I2:I6N2', ]; } } diff --git a/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php b/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php index 120d9d90..d8278d8a 100644 --- a/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php +++ b/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php @@ -13,7 +13,6 @@ final class OverrideCyclesTest extends TestCase { /** * @param Collection $overrideCyclesList - * * @dataProvider provideCasesForFromStringToString */ #[DataProvider('provideCasesForFromStringToString')] @@ -27,62 +26,57 @@ public function testFromStringToString(string $overrideCyclesAsString, Collectio } /** - * @return iterable, - * indexOrientation: IndexOrientation, - * expected: string - * }> + * @return iterable, IndexOrientation, string}> */ public static function provideCasesForFromStringToString(): iterable { yield 'L1 diff in length' => [ - 'overrideCyclesAsString' => 'R1:U5N2Y94;I1:I6;I2:I8;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U5N2Y94;I1:I6;I2:I8;R2:Y251', 'R1:U5N2Y94;I1:I8;I2:I8;R2:Y251']), - 'indexOrientation' => IndexOrientation::FORWARD, - 'expected' => 'R1:U5N2Y94;I1:I6N2;I2:I8;R2:Y251', + 'R1:U5N2Y94;I1:I6;I2:I8;R2:Y251', + new Collection(['R1:U5N2Y94;I1:I6;I2:I8;R2:Y251', 'R1:U5N2Y94;I1:I8;I2:I8;R2:Y251']), + IndexOrientation::FORWARD(), + 'R1:U5N2Y94;I1:I6N2;I2:I8;R2:Y251', ]; yield 'R1 read diff in length' => [ - 'overrideCyclesAsString' => 'R1:U5N2Y94;I1:I8;I2:I8;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U5N2Y94;I1:I8;I2:I8;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), - 'indexOrientation' => IndexOrientation::FORWARD, - 'expected' => 'R1:U5N2Y94N4;I1:I8;I2:I8;R2:Y251', + 'R1:U5N2Y94;I1:I8;I2:I8;R2:Y251', + new Collection(['R1:U5N2Y94;I1:I8;I2:I8;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + IndexOrientation::FORWARD(), + 'R1:U5N2Y94N4;I1:I8;I2:I8;R2:Y251', ]; yield 'R1 UMI diff in length' => [ - 'overrideCyclesAsString' => 'R1:U4N2Y98;I1:I8;I2:I8;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U4N2Y98;I1:I8;I2:I8;R2:Y251', 'R1:U5N2Y98;I1:I6;I2:I8;R2:Y251']), - 'indexOrientation' => IndexOrientation::FORWARD, - 'expected' => 'R1:U4N2Y98N1;I1:I8;I2:I8;R2:Y251', + 'R1:U4N2Y98;I1:I8;I2:I8;R2:Y251', + new Collection(['R1:U4N2Y98;I1:I8;I2:I8;R2:Y251', 'R1:U5N2Y98;I1:I6;I2:I8;R2:Y251']), + IndexOrientation::FORWARD(), + 'R1:U4N2Y98N1;I1:I8;I2:I8;R2:Y251', ]; yield 'R2 read diff in length' => [ - 'overrideCyclesAsString' => 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241', - 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I8;R2:Y241', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), - 'indexOrientation' => IndexOrientation::FORWARD, - 'expected' => 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241N10', + 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241', + new Collection(['R1:U5N2Y98;I1:I8;I2:I8;R2:Y241', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + IndexOrientation::FORWARD(), + 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241N10', ]; yield 'I2 Changed - Index Forward' => [ - 'overrideCyclesAsString' => 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), - 'indexOrientation' => IndexOrientation::FORWARD, - 'expected' => 'R1:U5N2Y98;I1:I8;I2:N2I6;R2:Y251', + 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', + new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + IndexOrientation::FORWARD(), + 'R1:U5N2Y98;I1:I8;I2:N2I6;R2:Y251', ]; yield 'I2 Changed - Index Reverse' => [ - 'overrideCyclesAsString' => 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), - 'indexOrientation' => IndexOrientation::REVERSE, - 'expected' => 'R1:U5N2Y98;I1:I8;I2:I6N2;R2:Y251', + 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', + new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + IndexOrientation::REVERSE(), + 'R1:U5N2Y98;I1:I8;I2:I6N2;R2:Y251', ]; yield 'R1 changed, I1 Changed, I2 Changed, R2 Changed' => [ - 'overrideCyclesAsString' => 'R1:U4N2Y98;I1:I6;I2:I6;R2:Y251', - 'overrideCyclesList' => new Collection(['R1:U4N2Y98;I1:I6;I2:I8;R2:Y251', 'R1:U5N2Y100;I1:I8;I2:I6;R2:Y241']), - 'indexOrientation' => IndexOrientation::REVERSE, - 'expected' => 'R1:U4N2Y98N3;I1:I6N2;I2:I6N2;R2:Y251', + 'R1:U4N2Y98;I1:I6;I2:I6;R2:Y251', + new Collection(['R1:U4N2Y98;I1:I6;I2:I8;R2:Y251', 'R1:U5N2Y100;I1:I8;I2:I6;R2:Y241']), + IndexOrientation::REVERSE(), + 'R1:U4N2Y98N3;I1:I6N2;I2:I6N2;R2:Y251', ]; } } From 8dea0a8c8270905e43b60886eb8acf9f28469383 Mon Sep 17 00:00:00 2001 From: KingKong1213 <168984406+KingKong1213@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:37:40 +0000 Subject: [PATCH 06/40] Apply php-cs-fixer changes --- .../V2/BclConvert/BclSample.php | 1 + .../V2/BclConvert/FlowcellType.php | 21 +++++++------------ .../V2/BclConvert/NovaSeqX1_5B.php | 4 ++-- .../V2/BclConvert/OverrideCycle.php | 6 ++---- .../V2/BclConvert/OverrideCycleCounter.php | 10 ++++----- .../V2/BclConvert/OverrideCycles.php | 3 ++- .../V2/IlluminaSampleSheetVersion2.php | 16 +++++++------- .../V2/Sections/BclConvertDataSection.php | 12 +++++------ .../V2/Sections/CloudDataSection.php | 4 +--- .../V2/Sections/SimpleKeyValueSection.php | 4 +--- .../V2/OverrideCycleTest.php | 10 +++------ .../V2/OverrideCyclesTest.php | 5 ++--- 12 files changed, 40 insertions(+), 56 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php index a08c0faf..374fe64a 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -75,6 +75,7 @@ public function toString(OverrideCycleCounter $overrideCycleCounter): string ] ); } + return implode(PHP_EOL, $content); } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/FlowcellType.php b/src/IlluminaSampleSheet/V2/BclConvert/FlowcellType.php index 3c1b342c..6141be01 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/FlowcellType.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/FlowcellType.php @@ -1,36 +1,31 @@ - */ public array $lanes; - /** - * @param array|null $lanes - */ + /** @param array|null $lanes */ public function __construct(?array $lanes) { - if($lanes === null){ + if ($lanes === null) { $lanes = range(1, $this->totalLaneCount()); } $this->validate($lanes); $this->lanes = $lanes; } - /** - * @param array $specificLanes - */ + /** @param array $specificLanes */ public function validate(array $specificLanes): void { - if(count(array_intersect($specificLanes, range(1, $this->totalLaneCount()))) !== count($specificLanes)){ - throw new FlowcellLaneNotExistsException( - "Der FlowcellTyp: '{$this->name()}' besitzt keine Lane: ".implode(', ', array_diff($specificLanes, range(1, $this->totalLaneCount()))) - ); + if (count(array_intersect($specificLanes, range(1, $this->totalLaneCount()))) !== count($specificLanes)) { + throw new FlowcellLaneNotExistsException("Der FlowcellTyp: '{$this->name()}' besitzt keine Lane: " . implode(', ', array_diff($specificLanes, range(1, $this->totalLaneCount())))); } } -} \ No newline at end of file +} diff --git a/src/IlluminaSampleSheet/V2/BclConvert/NovaSeqX1_5B.php b/src/IlluminaSampleSheet/V2/BclConvert/NovaSeqX1_5B.php index b7af8b74..b0449b5d 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/NovaSeqX1_5B.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/NovaSeqX1_5B.php @@ -1,4 +1,4 @@ - $cycleTypeWithCountList - */ + /** @param array $cycleTypeWithCountList */ public function __construct(NucleotideType $nucleotideType, array $cycleTypeWithCountList, IndexOrientation $indexOrientation) { $this->nucleotideType = $nucleotideType; @@ -66,7 +64,7 @@ public function fillUpTo(int $fillUpToMaxNucleotideCount): self $trimmedCycle = new CycleTypeWithCount( new CycleType(CycleType::TRIMMED_CYCLE), - ($fillUpToMaxNucleotideCount - $countOfAllCycleTypes) + $fillUpToMaxNucleotideCount - $countOfAllCycleTypes ); if ($this->nucleotideType->value === NucleotideType::I2 && $this->indexOrientation->value === IndexOrientation::FORWARD) { diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php index 8d4cbf57..68b87604 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php @@ -9,9 +9,7 @@ class OverrideCycleCounter /** @var Collection */ public $overrideCyclesList; - /** - * @param Collection $overrideCyclesList - */ + /** @param Collection $overrideCyclesList */ public function __construct(Collection $overrideCyclesList) { $this->overrideCyclesList = $overrideCyclesList; @@ -42,7 +40,8 @@ public function maxIndex1CycleCount(): int public function maxIndex2CycleCount(): int { $max = $this->overrideCyclesList - ->max(fn (OverrideCycles $overrideCycles): int => $overrideCycles->overrideCycleIndex2 !== null + ->max( + fn (OverrideCycles $overrideCycles): int => $overrideCycles->overrideCycleIndex2 !== null ? $overrideCycles->overrideCycleIndex2->sumCountOfAllCycles() : 0 ); @@ -54,7 +53,8 @@ public function maxIndex2CycleCount(): int public function maxRead2CycleCount(): int { $max = $this->overrideCyclesList - ->max(fn (OverrideCycles $overrideCycles): int => $overrideCycles->overrideCycleRead2 !== null + ->max( + fn (OverrideCycles $overrideCycles): int => $overrideCycles->overrideCycleRead2 !== null ? $overrideCycles->overrideCycleRead2->sumCountOfAllCycles() : 0 ); diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php index e6683a1a..2d49b886 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php @@ -32,7 +32,8 @@ public function __construct( public static function fromString(string $overrideCyclesAsString, IndexOrientation $indexOrientation): self { - $overrideCyclesAsArray = explode(";", $overrideCyclesAsString); + $overrideCyclesAsArray = explode(';', $overrideCyclesAsString); + return new self( OverrideCycle::fromString($overrideCyclesAsArray[0], $indexOrientation), OverrideCycle::fromString($overrideCyclesAsArray[1], $indexOrientation), diff --git a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php index ea1be133..575fba97 100644 --- a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php +++ b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php @@ -12,26 +12,26 @@ /** * Illumina SampleSheet Documentation - * https://help.connected.illumina.com/run-set-up/overview/sample-sheet-structure/bcl-convert-interactive-sample-sheet + * https://help.connected.illumina.com/run-set-up/overview/sample-sheet-structure/bcl-convert-interactive-sample-sheet. */ final class IlluminaSampleSheetVersion2 extends BaseSampleSheet { public function __construct( - HeaderSection $headerSection, - ReadsSection $readsSection, + HeaderSection $headerSection, + ReadsSection $readsSection, BclConvertSettingsSection $bclConvertSettingsSection, - BclConvertDataSection $bclConvertDataSection, - ?CloudSettingsSection $cloudSettingsSection, - ?CloudDataSection $cloudDataSection + BclConvertDataSection $bclConvertDataSection, + ?CloudSettingsSection $cloudSettingsSection, + ?CloudDataSection $cloudDataSection ) { $this->addSection($headerSection); $this->addSection($readsSection); $this->addSection($bclConvertSettingsSection); $this->addSection($bclConvertDataSection); - if($cloudSettingsSection instanceof CloudSettingsSection){ + if ($cloudSettingsSection instanceof CloudSettingsSection) { $this->addSection($cloudSettingsSection); } - if($cloudDataSection instanceof CloudDataSection){ + if ($cloudDataSection instanceof CloudDataSection) { $this->addSection($cloudDataSection); } } diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php index 4452b849..032ddd66 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php @@ -17,9 +17,7 @@ class BclConvertDataSection implements Section /** @var OverrideCycleCounter */ public $overrideCycleCounter; - /** - * @param Collection $bclSampleList - */ + /** @param Collection $bclSampleList */ public function __construct(Collection $bclSampleList) { $this->bclSampleList = $bclSampleList; @@ -33,10 +31,10 @@ public function convertSectionToString(): string $this->assertNotEmpty(); return - BclSample::HEADER_ROW . PHP_EOL . - $this->bclSampleList - ->map(fn (BclSample $bclSample): string => $bclSample->toString($this->overrideCycleCounter)) - ->join(PHP_EOL) . PHP_EOL; + BclSample::HEADER_ROW . PHP_EOL + . $this->bclSampleList + ->map(fn (BclSample $bclSample): string => $bclSample->toString($this->overrideCycleCounter)) + ->join(PHP_EOL) . PHP_EOL; } public function assertNotEmpty(): void diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php index df40f929..90038773 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php @@ -10,9 +10,7 @@ final class CloudDataSection implements Section /** @var Collection */ private $cloudDataItems; - /** - * @param Collection $cloudDataItems - */ + /** @param Collection $cloudDataItems */ public function __construct(Collection $cloudDataItems) { $this->cloudDataItems = $cloudDataItems; diff --git a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php index eb356779..0fc8814e 100644 --- a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php @@ -10,9 +10,7 @@ abstract class SimpleKeyValueSection implements Section /** @var Collection */ private $keyValues; - /** - * @param Collection $keyValues - */ + /** @param Collection $keyValues */ public function __construct(Collection $keyValues) { $this->keyValues = $keyValues; diff --git a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php index b898d545..016137b7 100644 --- a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php +++ b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php @@ -26,9 +26,7 @@ public function testFromString(): void self::assertSame(94, $overrideCycle->cycleTypeWithCountList[2]->count); } - /** - * @dataProvider provideCasesForFillUpTest - */ + /** @dataProvider provideCasesForFillUpTest */ #[DataProvider('provideCasesForFillUpTest')] public function testFillUp(string $overrideCycleAsString, int $diff, IndexOrientation $indexOrientation, string $expected): void { @@ -37,15 +35,13 @@ public function testFillUp(string $overrideCycleAsString, int $diff, IndexOrient self::assertSame( $expected, - $overrideCycle + $overrideCycle ->fillUpTo($total) ->toString() ); } - /** - * @return iterable - */ + /** @return iterable */ public static function provideCasesForFillUpTest(): iterable { yield 'R1 diff in length' => [ diff --git a/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php b/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php index d8278d8a..2f225ef6 100644 --- a/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php +++ b/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php @@ -13,6 +13,7 @@ final class OverrideCyclesTest extends TestCase { /** * @param Collection $overrideCyclesList + * * @dataProvider provideCasesForFromStringToString */ #[DataProvider('provideCasesForFromStringToString')] @@ -25,9 +26,7 @@ public function testFromStringToString(string $overrideCyclesAsString, Collectio self::assertSame($expected, $overrideCycles->toString($overrideCycleCounter)); } - /** - * @return iterable, IndexOrientation, string}> - */ + /** @return iterable, IndexOrientation, string}> */ public static function provideCasesForFromStringToString(): iterable { yield 'L1 diff in length' => [ From 94b5f19c8e57ca17656f8f95be2b51491bca9eee Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Thu, 19 Feb 2026 14:12:29 +0100 Subject: [PATCH 07/40] refactor code to be usable for PHP 7.4 --- .../FlowcellLaneNotExistsException.php | 2 +- .../V2/BclConvert => Flowcells}/FlowcellType.php | 2 +- src/Flowcells/Miseqi100_100M.php | 16 ++++++++++++++++ src/Flowcells/Miseqi100_25M.php | 16 ++++++++++++++++ src/Flowcells/Miseqi100_50M.php | 16 ++++++++++++++++ src/Flowcells/Miseqi100_5M.php | 16 ++++++++++++++++ src/Flowcells/NovaSeqX10B.php | 16 ++++++++++++++++ .../V2/BclConvert => Flowcells}/NovaSeqX1_5B.php | 2 +- src/Flowcells/NovaSeqX25B.php | 16 ++++++++++++++++ src/Flowcells/NovaSeqX5B.php | 16 ++++++++++++++++ .../V2/BclConvert/BclSample.php | 2 ++ .../V2/IlluminaSampleSheetVersion2Test.php | 2 +- 12 files changed, 118 insertions(+), 4 deletions(-) rename src/{IlluminaSampleSheet/V2/BclConvert => Flowcells}/FlowcellLaneNotExistsException.php (62%) rename src/{IlluminaSampleSheet/V2/BclConvert => Flowcells}/FlowcellType.php (94%) create mode 100644 src/Flowcells/Miseqi100_100M.php create mode 100644 src/Flowcells/Miseqi100_25M.php create mode 100644 src/Flowcells/Miseqi100_50M.php create mode 100644 src/Flowcells/Miseqi100_5M.php create mode 100644 src/Flowcells/NovaSeqX10B.php rename src/{IlluminaSampleSheet/V2/BclConvert => Flowcells}/NovaSeqX1_5B.php (80%) create mode 100644 src/Flowcells/NovaSeqX25B.php create mode 100644 src/Flowcells/NovaSeqX5B.php diff --git a/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLaneNotExistsException.php b/src/Flowcells/FlowcellLaneNotExistsException.php similarity index 62% rename from src/IlluminaSampleSheet/V2/BclConvert/FlowcellLaneNotExistsException.php rename to src/Flowcells/FlowcellLaneNotExistsException.php index ea54384f..7d62136f 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/FlowcellLaneNotExistsException.php +++ b/src/Flowcells/FlowcellLaneNotExistsException.php @@ -1,5 +1,5 @@ Date: Thu, 19 Feb 2026 13:13:13 +0000 Subject: [PATCH 08/40] Apply php-cs-fixer changes --- src/Flowcells/Miseqi100_100M.php | 4 ++-- src/Flowcells/Miseqi100_25M.php | 4 ++-- src/Flowcells/Miseqi100_50M.php | 4 ++-- src/Flowcells/Miseqi100_5M.php | 4 ++-- src/Flowcells/NovaSeqX10B.php | 4 ++-- src/Flowcells/NovaSeqX25B.php | 4 ++-- src/Flowcells/NovaSeqX5B.php | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Flowcells/Miseqi100_100M.php b/src/Flowcells/Miseqi100_100M.php index 0bbe0480..b7e0dd47 100644 --- a/src/Flowcells/Miseqi100_100M.php +++ b/src/Flowcells/Miseqi100_100M.php @@ -1,4 +1,4 @@ - Date: Thu, 19 Feb 2026 15:51:57 +0100 Subject: [PATCH 09/40] Fix class name --- src/Flowcells/Miseqi100_5M.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Flowcells/Miseqi100_5M.php b/src/Flowcells/Miseqi100_5M.php index 679898ae..07ef7ec3 100644 --- a/src/Flowcells/Miseqi100_5M.php +++ b/src/Flowcells/Miseqi100_5M.php @@ -2,7 +2,7 @@ namespace MLL\Utils\Flowcells; -class Miseqi1005M extends FlowcellType +class Miseqi100_5M extends FlowcellType { public function totalLaneCount(): int { From 02d74bed8f4a670bf4f480e65ffee7f0985fda1a Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Thu, 19 Feb 2026 17:53:51 +0100 Subject: [PATCH 10/40] Add header row in CloudDataSection --- src/IlluminaSampleSheet/V2/BclConvert/BclSample.php | 3 --- .../V2/Sections/BclConvertDataSection.php | 5 ++++- src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php | 7 ++++++- .../V2/IlluminaSampleSheetVersion2Test.php | 1 + 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php index 2e6763a1..0a21341c 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -6,9 +6,6 @@ class BclSample { - /** @var string */ - public const HEADER_ROW = 'Lane,Sample_ID,Index,Index2,OverrideCycles,AdapterRead1,AdapterRead2,BarcodeMismatchesIndex1,BarcodeMismatchesIndex2'; - /** @var FlowcellType */ public $flowcellType; diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php index 032ddd66..b6092158 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php @@ -11,6 +11,9 @@ class BclConvertDataSection implements Section { + /** @var string */ + public const HEADER_ROW = 'Lane,Sample_ID,Index,Index2,OverrideCycles,AdapterRead1,AdapterRead2,BarcodeMismatchesIndex1,BarcodeMismatchesIndex2'; + /** @var Collection */ public $bclSampleList; @@ -31,7 +34,7 @@ public function convertSectionToString(): string $this->assertNotEmpty(); return - BclSample::HEADER_ROW . PHP_EOL + self::HEADER_ROW . PHP_EOL . $this->bclSampleList ->map(fn (BclSample $bclSample): string => $bclSample->toString($this->overrideCycleCounter)) ->join(PHP_EOL) . PHP_EOL; diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php index 90038773..604e9897 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php @@ -7,6 +7,9 @@ final class CloudDataSection implements Section { + /** @var string */ + public const HEADER_ROW = 'Sample_ID,ProjectName,LibraryName'; + /** @var Collection */ private $cloudDataItems; @@ -18,7 +21,9 @@ public function __construct(Collection $cloudDataItems) public function convertSectionToString(): string { - return $this->cloudDataItems + return + self::HEADER_ROW . PHP_EOL + . $this->cloudDataItems ->map(fn (CloudDataItem $cloudDataItem): string => $cloudDataItem->toString()) ->join(PHP_EOL) . PHP_EOL; } diff --git a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php index d161dca2..92027751 100644 --- a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php +++ b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php @@ -144,6 +144,7 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi BCLConvert_Pipeline,urn:ilmn:ica:pipeline:d5c7e407-d439-48c8-bce5-b7aec225f6a7#BclConvert_v4_1_23_patch1 [Cloud_Data] +Sample_ID,ProjectName,LibraryName Sample1,test,foo Sample2,test,foo Sample3,test,foo From f5e8b7d29dfc1ce96faffad8454f4c95a2cd39d4 Mon Sep 17 00:00:00 2001 From: KingKong1213 <168984406+KingKong1213@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:54:28 +0000 Subject: [PATCH 11/40] Apply php-cs-fixer changes --- src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php index 604e9897..a1d4863c 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php @@ -24,8 +24,8 @@ public function convertSectionToString(): string return self::HEADER_ROW . PHP_EOL . $this->cloudDataItems - ->map(fn (CloudDataItem $cloudDataItem): string => $cloudDataItem->toString()) - ->join(PHP_EOL) . PHP_EOL; + ->map(fn (CloudDataItem $cloudDataItem): string => $cloudDataItem->toString()) + ->join(PHP_EOL) . PHP_EOL; } public function sectionName(): string From 0b67d6163a86c7f3d67feddd1e2ee5528266e690 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Fri, 20 Feb 2026 09:33:28 +0100 Subject: [PATCH 12/40] use typed variables --- .../V2/BclConvert/BclSample.php | 27 +++++++------------ .../V2/BclConvert/CycleType.php | 3 +-- .../V2/BclConvert/CycleTypeWithCount.php | 6 ++--- .../V2/BclConvert/NucleotideType.php | 3 +-- .../V2/BclConvert/OverrideCycle.php | 8 +++--- .../V2/BclConvert/OverrideCycleCounter.php | 2 +- .../V2/BclConvert/OverrideCycles.php | 12 +++------ .../V2/BclConvertSoftwareVersion.php | 3 +-- .../V2/Enums/FastQCompressionFormat.php | 3 +-- .../V2/IndexOrientation.php | 3 +-- .../V2/InstrumentPlatform.php | 3 +-- .../V2/Sections/BclConvertDataSection.php | 5 ++-- .../V2/Sections/CloudDataItem.php | 9 +++---- .../V2/Sections/CloudDataSection.php | 2 +- .../V2/Sections/HeaderSection.php | 12 +++------ .../V2/Sections/SimpleKeyValueSection.php | 2 +- 16 files changed, 36 insertions(+), 67 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php index 0a21341c..565e46e7 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -6,32 +6,23 @@ class BclSample { - /** @var FlowcellType */ - public $flowcellType; + public FlowcellType $flowcellType; - /** @var string */ - public $sampleID; + public string $sampleID; - /** @var string */ - public $indexRead1; + public string $indexRead1; - /** @var string */ - public $indexRead2; + public string $indexRead2; - /** @var OverrideCycles */ - public $overrideCycles; + public OverrideCycles $overrideCycles; - /** @var string */ - public $adapterRead1; + public string $adapterRead1; - /** @var string */ - public $adapterRead2; + public string $adapterRead2; - /** @var string */ - public $barcodeMismatchesIndex1; + public string $barcodeMismatchesIndex1; - /** @var string */ - public $barcodeMismatchesIndex2; + public string $barcodeMismatchesIndex2; public function __construct( FlowcellType $flowcellType, diff --git a/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php b/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php index 922f15fc..adb6ed1d 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php @@ -9,8 +9,7 @@ class CycleType public const UMI_CYCLE = 'U'; public const INDEX_CYCLE = 'I'; - /** @var string */ - public $value; + public string $value; public function __construct(string $value) { diff --git a/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php b/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php index e21365b4..2556e5cf 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/CycleTypeWithCount.php @@ -4,11 +4,9 @@ class CycleTypeWithCount { - /** @var CycleType */ - public $cycleType; + public CycleType $cycleType; - /** @var int */ - public $count; + public int $count; public function __construct(CycleType $cycleType, int $count) { diff --git a/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php b/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php index 734a4511..67a2ab56 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php @@ -9,8 +9,7 @@ class NucleotideType public const I2 = 'I2'; public const R2 = 'R2'; - /** @var string */ - public $value; + public string $value; public function __construct(string $value) { diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php index 6ec86f46..dd7b2577 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php @@ -7,14 +7,12 @@ class OverrideCycle { - /** @var NucleotideType */ - public $nucleotideType; + public NucleotideType $nucleotideType; /** @var array */ - public $cycleTypeWithCountList; + public array $cycleTypeWithCountList; - /** @var IndexOrientation */ - public $indexOrientation; + public IndexOrientation $indexOrientation; /** @param array $cycleTypeWithCountList */ public function __construct(NucleotideType $nucleotideType, array $cycleTypeWithCountList, IndexOrientation $indexOrientation) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php index 68b87604..af11e255 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycleCounter.php @@ -7,7 +7,7 @@ class OverrideCycleCounter { /** @var Collection */ - public $overrideCyclesList; + public Collection $overrideCyclesList; /** @param Collection $overrideCyclesList */ public function __construct(Collection $overrideCyclesList) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php index 2d49b886..5221d703 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php @@ -6,17 +6,13 @@ class OverrideCycles { - /** @var OverrideCycle */ - public $overrideCycleRead1; + public OverrideCycle $overrideCycleRead1; - /** @var OverrideCycle */ - public $overrideCycleIndex1; + public OverrideCycle $overrideCycleIndex1; - /** @var OverrideCycle|null */ - public $overrideCycleIndex2; + public ?OverrideCycle $overrideCycleIndex2; - /** @var OverrideCycle|null */ - public $overrideCycleRead2; + public ?OverrideCycle $overrideCycleRead2; public function __construct( OverrideCycle $overrideCycleRead1, diff --git a/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php b/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php index d819b3a5..67a3dc34 100644 --- a/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php +++ b/src/IlluminaSampleSheet/V2/BclConvertSoftwareVersion.php @@ -6,8 +6,7 @@ class BclConvertSoftwareVersion { public const V4_1_23 = '4.1.23'; - /** @var string */ - public $value; + public string $value; public function __construct(string $value) { diff --git a/src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php b/src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php index 7f21d94c..42b69b82 100644 --- a/src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php +++ b/src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php @@ -7,8 +7,7 @@ class FastQCompressionFormat public const GZIP = 'gzip'; public const DRAGEN = 'dragen'; - /** @var string */ - public $value; + public string $value; public function __construct(string $value) { diff --git a/src/IlluminaSampleSheet/V2/IndexOrientation.php b/src/IlluminaSampleSheet/V2/IndexOrientation.php index 0da152c6..d421c838 100644 --- a/src/IlluminaSampleSheet/V2/IndexOrientation.php +++ b/src/IlluminaSampleSheet/V2/IndexOrientation.php @@ -7,8 +7,7 @@ class IndexOrientation public const FORWARD = 'Forward'; public const REVERSE = 'Reverse'; - /** @var string */ - public $value; + public string $value; public function __construct(string $value) { diff --git a/src/IlluminaSampleSheet/V2/InstrumentPlatform.php b/src/IlluminaSampleSheet/V2/InstrumentPlatform.php index 35a5825b..0d40ce43 100644 --- a/src/IlluminaSampleSheet/V2/InstrumentPlatform.php +++ b/src/IlluminaSampleSheet/V2/InstrumentPlatform.php @@ -7,8 +7,7 @@ class InstrumentPlatform public const NOVASEQ_X_SERIES = 'NovaSeqXSeries'; public const MISEQ_I100_SERIES = 'MiSeqi100Series'; - /** @var string */ - public $value; + public string $value; public function __construct(string $value) { diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php index b6092158..eff4080f 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertDataSection.php @@ -15,10 +15,9 @@ class BclConvertDataSection implements Section public const HEADER_ROW = 'Lane,Sample_ID,Index,Index2,OverrideCycles,AdapterRead1,AdapterRead2,BarcodeMismatchesIndex1,BarcodeMismatchesIndex2'; /** @var Collection */ - public $bclSampleList; + public Collection $bclSampleList; - /** @var OverrideCycleCounter */ - public $overrideCycleCounter; + public OverrideCycleCounter $overrideCycleCounter; /** @param Collection $bclSampleList */ public function __construct(Collection $bclSampleList) diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php index f0210325..574de175 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataItem.php @@ -4,14 +4,11 @@ final class CloudDataItem { - /** @var string */ - public $bioSampleName; + public string $bioSampleName; - /** @var string */ - public $projectName; + public string $projectName; - /** @var string */ - public $libraryName; + public string $libraryName; public function __construct( string $bioSampleName, diff --git a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php index a1d4863c..61dfffd6 100644 --- a/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/CloudDataSection.php @@ -11,7 +11,7 @@ final class CloudDataSection implements Section public const HEADER_ROW = 'Sample_ID,ProjectName,LibraryName'; /** @var Collection */ - private $cloudDataItems; + private Collection $cloudDataItems; /** @param Collection $cloudDataItems */ public function __construct(Collection $cloudDataItems) diff --git a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php index 382b15f1..b956c394 100644 --- a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php @@ -10,17 +10,13 @@ class HeaderSection extends SimpleKeyValueSection { protected const FILE_FORMAT_VERSION = '2'; - /** @var string */ - public $runName; + public string $runName; - /** @var IndexOrientation */ - public $indexOrientation; + public IndexOrientation $indexOrientation; - /** @var InstrumentPlatform */ - public $instrumentPlatform; + public InstrumentPlatform $instrumentPlatform; - /** @var string|null */ - public $runDescription; + public ?string $runDescription; public function __construct( string $runName, diff --git a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php index 0fc8814e..71a72df9 100644 --- a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php @@ -8,7 +8,7 @@ abstract class SimpleKeyValueSection implements Section { /** @var Collection */ - private $keyValues; + private Collection $keyValues; /** @param Collection $keyValues */ public function __construct(Collection $keyValues) From dbf25f42cebf6964b3d608851de523d601aaf470 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Fri, 20 Feb 2026 09:48:26 +0100 Subject: [PATCH 13/40] Refactor function because of code review --- src/Flowcells/FlowcellType.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Flowcells/FlowcellType.php b/src/Flowcells/FlowcellType.php index dd0f5314..7e16f61b 100644 --- a/src/Flowcells/FlowcellType.php +++ b/src/Flowcells/FlowcellType.php @@ -21,11 +21,17 @@ public function __construct(?array $lanes) $this->lanes = $lanes; } - /** @param array $specificLanes */ + /** + * @param array $specificLanes + */ public function validate(array $specificLanes): void { - if (count(array_intersect($specificLanes, range(1, $this->totalLaneCount()))) !== count($specificLanes)) { - throw new FlowcellLaneNotExistsException("Der FlowcellTyp: '{$this->name()}' besitzt keine Lane: " . implode(', ', array_diff($specificLanes, range(1, $this->totalLaneCount())))); + $validLanes = range(1, $this->totalLaneCount()); + $invalidLanes = array_diff($specificLanes, $validLanes); + + if (count($invalidLanes) > 0){ + $invalidLanesAsString = count($invalidLanes) > 1 ? "Lanes: " : "Lane: ".implode(', ', $invalidLanes); + throw new FlowcellLaneNotExistsException("Der Flowcell-Typ: '{$this->name()}' besitzt keine {$invalidLanesAsString}"); } } } From ca9b935c3aa7a4cf52690f954e857ade37885b0a Mon Sep 17 00:00:00 2001 From: KingKong1213 <168984406+KingKong1213@users.noreply.github.com> Date: Fri, 20 Feb 2026 08:48:55 +0000 Subject: [PATCH 14/40] Apply php-cs-fixer changes --- src/Flowcells/FlowcellType.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Flowcells/FlowcellType.php b/src/Flowcells/FlowcellType.php index 7e16f61b..c87f95b6 100644 --- a/src/Flowcells/FlowcellType.php +++ b/src/Flowcells/FlowcellType.php @@ -21,16 +21,14 @@ public function __construct(?array $lanes) $this->lanes = $lanes; } - /** - * @param array $specificLanes - */ + /** @param array $specificLanes */ public function validate(array $specificLanes): void { $validLanes = range(1, $this->totalLaneCount()); $invalidLanes = array_diff($specificLanes, $validLanes); - if (count($invalidLanes) > 0){ - $invalidLanesAsString = count($invalidLanes) > 1 ? "Lanes: " : "Lane: ".implode(', ', $invalidLanes); + if (count($invalidLanes) > 0) { + $invalidLanesAsString = count($invalidLanes) > 1 ? 'Lanes: ' : 'Lane: ' . implode(', ', $invalidLanes); throw new FlowcellLaneNotExistsException("Der Flowcell-Typ: '{$this->name()}' besitzt keine {$invalidLanesAsString}"); } } From 9e614e64940329e5cf6c4e6e9f8191c25ca79317 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Fri, 20 Feb 2026 09:49:41 +0100 Subject: [PATCH 15/40] Use english exception --- src/Flowcells/FlowcellType.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Flowcells/FlowcellType.php b/src/Flowcells/FlowcellType.php index c87f95b6..4bd5e0fe 100644 --- a/src/Flowcells/FlowcellType.php +++ b/src/Flowcells/FlowcellType.php @@ -27,9 +27,9 @@ public function validate(array $specificLanes): void $validLanes = range(1, $this->totalLaneCount()); $invalidLanes = array_diff($specificLanes, $validLanes); - if (count($invalidLanes) > 0) { - $invalidLanesAsString = count($invalidLanes) > 1 ? 'Lanes: ' : 'Lane: ' . implode(', ', $invalidLanes); - throw new FlowcellLaneNotExistsException("Der Flowcell-Typ: '{$this->name()}' besitzt keine {$invalidLanesAsString}"); + if (count($invalidLanes) > 0){ + $invalidLanesAsString = count($invalidLanes) > 1 ? "lanes: " : "lane: ".implode(', ', $invalidLanes); + throw new FlowcellLaneNotExistsException("The Flowcell-Type: '{$this->name()}' doesn't contain {$invalidLanesAsString}"); } } } From 8ebcf1da99e7bc76e490e2f2e8551e4291073937 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Fri, 20 Feb 2026 09:50:42 +0100 Subject: [PATCH 16/40] FlowcellLaneNotExistException -> FlowcellLaneDoesNotExistException --- ...istsException.php => FlowcellLaneDoesNotExistsException.php} | 2 +- src/Flowcells/FlowcellType.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/Flowcells/{FlowcellLaneNotExistsException.php => FlowcellLaneDoesNotExistsException.php} (50%) diff --git a/src/Flowcells/FlowcellLaneNotExistsException.php b/src/Flowcells/FlowcellLaneDoesNotExistsException.php similarity index 50% rename from src/Flowcells/FlowcellLaneNotExistsException.php rename to src/Flowcells/FlowcellLaneDoesNotExistsException.php index 7d62136f..84630155 100644 --- a/src/Flowcells/FlowcellLaneNotExistsException.php +++ b/src/Flowcells/FlowcellLaneDoesNotExistsException.php @@ -2,4 +2,4 @@ namespace MLL\Utils\Flowcells; -class FlowcellLaneNotExistsException extends \Exception {} +class FlowcellLaneDoesNotExistsException extends \Exception {} diff --git a/src/Flowcells/FlowcellType.php b/src/Flowcells/FlowcellType.php index 4bd5e0fe..a25811ba 100644 --- a/src/Flowcells/FlowcellType.php +++ b/src/Flowcells/FlowcellType.php @@ -29,7 +29,7 @@ public function validate(array $specificLanes): void if (count($invalidLanes) > 0){ $invalidLanesAsString = count($invalidLanes) > 1 ? "lanes: " : "lane: ".implode(', ', $invalidLanes); - throw new FlowcellLaneNotExistsException("The Flowcell-Type: '{$this->name()}' doesn't contain {$invalidLanesAsString}"); + throw new FlowcellLaneDoesNotExistsException("The Flowcell-Type: '{$this->name()}' doesn't contain {$invalidLanesAsString}"); } } } From 14d7199a79f85e5c90a7b5360b46d1b58259dbd4 Mon Sep 17 00:00:00 2001 From: KingKong1213 <168984406+KingKong1213@users.noreply.github.com> Date: Fri, 20 Feb 2026 08:51:10 +0000 Subject: [PATCH 17/40] Apply php-cs-fixer changes --- src/Flowcells/FlowcellType.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Flowcells/FlowcellType.php b/src/Flowcells/FlowcellType.php index a25811ba..25822973 100644 --- a/src/Flowcells/FlowcellType.php +++ b/src/Flowcells/FlowcellType.php @@ -27,8 +27,8 @@ public function validate(array $specificLanes): void $validLanes = range(1, $this->totalLaneCount()); $invalidLanes = array_diff($specificLanes, $validLanes); - if (count($invalidLanes) > 0){ - $invalidLanesAsString = count($invalidLanes) > 1 ? "lanes: " : "lane: ".implode(', ', $invalidLanes); + if (count($invalidLanes) > 0) { + $invalidLanesAsString = count($invalidLanes) > 1 ? 'lanes: ' : 'lane: ' . implode(', ', $invalidLanes); throw new FlowcellLaneDoesNotExistsException("The Flowcell-Type: '{$this->name()}' doesn't contain {$invalidLanesAsString}"); } } From a648ce84616b44ddd42d800ce5a6f98283b8f2f5 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Fri, 20 Feb 2026 10:05:40 +0100 Subject: [PATCH 18/40] Use Collection->join instead of implode --- .../V2/BclConvert/BclSample.php | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php index 565e46e7..2b8ea8cd 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -2,26 +2,19 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert; +use Illuminate\Support\Collection; use MLL\Utils\Flowcells\FlowcellType; class BclSample { public FlowcellType $flowcellType; - public string $sampleID; - public string $indexRead1; - public string $indexRead2; - public OverrideCycles $overrideCycles; - public string $adapterRead1; - public string $adapterRead2; - public string $barcodeMismatchesIndex1; - public string $barcodeMismatchesIndex2; public function __construct( @@ -48,10 +41,9 @@ public function __construct( public function toString(OverrideCycleCounter $overrideCycleCounter): string { - $content = []; + $content = new Collection(); foreach ($this->flowcellType->lanes as $lane) { - $content[] = join( - ',', + $bclSampleAsString = join(',', [ $lane, $this->sampleID, @@ -64,8 +56,9 @@ public function toString(OverrideCycleCounter $overrideCycleCounter): string $this->barcodeMismatchesIndex2, ] ); + $content->add($bclSampleAsString); } - return implode(PHP_EOL, $content); + return $content->join(PHP_EOL); } } From 3bc05e8ef098f24e599e34c204132188967492e8 Mon Sep 17 00:00:00 2001 From: KingKong1213 <168984406+KingKong1213@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:06:12 +0000 Subject: [PATCH 19/40] Apply php-cs-fixer changes --- src/IlluminaSampleSheet/V2/BclConvert/BclSample.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php index 2b8ea8cd..5e7bda71 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -8,13 +8,21 @@ class BclSample { public FlowcellType $flowcellType; + public string $sampleID; + public string $indexRead1; + public string $indexRead2; + public OverrideCycles $overrideCycles; + public string $adapterRead1; + public string $adapterRead2; + public string $barcodeMismatchesIndex1; + public string $barcodeMismatchesIndex2; public function __construct( @@ -43,7 +51,8 @@ public function toString(OverrideCycleCounter $overrideCycleCounter): string { $content = new Collection(); foreach ($this->flowcellType->lanes as $lane) { - $bclSampleAsString = join(',', + $bclSampleAsString = join( + ',', [ $lane, $this->sampleID, From 180c7f404903c4b951ac307f2a6746ac3b42f1c9 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Fri, 20 Feb 2026 10:07:55 +0100 Subject: [PATCH 20/40] Change namespaces in tests Remove unused exception --- .../V2/MissingRequiredFieldException.php | 11 ----------- .../V2/BclConvertSettingsSectionTest.php | 2 +- tests/IlluminaSampleSheet/V2/HeaderSectionTest.php | 2 +- tests/IlluminaSampleSheet/V2/OverrideCycleTest.php | 2 +- 4 files changed, 3 insertions(+), 14 deletions(-) delete mode 100644 src/IlluminaSampleSheet/V2/MissingRequiredFieldException.php diff --git a/src/IlluminaSampleSheet/V2/MissingRequiredFieldException.php b/src/IlluminaSampleSheet/V2/MissingRequiredFieldException.php deleted file mode 100644 index f19ac9f3..00000000 --- a/src/IlluminaSampleSheet/V2/MissingRequiredFieldException.php +++ /dev/null @@ -1,11 +0,0 @@ - Date: Fri, 20 Feb 2026 10:12:25 +0100 Subject: [PATCH 21/40] Change namespaces in tests --- tests/FluidXPlate/FluidXScannerTest.php | 2 +- tests/Tecan/BasicCommands/CommentTest.php | 2 +- tests/Tecan/BasicCommands/ReagentDistributionTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/FluidXPlate/FluidXScannerTest.php b/tests/FluidXPlate/FluidXScannerTest.php index b4a5b25a..29ebd9dd 100644 --- a/tests/FluidXPlate/FluidXScannerTest.php +++ b/tests/FluidXPlate/FluidXScannerTest.php @@ -1,6 +1,6 @@ Date: Fri, 20 Feb 2026 11:41:26 +0100 Subject: [PATCH 22/40] Add Cloud and local specific parameters --- .../V2/IlluminaSampleSheetVersion2.php | 8 ++++++++ .../V2/Sections/BclConvertSettingsSection.php | 20 +++++++++++-------- .../V2/Sections/HeaderSection.php | 5 +++++ .../V2/Sections/SimpleKeyValueSection.php | 2 +- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php index 575fba97..433f2a10 100644 --- a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php +++ b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php @@ -24,6 +24,14 @@ public function __construct( ?CloudSettingsSection $cloudSettingsSection, ?CloudDataSection $cloudDataSection ) { + if($cloudSettingsSection instanceof CloudSettingsSection){ + $bclConvertSettingsSection->performAnalysisOnCloud(); + } + else{ + $headerSection->performAnalysisLocal(); + $bclConvertSettingsSection->performAnalysisLocal(); + } + $this->addSection($headerSection); $this->addSection($readsSection); $this->addSection($bclConvertSettingsSection); diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php index df677866..50510c9e 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php @@ -8,20 +8,24 @@ final class BclConvertSettingsSection extends SimpleKeyValueSection { - public function __construct( - ?BclConvertSoftwareVersion $bclConvertSoftwareVersion - ) { + public function __construct() { $fields = new Collection([ - 'FastqCompressionFormat' => FastQCompressionFormat::GZIP, - 'GenerateFastqcMetrics' => 'true', + 'FastqCompressionFormat' => FastQCompressionFormat::GZIP ]); - if ($bclConvertSoftwareVersion instanceof BclConvertSoftwareVersion) { - $fields->put('SoftwareVersion', $bclConvertSoftwareVersion->value); - } parent::__construct($fields); } + public function performAnalysisOnCloud(): void + { + $this->keyValues['SoftwareVersion'] = BclConvertSoftwareVersion::V4_1_23; + } + + public function performAnalysisLocal(): void + { + $this->keyValues['GenerateFastqcMetrics'] = 'true'; + } + public function sectionName(): string { return 'BCLConvert_Settings'; diff --git a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php index b956c394..eae22e8a 100644 --- a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php @@ -47,4 +47,9 @@ public function sectionName(): string { return 'Header'; } + + public function performAnalysisLocal(): void + { + $this->keyValues['AnalysisLocation'] = 'Local'; + } } diff --git a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php index 71a72df9..5c81d53c 100644 --- a/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/SimpleKeyValueSection.php @@ -8,7 +8,7 @@ abstract class SimpleKeyValueSection implements Section { /** @var Collection */ - private Collection $keyValues; + public Collection $keyValues; /** @param Collection $keyValues */ public function __construct(Collection $keyValues) From 765928ee6c2291d6679f544c65c307fb9a308c95 Mon Sep 17 00:00:00 2001 From: KingKong1213 <168984406+KingKong1213@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:41:57 +0000 Subject: [PATCH 23/40] Apply php-cs-fixer changes --- src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php | 5 ++--- .../V2/Sections/BclConvertSettingsSection.php | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php index 433f2a10..ff6b9395 100644 --- a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php +++ b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php @@ -24,10 +24,9 @@ public function __construct( ?CloudSettingsSection $cloudSettingsSection, ?CloudDataSection $cloudDataSection ) { - if($cloudSettingsSection instanceof CloudSettingsSection){ + if ($cloudSettingsSection instanceof CloudSettingsSection) { $bclConvertSettingsSection->performAnalysisOnCloud(); - } - else{ + } else { $headerSection->performAnalysisLocal(); $bclConvertSettingsSection->performAnalysisLocal(); } diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php index 50510c9e..119386a9 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php @@ -8,9 +8,10 @@ final class BclConvertSettingsSection extends SimpleKeyValueSection { - public function __construct() { + public function __construct() + { $fields = new Collection([ - 'FastqCompressionFormat' => FastQCompressionFormat::GZIP + 'FastqCompressionFormat' => FastQCompressionFormat::GZIP, ]); parent::__construct($fields); From 7a38fb8e5bbcbc6cd9241cf73d9d31ccae9fe5d7 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Fri, 20 Feb 2026 11:53:48 +0100 Subject: [PATCH 24/40] Add Cloud and local specific parameters --- .../V2/BclConvertSettingsSectionTest.php | 15 +++++------- .../V2/HeaderSectionTest.php | 23 ++++++++++++++++++- .../V2/IlluminaSampleSheetVersion2Test.php | 3 +-- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php b/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php index 63341f87..e2319ab1 100644 --- a/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php +++ b/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php @@ -2,30 +2,27 @@ namespace MLL\Utils\Tests\IlluminaSampleSheet\V2; -use MLL\Utils\IlluminaSampleSheet\V2\BclConvertSoftwareVersion; use MLL\Utils\IlluminaSampleSheet\V2\Sections\BclConvertSettingsSection; use PHPUnit\Framework\TestCase; final class BclConvertSettingsSectionTest extends TestCase { - public function testToStringWithSoftwareVersion(): void + public function testToStringOnCloud(): void { - $bclConvertSettingsSection = new BclConvertSettingsSection( - BclConvertSoftwareVersion::V4_1_23() - ); - + $bclConvertSettingsSection = new BclConvertSettingsSection(); + $bclConvertSettingsSection->performAnalysisOnCloud(); $expected = <<<'CSV' FastqCompressionFormat,gzip -GenerateFastqcMetrics,true SoftwareVersion,4.1.23 CSV; self::assertSame($expected, $bclConvertSettingsSection->convertSectionToString()); } - public function testToStringWithoutSoftwareVersion(): void + public function testToStringLocal(): void { - $bclConvertSettingsSection = new BclConvertSettingsSection(null); + $bclConvertSettingsSection = new BclConvertSettingsSection(); + $bclConvertSettingsSection->performAnalysisLocal(); $expected = <<<'CSV' FastqCompressionFormat,gzip diff --git a/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php b/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php index e3fac7bf..32c591da 100644 --- a/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php +++ b/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php @@ -9,7 +9,7 @@ final class HeaderSectionTest extends TestCase { - public function testToString(): void + public function testToStringOnCloud(): void { $headerSection = new HeaderSection( 'Test1234', @@ -24,6 +24,27 @@ public function testToString(): void IndexOrientation,Forward InstrumentPlatform,NovaSeqXSeries +CSV; + self::assertSame($expected, $headerSection->convertSectionToString()); + } + + public function testToStringLocal(): void + { + $headerSection = new HeaderSection( + 'Test1234', + IndexOrientation::FORWARD(), + InstrumentPlatform::NOVASEQ_X_SERIES(), + null + ); + $headerSection->performAnalysisLocal(); + + $expected = <<<'CSV' +FileFormatVersion,2 +RunName,Test1234 +IndexOrientation,Forward +InstrumentPlatform,NovaSeqXSeries +AnalysisLocation,Local + CSV; self::assertSame($expected, $headerSection->convertSectionToString()); } diff --git a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php index 92027751..0c35a4c6 100644 --- a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php +++ b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php @@ -92,7 +92,7 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi null ); - $bclConvertSettingsSection = new BclConvertSettingsSection(BclConvertSoftwareVersion::V4_1_23()); + $bclConvertSettingsSection = new BclConvertSettingsSection(); $bclConvertDataSection = new BclConvertDataSection($bclSampleList); @@ -126,7 +126,6 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi [BCLConvert_Settings] FastqCompressionFormat,gzip -GenerateFastqcMetrics,true SoftwareVersion,4.1.23 [BCLConvert_Data] From dd4602ad872ad9a8020184cfc3d245205a7ed9f2 Mon Sep 17 00:00:00 2001 From: KingKong1213 <168984406+KingKong1213@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:54:29 +0000 Subject: [PATCH 25/40] Apply php-cs-fixer changes --- tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php index 0c35a4c6..c0ec8577 100644 --- a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php +++ b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php @@ -6,7 +6,6 @@ use MLL\Utils\Flowcells\NovaSeqX1_5B; use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\BclSample; use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\OverrideCycles; -use MLL\Utils\IlluminaSampleSheet\V2\BclConvertSoftwareVersion; use MLL\Utils\IlluminaSampleSheet\V2\IlluminaSampleSheetVersion2; use MLL\Utils\IlluminaSampleSheet\V2\IndexOrientation; use MLL\Utils\IlluminaSampleSheet\V2\InstrumentPlatform; From 31c1828ee29aaaae28b97403642c54957a4e5fd0 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Mon, 23 Feb 2026 10:52:41 +0100 Subject: [PATCH 26/40] IndexRead2 can be null --- src/IlluminaSampleSheet/V2/BclConvert/BclSample.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php index 5e7bda71..4ab633a3 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -13,7 +13,7 @@ class BclSample public string $indexRead1; - public string $indexRead2; + public ?string $indexRead2; public OverrideCycles $overrideCycles; @@ -29,7 +29,7 @@ public function __construct( FlowcellType $flowcellType, string $sampleID, string $indexRead1, - string $indexRead2, + ?string $indexRead2, OverrideCycles $overrideCycles, string $adapterRead1, string $adapterRead2, @@ -57,7 +57,7 @@ public function toString(OverrideCycleCounter $overrideCycleCounter): string $lane, $this->sampleID, $this->indexRead1, - $this->indexRead2, + $this->indexRead2 ?? '', $this->overrideCycles->toString($overrideCycleCounter), $this->adapterRead1, $this->adapterRead2, From b52268b5cbf6f2e31260ba6485b6bb62de3d1b3a Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Mon, 23 Feb 2026 11:21:00 +0100 Subject: [PATCH 27/40] IndexRead2 can be not be null, instead it can be 0. Change validation on null to 0 --- .../V2/Sections/ReadsSection.php | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php index a63a5102..5a54b28e 100644 --- a/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php @@ -9,34 +9,49 @@ class ReadsSection extends SimpleKeyValueSection { public function __construct( - int $read1CycleCount, - int $index1CycleCount, - ?int $read2CycleCount, - ?int $index2CycleCount + int $maximumCycleCountForRead1, + int $maximumCycleCountForIndex1, + int $maximumCycleCountForRead2, + int $maximumCycleCountForIndex2 ) { - if ($read1CycleCount < 1) { + if ($maximumCycleCountForRead1 < 1) { throw new IlluminaSampleSheetException('Read1Cycles must be a positive integer.'); } - if ($read2CycleCount !== null && $read2CycleCount < 1) { - throw new IlluminaSampleSheetException('Read2Cycles must be a positive integer or null.'); + /** + * Maximum cycle count for read 2 can be 0 (Single Read sequencing mode), but it the maximum cycle count for + * read 2 exists (Paired read sequencing mode) it has to be at least 6 + */ + if ( + $maximumCycleCountForRead2 < 0 + || ($maximumCycleCountForRead2 > 0 && $maximumCycleCountForRead2 < 6) + ){ + throw new IlluminaSampleSheetException('Read2Cycles must be a positive integer.'); } - if ($index1CycleCount < 6) { + + if ($maximumCycleCountForIndex1 < 6){ throw new IlluminaSampleSheetException('Index1Cycles must be at least 6.'); } - if ($index2CycleCount !== null && ($index2CycleCount < 6)) { + /** + * Maximum cycle count for index2 can be 0 (Single Indexing), but it the maximum cycle count for index 2 + * exists (Dual indexing) it has to be at least 6 + */ + if ( + $maximumCycleCountForIndex2 < 0 + || ($maximumCycleCountForIndex2 > 0 && $maximumCycleCountForIndex2 < 6) + ){ throw new IlluminaSampleSheetException('Index2Cycles must be at least 6.'); } $fields = new Collection(); - $fields->put('Read1Cycles', $read1CycleCount); - if (is_int($read2CycleCount)) { - $fields->put('Read2Cycles', $read2CycleCount); + $fields->put('Read1Cycles', $maximumCycleCountForRead1); + if ($maximumCycleCountForRead2 > 0) { + $fields->put('Read2Cycles', $maximumCycleCountForRead2); } - $fields->put('Index1Cycles', $index1CycleCount); - if (is_int($index2CycleCount)) { - $fields->put('Index2Cycles', $index2CycleCount); + $fields->put('Index1Cycles', $maximumCycleCountForIndex1); + if ($maximumCycleCountForIndex2 > 0) { + $fields->put('Index2Cycles', $maximumCycleCountForIndex2); } parent::__construct($fields); From 38f7057f4eaba29c416b7f7775836162689c48f4 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Mon, 23 Feb 2026 11:21:02 +0100 Subject: [PATCH 28/40] IndexRead2 can be not be null, instead it can be 0. Change validation on null to 0 --- src/IlluminaSampleSheet/V2/Sections/HeaderSection.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php index eae22e8a..db8fa1ed 100644 --- a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php @@ -19,13 +19,13 @@ class HeaderSection extends SimpleKeyValueSection public ?string $runDescription; public function __construct( - string $runName, - IndexOrientation $indexOrientation, + string $runName, + IndexOrientation $indexOrientationForIndex1, InstrumentPlatform $instrumentPlatform, - ?string $runDescription + ?string $runDescription ) { $this->runName = $runName; - $this->indexOrientation = $indexOrientation; + $this->indexOrientation = $indexOrientationForIndex1; $this->instrumentPlatform = $instrumentPlatform; $this->runDescription = $runDescription; From aa268e10270e1d648fc99cebd9fd92b83208bc87 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Mon, 23 Feb 2026 11:22:41 +0100 Subject: [PATCH 29/40] IndexRead2 can be not be null, instead it can be 0. Change validation on null to 0 --- src/IlluminaSampleSheet/V2/Sections/HeaderSection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php index db8fa1ed..09990fc8 100644 --- a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php @@ -20,12 +20,12 @@ class HeaderSection extends SimpleKeyValueSection public function __construct( string $runName, - IndexOrientation $indexOrientationForIndex1, + IndexOrientation $indexOrientation, InstrumentPlatform $instrumentPlatform, ?string $runDescription ) { $this->runName = $runName; - $this->indexOrientation = $indexOrientationForIndex1; + $this->indexOrientation = $indexOrientation; $this->instrumentPlatform = $instrumentPlatform; $this->runDescription = $runDescription; From f29e20bd19d9ebf432f4c492c1819c39195ac54c Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Mon, 23 Feb 2026 11:22:58 +0100 Subject: [PATCH 30/40] IndexRead2 can be not be null, instead it can be 0. Change validation on null to 0 --- src/IlluminaSampleSheet/V2/Sections/HeaderSection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php index 09990fc8..eae22e8a 100644 --- a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php @@ -19,10 +19,10 @@ class HeaderSection extends SimpleKeyValueSection public ?string $runDescription; public function __construct( - string $runName, - IndexOrientation $indexOrientation, + string $runName, + IndexOrientation $indexOrientation, InstrumentPlatform $instrumentPlatform, - ?string $runDescription + ?string $runDescription ) { $this->runName = $runName; $this->indexOrientation = $indexOrientation; From 241bb58e4a89bfab1b27a8423c3d7db7f29d28f1 Mon Sep 17 00:00:00 2001 From: KingKong1213 <168984406+KingKong1213@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:23:36 +0000 Subject: [PATCH 31/40] Apply php-cs-fixer changes --- src/IlluminaSampleSheet/V2/Sections/ReadsSection.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php index 5a54b28e..05d14645 100644 --- a/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/ReadsSection.php @@ -19,26 +19,26 @@ public function __construct( } /** * Maximum cycle count for read 2 can be 0 (Single Read sequencing mode), but it the maximum cycle count for - * read 2 exists (Paired read sequencing mode) it has to be at least 6 + * read 2 exists (Paired read sequencing mode) it has to be at least 6. */ if ( $maximumCycleCountForRead2 < 0 || ($maximumCycleCountForRead2 > 0 && $maximumCycleCountForRead2 < 6) - ){ + ) { throw new IlluminaSampleSheetException('Read2Cycles must be a positive integer.'); } - if ($maximumCycleCountForIndex1 < 6){ + if ($maximumCycleCountForIndex1 < 6) { throw new IlluminaSampleSheetException('Index1Cycles must be at least 6.'); } /** * Maximum cycle count for index2 can be 0 (Single Indexing), but it the maximum cycle count for index 2 - * exists (Dual indexing) it has to be at least 6 + * exists (Dual indexing) it has to be at least 6. */ if ( $maximumCycleCountForIndex2 < 0 || ($maximumCycleCountForIndex2 > 0 && $maximumCycleCountForIndex2 < 6) - ){ + ) { throw new IlluminaSampleSheetException('Index2Cycles must be at least 6.'); } From 569c6b7caa204d75925423c42b0899ed20622777 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Mon, 23 Feb 2026 14:08:23 +0100 Subject: [PATCH 32/40] Use public function performAnalysisOn and Enum to ensure either cloud or local machine were set to perform analysis --- .../V2/IlluminaSampleSheetVersion2.php | 7 ++--- .../V2/Sections/AnalysisLocation.php | 26 +++++++++++++++++++ .../V2/Sections/BclConvertSettingsSection.php | 17 ++++++++++-- .../V2/Sections/HeaderSection.php | 2 +- ...ssingAnalysisLocationSelectedException.php | 11 ++++++++ .../RequiresAnalysisLocationTobeSet.php | 15 +++++++++++ .../V2/BclConvertSettingsSectionTest.php | 5 ++-- .../V2/HeaderSectionTest.php | 2 +- 8 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 src/IlluminaSampleSheet/V2/Sections/AnalysisLocation.php create mode 100644 src/IlluminaSampleSheet/V2/Sections/MissingAnalysisLocationSelectedException.php create mode 100644 src/IlluminaSampleSheet/V2/Sections/RequiresAnalysisLocationTobeSet.php diff --git a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php index ff6b9395..26091824 100644 --- a/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php +++ b/src/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2.php @@ -3,6 +3,7 @@ namespace MLL\Utils\IlluminaSampleSheet\V2; use MLL\Utils\IlluminaSampleSheet\BaseSampleSheet; +use MLL\Utils\IlluminaSampleSheet\V2\Sections\AnalysisLocation; use MLL\Utils\IlluminaSampleSheet\V2\Sections\BclConvertDataSection; use MLL\Utils\IlluminaSampleSheet\V2\Sections\BclConvertSettingsSection; use MLL\Utils\IlluminaSampleSheet\V2\Sections\CloudDataSection; @@ -25,10 +26,10 @@ public function __construct( ?CloudDataSection $cloudDataSection ) { if ($cloudSettingsSection instanceof CloudSettingsSection) { - $bclConvertSettingsSection->performAnalysisOnCloud(); + $bclConvertSettingsSection->performAnalysisOn(AnalysisLocation::CLOUD()); } else { - $headerSection->performAnalysisLocal(); - $bclConvertSettingsSection->performAnalysisLocal(); + $headerSection->performAnalysisOnLocalMachine(); + $bclConvertSettingsSection->performAnalysisOn(AnalysisLocation::LOCAL_MACHINE()); } $this->addSection($headerSection); diff --git a/src/IlluminaSampleSheet/V2/Sections/AnalysisLocation.php b/src/IlluminaSampleSheet/V2/Sections/AnalysisLocation.php new file mode 100644 index 00000000..8114a9dd --- /dev/null +++ b/src/IlluminaSampleSheet/V2/Sections/AnalysisLocation.php @@ -0,0 +1,26 @@ +value = $value; + } + + public static function LOCAL_MACHINE(): self + { + return new self(self::LOCAL_MACHINE); + } + + public static function CLOUD(): self + { + return new self(self::CLOUD); + } +} \ No newline at end of file diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php index 119386a9..836acdca 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php @@ -8,6 +8,9 @@ final class BclConvertSettingsSection extends SimpleKeyValueSection { + use RequiresAnalysisLocationTobeSet; + + public function __construct() { $fields = new Collection([ @@ -17,18 +20,28 @@ public function __construct() parent::__construct($fields); } - public function performAnalysisOnCloud(): void + public function performAnalysisOn(AnalysisLocation $analysisLocation): void + { + $this->analysisLocation = $analysisLocation; + $analysisLocation->value === AnalysisLocation::LOCAL_MACHINE + ? $this->performAnalysisOnLocalMachine() + : $this->performAnalysisOnCloud(); + } + + private function performAnalysisOnCloud(): void { $this->keyValues['SoftwareVersion'] = BclConvertSoftwareVersion::V4_1_23; } - public function performAnalysisLocal(): void + private function performAnalysisOnLocalMachine(): void { $this->keyValues['GenerateFastqcMetrics'] = 'true'; } public function sectionName(): string { + $this->checkIfAnalysisLocationIsSet(); + return 'BCLConvert_Settings'; } } diff --git a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php index eae22e8a..7b512fb2 100644 --- a/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/HeaderSection.php @@ -48,7 +48,7 @@ public function sectionName(): string return 'Header'; } - public function performAnalysisLocal(): void + public function performAnalysisOnLocalMachine(): void { $this->keyValues['AnalysisLocation'] = 'Local'; } diff --git a/src/IlluminaSampleSheet/V2/Sections/MissingAnalysisLocationSelectedException.php b/src/IlluminaSampleSheet/V2/Sections/MissingAnalysisLocationSelectedException.php new file mode 100644 index 00000000..4f0c2ade --- /dev/null +++ b/src/IlluminaSampleSheet/V2/Sections/MissingAnalysisLocationSelectedException.php @@ -0,0 +1,11 @@ +analysisLocation === null){ + throw new MissingAnalysisLocationSelectedException(); + } + } +} \ No newline at end of file diff --git a/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php b/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php index e2319ab1..8a7e73d4 100644 --- a/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php +++ b/tests/IlluminaSampleSheet/V2/BclConvertSettingsSectionTest.php @@ -2,6 +2,7 @@ namespace MLL\Utils\Tests\IlluminaSampleSheet\V2; +use MLL\Utils\IlluminaSampleSheet\V2\Sections\AnalysisLocation; use MLL\Utils\IlluminaSampleSheet\V2\Sections\BclConvertSettingsSection; use PHPUnit\Framework\TestCase; @@ -10,7 +11,7 @@ final class BclConvertSettingsSectionTest extends TestCase public function testToStringOnCloud(): void { $bclConvertSettingsSection = new BclConvertSettingsSection(); - $bclConvertSettingsSection->performAnalysisOnCloud(); + $bclConvertSettingsSection->performAnalysisOn(AnalysisLocation::CLOUD()); $expected = <<<'CSV' FastqCompressionFormat,gzip SoftwareVersion,4.1.23 @@ -22,7 +23,7 @@ public function testToStringOnCloud(): void public function testToStringLocal(): void { $bclConvertSettingsSection = new BclConvertSettingsSection(); - $bclConvertSettingsSection->performAnalysisLocal(); + $bclConvertSettingsSection->performAnalysisOn(AnalysisLocation::LOCAL_MACHINE()); $expected = <<<'CSV' FastqCompressionFormat,gzip diff --git a/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php b/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php index 32c591da..a3da9dc4 100644 --- a/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php +++ b/tests/IlluminaSampleSheet/V2/HeaderSectionTest.php @@ -36,7 +36,7 @@ public function testToStringLocal(): void InstrumentPlatform::NOVASEQ_X_SERIES(), null ); - $headerSection->performAnalysisLocal(); + $headerSection->performAnalysisOnLocalMachine(); $expected = <<<'CSV' FileFormatVersion,2 From 1ac5e863b25f18256554d29c16b89d6de877e219 Mon Sep 17 00:00:00 2001 From: KingKong1213 <168984406+KingKong1213@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:09:03 +0000 Subject: [PATCH 33/40] Apply php-cs-fixer changes --- src/IlluminaSampleSheet/V2/Sections/AnalysisLocation.php | 4 ++-- .../V2/Sections/BclConvertSettingsSection.php | 1 - .../Sections/MissingAnalysisLocationSelectedException.php | 4 ++-- .../V2/Sections/RequiresAnalysisLocationTobeSet.php | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/Sections/AnalysisLocation.php b/src/IlluminaSampleSheet/V2/Sections/AnalysisLocation.php index 8114a9dd..e1d8dd61 100644 --- a/src/IlluminaSampleSheet/V2/Sections/AnalysisLocation.php +++ b/src/IlluminaSampleSheet/V2/Sections/AnalysisLocation.php @@ -1,4 +1,4 @@ -analysisLocation === null){ + if ($this->analysisLocation === null) { throw new MissingAnalysisLocationSelectedException(); } } -} \ No newline at end of file +} From 252d8cad157f71d003fa8c2adab8bee5c1767cf0 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Tue, 24 Feb 2026 10:45:52 +0100 Subject: [PATCH 34/40] Code Review suggestion. Prevent to manipulate object, create new object instead --- .../V2/BclConvert/OverrideCycle.php | 8 +++++--- tests/IlluminaSampleSheet/V2/OverrideCycleTest.php | 11 +++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php index dd7b2577..b3b86338 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php @@ -65,13 +65,15 @@ public function fillUpTo(int $fillUpToMaxNucleotideCount): self $fillUpToMaxNucleotideCount - $countOfAllCycleTypes ); + $newCycleTypeWithCountList = $this->cycleTypeWithCountList; + if ($this->nucleotideType->value === NucleotideType::I2 && $this->indexOrientation->value === IndexOrientation::FORWARD) { - array_unshift($this->cycleTypeWithCountList, $trimmedCycle); + array_unshift($newCycleTypeWithCountList, $trimmedCycle); } else { - $this->cycleTypeWithCountList[] = $trimmedCycle; + $newCycleTypeWithCountList[] = $trimmedCycle; } - return $this; + return new self($this->nucleotideType, $newCycleTypeWithCountList, $this->indexOrientation); } public function sumCountOfAllCycles(): int diff --git a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php index 68fea0f7..f974a91e 100644 --- a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php +++ b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php @@ -41,6 +41,17 @@ public function testFillUp(string $overrideCycleAsString, int $diff, IndexOrient ); } + public function testFillUpDoesNotMutateOriginal(): void + { + $overrideCycle = OverrideCycle::fromString('R1:U5N2Y94', IndexOrientation::FORWARD()); + $originalSum = $overrideCycle->sumCountOfAllCycles(); + + $overrideCycle->fillUpTo($originalSum + 4); + + self::assertSame($originalSum, $overrideCycle->sumCountOfAllCycles()); + self::assertCount(3, $overrideCycle->cycleTypeWithCountList); + } + /** @return iterable */ public static function provideCasesForFillUpTest(): iterable { From beac9f56f3f9dd42215cd0a1a75d46d7dff75b89 Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Tue, 24 Feb 2026 11:36:23 +0100 Subject: [PATCH 35/40] Cannot use Tag's in OverrideCycles --- .../V2/BclConvert/OverrideCycle.php | 27 ++++-------- .../V2/BclConvert/OverrideCycles.php | 12 ++++-- .../V2/IlluminaSampleSheetVersion2Test.php | 20 ++++----- .../V2/OverrideCycleTest.php | 38 ++++++++++------- .../V2/OverrideCyclesTest.php | 42 +++++++++---------- 5 files changed, 70 insertions(+), 69 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php index b3b86338..375e4fed 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php @@ -7,26 +7,22 @@ class OverrideCycle { - public NucleotideType $nucleotideType; - /** @var array */ public array $cycleTypeWithCountList; public IndexOrientation $indexOrientation; /** @param array $cycleTypeWithCountList */ - public function __construct(NucleotideType $nucleotideType, array $cycleTypeWithCountList, IndexOrientation $indexOrientation) + public function __construct(array $cycleTypeWithCountList, IndexOrientation $indexOrientation) { - $this->nucleotideType = $nucleotideType; $this->cycleTypeWithCountList = $cycleTypeWithCountList; $this->indexOrientation = $indexOrientation; } public static function fromString( - string $nucleotideAndCycleString, + string $cycleString, IndexOrientation $indexOrientation ): self { - [$nucleotideTypeAsString, $cycleString] = explode(':', $nucleotideAndCycleString); \Safe\preg_match_all('/([YNUI]+)(\d+)/', $cycleString, $matches, PREG_SET_ORDER); if (count($matches) > 4) { @@ -37,10 +33,7 @@ public static function fromString( throw new IlluminaSampleSheetException("Invalid Override Cycle Part. Should have at least 1 part: {$cycleString}."); } - $nucleotideType = NucleotideType::from($nucleotideTypeAsString); - return new self( - $nucleotideType, array_map( fn (array $match): CycleTypeWithCount => new CycleTypeWithCount(CycleType::from($match[1]), (int) $match[2]), $matches @@ -49,7 +42,7 @@ public static function fromString( ); } - public function fillUpTo(int $fillUpToMaxNucleotideCount): self + public function fillUpTo(int $fillUpToMaxNucleotideCount, NucleotideType $nucleotideType): self { $countOfAllCycleTypes = $this->sumCountOfAllCycles(); if ($countOfAllCycleTypes > $fillUpToMaxNucleotideCount) { @@ -67,13 +60,13 @@ public function fillUpTo(int $fillUpToMaxNucleotideCount): self $newCycleTypeWithCountList = $this->cycleTypeWithCountList; - if ($this->nucleotideType->value === NucleotideType::I2 && $this->indexOrientation->value === IndexOrientation::FORWARD) { + if ($nucleotideType->value === NucleotideType::I2 && $this->indexOrientation->value === IndexOrientation::FORWARD) { array_unshift($newCycleTypeWithCountList, $trimmedCycle); } else { $newCycleTypeWithCountList[] = $trimmedCycle; } - return new self($this->nucleotideType, $newCycleTypeWithCountList, $this->indexOrientation); + return new self($newCycleTypeWithCountList, $this->indexOrientation); } public function sumCountOfAllCycles(): int @@ -88,11 +81,9 @@ public function sumCountOfAllCycles(): int public function toString(): string { - return - "{$this->nucleotideType->value}:" - . implode('', array_map( - fn (CycleTypeWithCount $cycle): string => $cycle->toString(), - $this->cycleTypeWithCountList - )); + return implode('', array_map( + fn (CycleTypeWithCount $cycle): string => $cycle->toString(), + $this->cycleTypeWithCountList + )); } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php index 5221d703..9424ec50 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php @@ -46,16 +46,20 @@ public function toString(OverrideCycleCounter $overrideCycleCounter): string { $filledParts = array_filter([ // @phpstan-ignore arrayFilter.strict (we want truthy comparison) $this->overrideCycleRead1 - ->fillUpTo($overrideCycleCounter->maxRead1CycleCount()) + ->fillUpTo($overrideCycleCounter->maxRead1CycleCount(), new NucleotideType(NucleotideType::R1)) ->toString(), $this->overrideCycleIndex1 - ->fillUpTo($overrideCycleCounter->maxIndex1CycleCount()) + ->fillUpTo($overrideCycleCounter->maxIndex1CycleCount(), new NucleotideType(NucleotideType::I1)) ->toString(), $this->overrideCycleIndex2 !== null - ? $this->overrideCycleIndex2->fillUpTo($overrideCycleCounter->maxIndex2CycleCount())->toString() + ? $this->overrideCycleIndex2 + ->fillUpTo($overrideCycleCounter->maxIndex2CycleCount(), new NucleotideType(NucleotideType::I2)) + ->toString() : null, $this->overrideCycleRead2 !== null - ? $this->overrideCycleRead2->fillUpTo($overrideCycleCounter->maxRead2CycleCount())->toString() + ? $this->overrideCycleRead2 + ->fillUpTo($overrideCycleCounter->maxRead2CycleCount(), new NucleotideType(NucleotideType::R2)) + ->toString() : null, ]); diff --git a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php index c0ec8577..6c41cd5e 100644 --- a/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php +++ b/tests/IlluminaSampleSheet/V2/IlluminaSampleSheetVersion2Test.php @@ -24,7 +24,7 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi { $indexOrientation = IndexOrientation::FORWARD(); - $overrideCycles0 = OverrideCycles::fromString('R1:U7N1Y143;I1:I8;I2:I8;R2:U7N1Y143', $indexOrientation); + $overrideCycles0 = OverrideCycles::fromString('U7N1Y143;I8;I8;U7N1Y143', $indexOrientation); $bclSample0 = new BclSample( new NovaSeqX1_5B([1]), @@ -38,7 +38,7 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi '0' ); - $overrideCycles1 = OverrideCycles::fromString('R1:Y151;I1:I8;I2:I10;R2:Y151', $indexOrientation); + $overrideCycles1 = OverrideCycles::fromString('Y151;I8;I10;Y151', $indexOrientation); $bclSample1 = new BclSample( new NovaSeqX1_5B([2]), 'Sample2', @@ -51,7 +51,7 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi '0' ); - $overrideCycles2 = OverrideCycles::fromString('R1:Y151;I1:I8;I2:I8;R2:U10N12Y127', $indexOrientation); + $overrideCycles2 = OverrideCycles::fromString('Y151;I8;I8;U10N12Y127', $indexOrientation); $bclSample2 = new BclSample( new NovaSeqX1_5B(null), 'Sample3', @@ -64,7 +64,7 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi '0' ); - $overrideCycles3 = OverrideCycles::fromString('R1:Y101;I1:I8;I2:I8;R2:Y101', $indexOrientation); + $overrideCycles3 = OverrideCycles::fromString('Y101;I8;I8;Y101', $indexOrientation); $bclSample3 = new BclSample( new NovaSeqX1_5B(null), 'Sample4', @@ -129,12 +129,12 @@ public function testNovaSeqXCloudSampleSheetToStringReturnsExpectedResult(): voi [BCLConvert_Data] Lane,Sample_ID,Index,Index2,OverrideCycles,AdapterRead1,AdapterRead2,BarcodeMismatchesIndex1,BarcodeMismatchesIndex2 -1,Sample1,Index1,Index2,R1:U7N1Y143;I1:I8;I2:N2I8;R2:U7N1Y143,Adapter1,Adapter2,0,0 -2,Sample2,Index3,Index4,R1:Y151;I1:I8;I2:I10;R2:Y151,Adapter3,Adapter4,0,0 -1,Sample3,Index5,Index6,R1:Y151;I1:I8;I2:N2I8;R2:U10N12Y127N2,Adapter5,Adapter6,0,0 -2,Sample3,Index5,Index6,R1:Y151;I1:I8;I2:N2I8;R2:U10N12Y127N2,Adapter5,Adapter6,0,0 -1,Sample4,Index5,Index6,R1:Y101N50;I1:I8;I2:N2I8;R2:Y101N50,Adapter5,Adapter6,1,1 -2,Sample4,Index5,Index6,R1:Y101N50;I1:I8;I2:N2I8;R2:Y101N50,Adapter5,Adapter6,1,1 +1,Sample1,Index1,Index2,U7N1Y143;I8;N2I8;U7N1Y143,Adapter1,Adapter2,0,0 +2,Sample2,Index3,Index4,Y151;I8;I10;Y151,Adapter3,Adapter4,0,0 +1,Sample3,Index5,Index6,Y151;I8;N2I8;U10N12Y127N2,Adapter5,Adapter6,0,0 +2,Sample3,Index5,Index6,Y151;I8;N2I8;U10N12Y127N2,Adapter5,Adapter6,0,0 +1,Sample4,Index5,Index6,Y101N50;I8;N2I8;Y101N50,Adapter5,Adapter6,1,1 +2,Sample4,Index5,Index6,Y101N50;I8;N2I8;Y101N50,Adapter5,Adapter6,1,1 [Cloud_Settings] GeneratedVersion,2.6.0.202308300002 diff --git a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php index f974a91e..d1d7be13 100644 --- a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php +++ b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php @@ -2,6 +2,7 @@ namespace MLL\Utils\Tests\IlluminaSampleSheet\V2; +use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\NucleotideType; use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\OverrideCycle; use MLL\Utils\IlluminaSampleSheet\V2\IndexOrientation; use PHPUnit\Framework\Attributes\DataProvider; @@ -11,67 +12,72 @@ final class OverrideCycleTest extends TestCase { public function testFromString(): void { - $overrideCycle = OverrideCycle::fromString('R1:Y251', IndexOrientation::FORWARD()); + $overrideCycle = OverrideCycle::fromString('Y251', IndexOrientation::FORWARD()); self::assertCount(1, $overrideCycle->cycleTypeWithCountList); self::assertSame(251, $overrideCycle->cycleTypeWithCountList[0]->count); - $overrideCycle = OverrideCycle::fromString('I2:I6', IndexOrientation::FORWARD()); + $overrideCycle = OverrideCycle::fromString('I6', IndexOrientation::FORWARD()); self::assertCount(1, $overrideCycle->cycleTypeWithCountList); self::assertSame(6, $overrideCycle->cycleTypeWithCountList[0]->count); - $overrideCycle = OverrideCycle::fromString('R1:U5N2Y94', IndexOrientation::FORWARD()); + $overrideCycle = OverrideCycle::fromString('U5N2Y94', IndexOrientation::FORWARD()); self::assertCount(3, $overrideCycle->cycleTypeWithCountList); self::assertSame(5, $overrideCycle->cycleTypeWithCountList[0]->count); self::assertSame(2, $overrideCycle->cycleTypeWithCountList[1]->count); self::assertSame(94, $overrideCycle->cycleTypeWithCountList[2]->count); } - /** @dataProvider provideCasesForFillUpTest */ + /** + * @param array{string, NucleotideType} $cycleStringAndNucleotideType + * + * @dataProvider provideCasesForFillUpTest + */ #[DataProvider('provideCasesForFillUpTest')] - public function testFillUp(string $overrideCycleAsString, int $diff, IndexOrientation $indexOrientation, string $expected): void + public function testFillUp(array $cycleStringAndNucleotideType, int $diff, IndexOrientation $indexOrientation, string $expected): void { - $overrideCycle = OverrideCycle::fromString($overrideCycleAsString, $indexOrientation); + [$cycleString, $nucleotideType] = $cycleStringAndNucleotideType; + $overrideCycle = OverrideCycle::fromString($cycleString, $indexOrientation); $total = $overrideCycle->sumCountOfAllCycles() + $diff; self::assertSame( $expected, $overrideCycle - ->fillUpTo($total) + ->fillUpTo($total, $nucleotideType) ->toString() ); } public function testFillUpDoesNotMutateOriginal(): void { - $overrideCycle = OverrideCycle::fromString('R1:U5N2Y94', IndexOrientation::FORWARD()); + $overrideCycle = OverrideCycle::fromString('U5N2Y94', IndexOrientation::FORWARD()); $originalSum = $overrideCycle->sumCountOfAllCycles(); - $overrideCycle->fillUpTo($originalSum + 4); + $overrideCycle->fillUpTo($originalSum + 4, NucleotideType::from(NucleotideType::R1)); self::assertSame($originalSum, $overrideCycle->sumCountOfAllCycles()); self::assertCount(3, $overrideCycle->cycleTypeWithCountList); } - /** @return iterable */ + /** @return iterable */ public static function provideCasesForFillUpTest(): iterable { yield 'R1 diff in length' => [ - 'R1:U5N2Y94', 4, IndexOrientation::FORWARD(), 'R1:U5N2Y94N4', + ['U5N2Y94', NucleotideType::from(NucleotideType::R1)], 4, IndexOrientation::FORWARD(), 'U5N2Y94N4', ]; yield 'I1 diff in length' => [ - 'I1:I6', 2, IndexOrientation::FORWARD(), 'I1:I6N2', + ['I6', NucleotideType::from(NucleotideType::I1)], 2, IndexOrientation::FORWARD(), 'I6N2', ]; yield 'R1 UMI diff in length' => [ - 'R1:U4N2Y98', 1, IndexOrientation::FORWARD(), 'R1:U4N2Y98N1', + ['U4N2Y98', NucleotideType::from(NucleotideType::R1)], 1, IndexOrientation::FORWARD(), 'U4N2Y98N1', ]; yield 'R2 diff in length' => [ - 'R2:Y241', 10, IndexOrientation::FORWARD(), 'R2:Y241N10', + ['Y241', NucleotideType::from(NucleotideType::R2)], 10, IndexOrientation::FORWARD(), 'Y241N10', ]; yield 'I2 diff in length - IndexOrientation Forward' => [ - 'I2:I6', 2, IndexOrientation::FORWARD(), 'I2:N2I6', + ['I6', NucleotideType::from(NucleotideType::I2)], 2, IndexOrientation::FORWARD(), 'N2I6', ]; yield 'I2 diff in length - IndexOrientation Reverse' => [ - 'I2:I6', 2, IndexOrientation::REVERSE(), 'I2:I6N2', + ['I6', NucleotideType::from(NucleotideType::I2)], 2, IndexOrientation::REVERSE(), 'I6N2', ]; } } diff --git a/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php b/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php index 2f225ef6..f0b97ad4 100644 --- a/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php +++ b/tests/IlluminaSampleSheet/V2/OverrideCyclesTest.php @@ -30,52 +30,52 @@ public function testFromStringToString(string $overrideCyclesAsString, Collectio public static function provideCasesForFromStringToString(): iterable { yield 'L1 diff in length' => [ - 'R1:U5N2Y94;I1:I6;I2:I8;R2:Y251', - new Collection(['R1:U5N2Y94;I1:I6;I2:I8;R2:Y251', 'R1:U5N2Y94;I1:I8;I2:I8;R2:Y251']), + 'U5N2Y94;I6;I8;Y251', + new Collection(['U5N2Y94;I6;I8;Y251', 'U5N2Y94;I8;I8;Y251']), IndexOrientation::FORWARD(), - 'R1:U5N2Y94;I1:I6N2;I2:I8;R2:Y251', + 'U5N2Y94;I6N2;I8;Y251', ]; yield 'R1 read diff in length' => [ - 'R1:U5N2Y94;I1:I8;I2:I8;R2:Y251', - new Collection(['R1:U5N2Y94;I1:I8;I2:I8;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + 'U5N2Y94;I8;I8;Y251', + new Collection(['U5N2Y94;I8;I8;Y251', 'U5N2Y98;I8;I8;Y251']), IndexOrientation::FORWARD(), - 'R1:U5N2Y94N4;I1:I8;I2:I8;R2:Y251', + 'U5N2Y94N4;I8;I8;Y251', ]; yield 'R1 UMI diff in length' => [ - 'R1:U4N2Y98;I1:I8;I2:I8;R2:Y251', - new Collection(['R1:U4N2Y98;I1:I8;I2:I8;R2:Y251', 'R1:U5N2Y98;I1:I6;I2:I8;R2:Y251']), + 'U4N2Y98;I8;I8;Y251', + new Collection(['U4N2Y98;I8;I8;Y251', 'U5N2Y98;I6;I8;Y251']), IndexOrientation::FORWARD(), - 'R1:U4N2Y98N1;I1:I8;I2:I8;R2:Y251', + 'U4N2Y98N1;I8;I8;Y251', ]; yield 'R2 read diff in length' => [ - 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241', - new Collection(['R1:U5N2Y98;I1:I8;I2:I8;R2:Y241', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + 'U5N2Y98;I8;I8;Y241', + new Collection(['U5N2Y98;I8;I8;Y241', 'U5N2Y98;I8;I8;Y251']), IndexOrientation::FORWARD(), - 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y241N10', + 'U5N2Y98;I8;I8;Y241N10', ]; yield 'I2 Changed - Index Forward' => [ - 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', - new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + 'U5N2Y98;I8;I6;Y251', + new Collection(['U5N2Y98;I8;I6;Y251', 'U5N2Y98;I8;I8;Y251']), IndexOrientation::FORWARD(), - 'R1:U5N2Y98;I1:I8;I2:N2I6;R2:Y251', + 'U5N2Y98;I8;N2I6;Y251', ]; yield 'I2 Changed - Index Reverse' => [ - 'R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', - new Collection(['R1:U5N2Y98;I1:I8;I2:I6;R2:Y251', 'R1:U5N2Y98;I1:I8;I2:I8;R2:Y251']), + 'U5N2Y98;I8;I6;Y251', + new Collection(['U5N2Y98;I8;I6;Y251', 'U5N2Y98;I8;I8;Y251']), IndexOrientation::REVERSE(), - 'R1:U5N2Y98;I1:I8;I2:I6N2;R2:Y251', + 'U5N2Y98;I8;I6N2;Y251', ]; yield 'R1 changed, I1 Changed, I2 Changed, R2 Changed' => [ - 'R1:U4N2Y98;I1:I6;I2:I6;R2:Y251', - new Collection(['R1:U4N2Y98;I1:I6;I2:I8;R2:Y251', 'R1:U5N2Y100;I1:I8;I2:I6;R2:Y241']), + 'U4N2Y98;I6;I6;Y251', + new Collection(['U4N2Y98;I6;I8;Y251', 'U5N2Y100;I8;I6;Y241']), IndexOrientation::REVERSE(), - 'R1:U4N2Y98N3;I1:I6N2;I2:I6N2;R2:Y251', + 'U4N2Y98N3;I6N2;I6N2;Y251', ]; } } From 0ee28544d929723930786929e83193abd1a3fdfb Mon Sep 17 00:00:00 2001 From: Dennis Haupt Date: Tue, 24 Feb 2026 15:54:17 +0100 Subject: [PATCH 36/40] barcodeMismatchesIndex2 can be empty --- src/IlluminaSampleSheet/V2/BclConvert/BclSample.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php index 4ab633a3..9d2198a2 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -23,7 +23,7 @@ class BclSample public string $barcodeMismatchesIndex1; - public string $barcodeMismatchesIndex2; + public ?string $barcodeMismatchesIndex2; public function __construct( FlowcellType $flowcellType, @@ -34,7 +34,7 @@ public function __construct( string $adapterRead1, string $adapterRead2, string $barcodeMismatchesIndex1, - string $barcodeMismatchesIndex2 + ?string $barcodeMismatchesIndex2 ) { $this->flowcellType = $flowcellType; $this->sampleID = $sampleID; @@ -62,7 +62,7 @@ public function toString(OverrideCycleCounter $overrideCycleCounter): string $this->adapterRead1, $this->adapterRead2, $this->barcodeMismatchesIndex1, - $this->barcodeMismatchesIndex2, + $this->barcodeMismatchesIndex2 ?? '', ] ); $content->add($bclSampleAsString); From a2912c015aa88d7c1188736ad5ae899485d0c995 Mon Sep 17 00:00:00 2001 From: Simon Bigelmayr Date: Wed, 25 Feb 2026 08:26:51 +0100 Subject: [PATCH 37/40] Fix SpreadSheet class name casing to match PhpSpreadsheet Co-Authored-By: Claude Opus 4.6 --- src/Qiaxcel/QiaxcelImport.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Qiaxcel/QiaxcelImport.php b/src/Qiaxcel/QiaxcelImport.php index 2bd5cb9e..b9c0a303 100644 --- a/src/Qiaxcel/QiaxcelImport.php +++ b/src/Qiaxcel/QiaxcelImport.php @@ -27,7 +27,7 @@ public function __construct( $this->valueForEmptyCell = $valueForEmptyCell; } - public function generate(): SpreadSheet + public function generate(): Spreadsheet { $sampleSheetData = []; foreach (array_chunk($this->entries, 12) as $entryChunks) { From 2b3835cf1b584092a4fdb3f4f32c78e68050ae9d Mon Sep 17 00:00:00 2001 From: Simon Bigelmayr Date: Wed, 25 Feb 2026 08:42:49 +0100 Subject: [PATCH 38/40] Address code review feedback - Remove redundant from() factory methods in CycleType and NucleotideType - Fix regex to reject multi-char cycle type matches like YN10 - Replace NucleotideType::from() usages with constructor calls Co-Authored-By: Claude Opus 4.6 --- .../V2/BclConvert/CycleType.php | 4 ---- .../V2/BclConvert/NucleotideType.php | 4 ---- .../V2/BclConvert/OverrideCycle.php | 4 ++-- tests/IlluminaSampleSheet/V2/OverrideCycleTest.php | 14 +++++++------- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php b/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php index adb6ed1d..237f115f 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php @@ -16,8 +16,4 @@ public function __construct(string $value) $this->value = $value; } - public static function from(string $value): self - { - return new self($value); - } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php b/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php index 67a2ab56..a1c57d16 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php @@ -16,8 +16,4 @@ public function __construct(string $value) $this->value = $value; } - public static function from(string $value): self - { - return new self($value); - } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php index 375e4fed..5eace16f 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php @@ -23,7 +23,7 @@ public static function fromString( string $cycleString, IndexOrientation $indexOrientation ): self { - \Safe\preg_match_all('/([YNUI]+)(\d+)/', $cycleString, $matches, PREG_SET_ORDER); + \Safe\preg_match_all('/([YNUI])(\d+)/', $cycleString, $matches, PREG_SET_ORDER); if (count($matches) > 4) { throw new IlluminaSampleSheetException("Invalid Override Cycle Part. Should have less than 4 parts: {$cycleString}."); @@ -35,7 +35,7 @@ public static function fromString( return new self( array_map( - fn (array $match): CycleTypeWithCount => new CycleTypeWithCount(CycleType::from($match[1]), (int) $match[2]), + fn (array $match): CycleTypeWithCount => new CycleTypeWithCount(new CycleType($match[1]), (int) $match[2]), $matches ), $indexOrientation diff --git a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php index d1d7be13..a29a79d7 100644 --- a/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php +++ b/tests/IlluminaSampleSheet/V2/OverrideCycleTest.php @@ -52,7 +52,7 @@ public function testFillUpDoesNotMutateOriginal(): void $overrideCycle = OverrideCycle::fromString('U5N2Y94', IndexOrientation::FORWARD()); $originalSum = $overrideCycle->sumCountOfAllCycles(); - $overrideCycle->fillUpTo($originalSum + 4, NucleotideType::from(NucleotideType::R1)); + $overrideCycle->fillUpTo($originalSum + 4, new NucleotideType(NucleotideType::R1)); self::assertSame($originalSum, $overrideCycle->sumCountOfAllCycles()); self::assertCount(3, $overrideCycle->cycleTypeWithCountList); @@ -62,22 +62,22 @@ public function testFillUpDoesNotMutateOriginal(): void public static function provideCasesForFillUpTest(): iterable { yield 'R1 diff in length' => [ - ['U5N2Y94', NucleotideType::from(NucleotideType::R1)], 4, IndexOrientation::FORWARD(), 'U5N2Y94N4', + ['U5N2Y94', new NucleotideType(NucleotideType::R1)], 4, IndexOrientation::FORWARD(), 'U5N2Y94N4', ]; yield 'I1 diff in length' => [ - ['I6', NucleotideType::from(NucleotideType::I1)], 2, IndexOrientation::FORWARD(), 'I6N2', + ['I6', new NucleotideType(NucleotideType::I1)], 2, IndexOrientation::FORWARD(), 'I6N2', ]; yield 'R1 UMI diff in length' => [ - ['U4N2Y98', NucleotideType::from(NucleotideType::R1)], 1, IndexOrientation::FORWARD(), 'U4N2Y98N1', + ['U4N2Y98', new NucleotideType(NucleotideType::R1)], 1, IndexOrientation::FORWARD(), 'U4N2Y98N1', ]; yield 'R2 diff in length' => [ - ['Y241', NucleotideType::from(NucleotideType::R2)], 10, IndexOrientation::FORWARD(), 'Y241N10', + ['Y241', new NucleotideType(NucleotideType::R2)], 10, IndexOrientation::FORWARD(), 'Y241N10', ]; yield 'I2 diff in length - IndexOrientation Forward' => [ - ['I6', NucleotideType::from(NucleotideType::I2)], 2, IndexOrientation::FORWARD(), 'N2I6', + ['I6', new NucleotideType(NucleotideType::I2)], 2, IndexOrientation::FORWARD(), 'N2I6', ]; yield 'I2 diff in length - IndexOrientation Reverse' => [ - ['I6', NucleotideType::from(NucleotideType::I2)], 2, IndexOrientation::REVERSE(), 'I6N2', + ['I6', new NucleotideType(NucleotideType::I2)], 2, IndexOrientation::REVERSE(), 'I6N2', ]; } } From 9d1aee281a2f05355f13d3b8e7b2f366bbe4c83f Mon Sep 17 00:00:00 2001 From: simbig <26680884+simbig@users.noreply.github.com> Date: Wed, 25 Feb 2026 07:43:23 +0000 Subject: [PATCH 39/40] Apply php-cs-fixer changes --- src/IlluminaSampleSheet/V2/BclConvert/CycleType.php | 1 - src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php b/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php index 237f115f..636e9ba8 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/CycleType.php @@ -15,5 +15,4 @@ public function __construct(string $value) { $this->value = $value; } - } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php b/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php index a1c57d16..44c6adf8 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/NucleotideType.php @@ -15,5 +15,4 @@ public function __construct(string $value) { $this->value = $value; } - } From 6877cbf497ab78662a548bd3954ae5eda12d192b Mon Sep 17 00:00:00 2001 From: Simon Bigelmayr Date: Wed, 25 Feb 2026 12:01:38 +0100 Subject: [PATCH 40/40] Fix bugs, typos, and consistency issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix FlowcellType::validate() error message dropping lane numbers for multi-lane case (ternary operator precedence bug) - Fix uninitialized $analysisLocation property in trait causing TypeError instead of MissingAnalysisLocationSelectedException - Add input validation in OverrideCycles::fromString() for malformed strings with fewer than 2 semicolon-separated parts - Fix error message in OverrideCycle::fromString() ("less than 4" → "at most 4") to match actual validation logic - Rename FlowcellLaneDoesNotExistsException → DoesNotExistException (grammar) - Rename RequiresAnalysisLocationTobeSet → ToBeSet (typo) - Replace join() with implode() and remove unnecessary Collection usage in BclSample::toString() Co-Authored-By: Claude Opus 4.6 --- ... => FlowcellLaneDoesNotExistException.php} | 2 +- src/Flowcells/FlowcellType.php | 8 +++-- .../V2/BclConvert/BclSample.php | 35 ++++++++----------- .../V2/BclConvert/OverrideCycle.php | 2 +- .../V2/BclConvert/OverrideCycles.php | 5 +++ .../V2/Sections/BclConvertSettingsSection.php | 2 +- ...hp => RequiresAnalysisLocationToBeSet.php} | 4 +-- 7 files changed, 31 insertions(+), 27 deletions(-) rename src/Flowcells/{FlowcellLaneDoesNotExistsException.php => FlowcellLaneDoesNotExistException.php} (50%) rename src/IlluminaSampleSheet/V2/Sections/{RequiresAnalysisLocationTobeSet.php => RequiresAnalysisLocationToBeSet.php} (75%) diff --git a/src/Flowcells/FlowcellLaneDoesNotExistsException.php b/src/Flowcells/FlowcellLaneDoesNotExistException.php similarity index 50% rename from src/Flowcells/FlowcellLaneDoesNotExistsException.php rename to src/Flowcells/FlowcellLaneDoesNotExistException.php index 84630155..2300d71c 100644 --- a/src/Flowcells/FlowcellLaneDoesNotExistsException.php +++ b/src/Flowcells/FlowcellLaneDoesNotExistException.php @@ -2,4 +2,4 @@ namespace MLL\Utils\Flowcells; -class FlowcellLaneDoesNotExistsException extends \Exception {} +class FlowcellLaneDoesNotExistException extends \Exception {} diff --git a/src/Flowcells/FlowcellType.php b/src/Flowcells/FlowcellType.php index 25822973..1295266b 100644 --- a/src/Flowcells/FlowcellType.php +++ b/src/Flowcells/FlowcellType.php @@ -28,8 +28,12 @@ public function validate(array $specificLanes): void $invalidLanes = array_diff($specificLanes, $validLanes); if (count($invalidLanes) > 0) { - $invalidLanesAsString = count($invalidLanes) > 1 ? 'lanes: ' : 'lane: ' . implode(', ', $invalidLanes); - throw new FlowcellLaneDoesNotExistsException("The Flowcell-Type: '{$this->name()}' doesn't contain {$invalidLanesAsString}"); + $label = count($invalidLanes) > 1 + ? 'lanes' + : 'lane'; + $invalidLanesAsString = implode(', ', $invalidLanes); + + throw new FlowcellLaneDoesNotExistException("The Flowcell-Type: '{$this->name()}' doesn't contain {$label}: {$invalidLanesAsString}"); } } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php index 9d2198a2..a7b960cf 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -2,7 +2,6 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert; -use Illuminate\Support\Collection; use MLL\Utils\Flowcells\FlowcellType; class BclSample @@ -49,25 +48,21 @@ public function __construct( public function toString(OverrideCycleCounter $overrideCycleCounter): string { - $content = new Collection(); - foreach ($this->flowcellType->lanes as $lane) { - $bclSampleAsString = join( - ',', - [ - $lane, - $this->sampleID, - $this->indexRead1, - $this->indexRead2 ?? '', - $this->overrideCycles->toString($overrideCycleCounter), - $this->adapterRead1, - $this->adapterRead2, - $this->barcodeMismatchesIndex1, - $this->barcodeMismatchesIndex2 ?? '', - ] - ); - $content->add($bclSampleAsString); - } + $lines = array_map( + fn (int $lane): string => implode(',', [ + $lane, + $this->sampleID, + $this->indexRead1, + $this->indexRead2 ?? '', + $this->overrideCycles->toString($overrideCycleCounter), + $this->adapterRead1, + $this->adapterRead2, + $this->barcodeMismatchesIndex1, + $this->barcodeMismatchesIndex2 ?? '', + ]), + $this->flowcellType->lanes + ); - return $content->join(PHP_EOL); + return implode(PHP_EOL, $lines); } } diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php index 5eace16f..032db3b3 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycle.php @@ -26,7 +26,7 @@ public static function fromString( \Safe\preg_match_all('/([YNUI])(\d+)/', $cycleString, $matches, PREG_SET_ORDER); if (count($matches) > 4) { - throw new IlluminaSampleSheetException("Invalid Override Cycle Part. Should have less than 4 parts: {$cycleString}."); + throw new IlluminaSampleSheetException("Invalid Override Cycle Part. Should have at most 4 parts: {$cycleString}."); } if (count($matches) === 0) { diff --git a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php index 9424ec50..5822ae5d 100644 --- a/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php +++ b/src/IlluminaSampleSheet/V2/BclConvert/OverrideCycles.php @@ -2,6 +2,7 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert; +use MLL\Utils\IlluminaSampleSheet\IlluminaSampleSheetException; use MLL\Utils\IlluminaSampleSheet\V2\IndexOrientation; class OverrideCycles @@ -30,6 +31,10 @@ public static function fromString(string $overrideCyclesAsString, IndexOrientati { $overrideCyclesAsArray = explode(';', $overrideCyclesAsString); + if (count($overrideCyclesAsArray) < 2) { + throw new IlluminaSampleSheetException("Invalid OverrideCycles string. Must contain at least 2 semicolon-separated parts (Read1 and Index1): {$overrideCyclesAsString}"); + } + return new self( OverrideCycle::fromString($overrideCyclesAsArray[0], $indexOrientation), OverrideCycle::fromString($overrideCyclesAsArray[1], $indexOrientation), diff --git a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php index a01a9bf6..ed862474 100644 --- a/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php +++ b/src/IlluminaSampleSheet/V2/Sections/BclConvertSettingsSection.php @@ -8,7 +8,7 @@ final class BclConvertSettingsSection extends SimpleKeyValueSection { - use RequiresAnalysisLocationTobeSet; + use RequiresAnalysisLocationToBeSet; public function __construct() { diff --git a/src/IlluminaSampleSheet/V2/Sections/RequiresAnalysisLocationTobeSet.php b/src/IlluminaSampleSheet/V2/Sections/RequiresAnalysisLocationToBeSet.php similarity index 75% rename from src/IlluminaSampleSheet/V2/Sections/RequiresAnalysisLocationTobeSet.php rename to src/IlluminaSampleSheet/V2/Sections/RequiresAnalysisLocationToBeSet.php index bbbf4f50..dbf3c989 100644 --- a/src/IlluminaSampleSheet/V2/Sections/RequiresAnalysisLocationTobeSet.php +++ b/src/IlluminaSampleSheet/V2/Sections/RequiresAnalysisLocationToBeSet.php @@ -2,9 +2,9 @@ namespace MLL\Utils\IlluminaSampleSheet\V2\Sections; -trait RequiresAnalysisLocationTobeSet +trait RequiresAnalysisLocationToBeSet { - public ?AnalysisLocation $analysisLocation; + public ?AnalysisLocation $analysisLocation = null; private function checkIfAnalysisLocationIsSet(): void {