Skip to content

Commit

Permalink
Illumina sample sheet v2 (#19)
Browse files Browse the repository at this point in the history
* feature: Add Support Illumina NovaSeq Sample Sheets
  • Loading branch information
simbig authored May 8, 2024
1 parent e65a83a commit 1d67359
Show file tree
Hide file tree
Showing 25 changed files with 518 additions and 2 deletions.
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

0 comments on commit 1d67359

Please sign in to comment.