Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Illumina sample sheet v2 #19

Merged
merged 31 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ See [GitHub releases](https://github.com/mll-lab/php-utils/releases).

## Unreleased

## v1.14.0

### Added

- Support creating Illumina NovaSeq Sample Sheets (V2) for NovaSeqX

## v1.13.0

### Added
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ stan: vendor ## Runs a static analysis with phpstan
.PHONY: test
test: vendor ## Runs auto-review, unit, and integration tests with phpunit
mkdir --parents .build/phpunit
vendor/bin/phpunit --cache-result-file=.build/phpunit/result.cache
vendor/bin/phpunit --cache-directory=.build/phpunit

vendor: composer.json
composer validate --strict
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"phpstan/phpstan-phpunit": "^1",
"phpstan/phpstan-strict-rules": "^1",
"phpunit/phpunit": "^9 || ^10 || ^11",
"rector/rector": "^0.17",
"rector/rector": "^1",
"thecodingmachine/phpstan-safe-rule": "^1.2"
},
"suggest": {
Expand Down
4 changes: 4 additions & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use Rector\CodeQuality\Rector\Concat\JoinStringConcatRector;
use Rector\Config\RectorConfig;
use Rector\PHPUnit\Rector\Class_\PreferPHPUnitSelfCallRector;
use Rector\Set\ValueObject\SetList;

return static function (RectorConfig $rectorConfig): void {
Expand All @@ -17,6 +18,9 @@
__DIR__ . '/tests/CSVArrayTest.php', // keep `\r\n` for readability
],
]);

$rectorConfig->rule(PreferPHPUnitSelfCallRector::class);

$rectorConfig->paths([__DIR__ . '/src', __DIR__ . '/tests']);
$rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon');
};
5 changes: 5 additions & 0 deletions src/IlluminaSampleSheet/IlluminaSampleSheetException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php declare(strict_types=1);

namespace MLL\Utils\IlluminaSampleSheet;

class IlluminaSampleSheetException extends \Exception {}
24 changes: 24 additions & 0 deletions src/IlluminaSampleSheet/SampleSheet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php declare(strict_types=1);

namespace MLL\Utils\IlluminaSampleSheet;

abstract class SampleSheet
{
/** @var array<Section> */
protected array $sections = [];

public function addSection(Section $section): void
{
$this->sections[] = $section;
}

public function toString(): string
{
$sectionStrings = array_map(
fn (Section $section): string => $section->convertSectionToString(),
$this->sections
);

return implode("\n", $sectionStrings);
}
}
8 changes: 8 additions & 0 deletions src/IlluminaSampleSheet/Section.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php declare(strict_types=1);

namespace MLL\Utils\IlluminaSampleSheet;

interface Section
{
public function convertSectionToString(): string;
}
28 changes: 28 additions & 0 deletions src/IlluminaSampleSheet/V2/BclConvert/BclConvertSection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php declare(strict_types=1);

namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert;

use MLL\Utils\IlluminaSampleSheet\Section;

class BclConvertSection implements Section
{
protected SettingsSection $settingsSection;

protected DataSection $dataSection;

public function __construct(SettingsSection $settingsSection, DataSection $dataSection)
{
$this->settingsSection = $settingsSection;
$this->dataSection = $dataSection;
}

public function convertSectionToString(): string
{
$bclConvertLines = [
$this->settingsSection->convertSectionToString(),
$this->dataSection->convertSectionToString(),
];

return implode("\n", $bclConvertLines);
}
}
51 changes: 51 additions & 0 deletions src/IlluminaSampleSheet/V2/BclConvert/BclSample.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php declare(strict_types=1);

namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert;

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 ?string $overrideCycles = null;

public ?string $adapterRead1 = null;

public ?string $adapterRead2 = null;

public ?string $barcodeMismatchesIndex1 = null;

public ?string $barcodeMismatchesIndex2 = null;

public function __construct(
int $lane,
string $sample_ID,
string $index
) {
$this->lane = $lane;
$this->sample_ID = $sample_ID;
$this->index = $index;
}

/** @return array<int|string> */
public function toArray(): array
{
return array_filter([
$this->lane,
$this->sample_ID,
$this->index,
$this->index2,
$this->overrideCycles,
$this->adapterRead1,
$this->adapterRead2,
$this->barcodeMismatchesIndex1,
$this->barcodeMismatchesIndex2,
]);
}
}
52 changes: 52 additions & 0 deletions src/IlluminaSampleSheet/V2/BclConvert/DataSection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php declare(strict_types=1);

namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert;

use MLL\Utils\IlluminaSampleSheet\Section;

class DataSection implements Section
{
/** @var array<BclSample> */
protected array $dataRows = [];

public function addSample(BclSample $bclSample): void
{
$this->dataRows[] = $bclSample;
}

public function convertSectionToString(): string
{
/** @var array<string> $samplePropertiesOfFirstSample */
$samplePropertiesOfFirstSample = array_keys(get_object_vars($this->dataRows[0]));
foreach ($this->dataRows as $sample) {
$actualProperties = array_keys(get_object_vars($sample));
if ($samplePropertiesOfFirstSample !== $actualProperties) {
throw new \Exception('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<string> $samplePropertiesOfFirstSample */
protected function generateDataHeaderByProperties(array $samplePropertiesOfFirstSample): string
{
$samplePropertiesOfFirstSample = array_filter($samplePropertiesOfFirstSample, fn (string $value) // @phpstan-ignore-next-line Variable property access on a non-object required here
=> $this->dataRows[0]->$value !== null);

$samplePropertiesOfFirstSample = array_map(fn (string $value) => ucfirst($value), $samplePropertiesOfFirstSample);

return implode(',', $samplePropertiesOfFirstSample);
}
}
42 changes: 42 additions & 0 deletions src/IlluminaSampleSheet/V2/BclConvert/SettingsSection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types=1);

namespace MLL\Utils\IlluminaSampleSheet\V2\BclConvert;

use MLL\Utils\IlluminaSampleSheet\Section;
use MLL\Utils\IlluminaSampleSheet\V2\Enums\FastQCompressionFormat;

class SettingsSection implements Section
{
public string $softwareVersion;

public FastQCompressionFormat $fastqCompressionFormat;

public ?bool $trimUMI = null;

public function __construct(
string $softwareVersion,
FastQCompressionFormat $fastqCompressionFormat
) {
$this->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";
}
}
26 changes: 26 additions & 0 deletions src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php declare(strict_types=1);

namespace MLL\Utils\IlluminaSampleSheet\V2\Enums;

class FastQCompressionFormat
{
public const GZIP = 'gzip';
public const DRAGEN = 'dragen';

public string $value;

public function __construct(string $value)
{
$this->value = $value;
}

public static function GZIP(): self
{
return new self(self::GZIP);
}

public static function DRAGEN(): self
{
return new self(self::DRAGEN);
}
}
56 changes: 56 additions & 0 deletions src/IlluminaSampleSheet/V2/HeaderSection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php declare(strict_types=1);

namespace MLL\Utils\IlluminaSampleSheet\V2;

use MLL\Utils\IlluminaSampleSheet\Section;

class HeaderSection implements Section
{
protected const FILE_FORMAT_VERSION = '2';

protected string $fileFormatVersion = self::FILE_FORMAT_VERSION;

public string $runName;

public ?string $runDescription = null;

public ?string $instrumentType = null;

public ?string $instrumentPlatform = null;

/** @var array<string, string> */
protected array $customParams = [];

public function __construct(string $runName)
{
$this->runName = $runName;
}

public function setCustomParam(string $paramName, string $paramValue): void
{
$this->customParams['Custom_' . $paramName] = $paramValue;
}

public function convertSectionToString(): string
{
$headerLines = [
'[Header]',
"FileFormatVersion,{$this->fileFormatVersion}",
"RunName,{$this->runName}",
];
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}";
}

return implode("\n", $headerLines) . "\n";
}
}
21 changes: 21 additions & 0 deletions src/IlluminaSampleSheet/V2/NovaSeqXSampleSheet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);

namespace MLL\Utils\IlluminaSampleSheet\V2;

use MLL\Utils\IlluminaSampleSheet\SampleSheet;
use MLL\Utils\IlluminaSampleSheet\V2\BclConvert\BclConvertSection;

class NovaSeqXSampleSheet extends SampleSheet
{
public function __construct(
HeaderSection $header,
ReadsSection $reads,
?BclConvertSection $bclConvertSection
) {
$this->addSection($header);
$this->addSection($reads);
if (! is_null($bclConvertSection)) {
$this->addSection($bclConvertSection);
}
}
}
Loading
Loading