Skip to content

Commit

Permalink
Merge 865d30a into ca07291
Browse files Browse the repository at this point in the history
  • Loading branch information
SmetDenis committed Apr 2, 2024
2 parents ca07291 + 865d30a commit e958ad9
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 10 deletions.
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
<!-- /top-badges -->

<!-- rules-counter -->
[![Static Badge](https://img.shields.io/badge/Rules-367-green?label=Total%20number%20of%20rules&labelColor=darkgreen&color=gray)](schema-examples/full.yml)
[![Static Badge](https://img.shields.io/badge/Rules-153-green?label=Cell%20rules&labelColor=blue&color=gray)](src/Rules/Cell)
[![Static Badge](https://img.shields.io/badge/Rules-373-green?label=Total%20number%20of%20rules&labelColor=darkgreen&color=gray)](schema-examples/full.yml)
[![Static Badge](https://img.shields.io/badge/Rules-159-green?label=Cell%20rules&labelColor=blue&color=gray)](src/Rules/Cell)
[![Static Badge](https://img.shields.io/badge/Rules-206-green?label=Aggregate%20rules&labelColor=blue&color=gray)](src/Rules/Aggregate)
[![Static Badge](https://img.shields.io/badge/Rules-8-green?label=Extra%20checks&labelColor=blue&color=gray)](#extra-checks)
[![Static Badge](https://img.shields.io/badge/Rules-32/54/9-green?label=Plan%20to%20add&labelColor=gray&color=gray)](tests/schemas/todo.yml)
[![Static Badge](https://img.shields.io/badge/Rules-31/54/9-green?label=Plan%20to%20add&labelColor=gray&color=gray)](tests/schemas/todo.yml)
<!-- /rules-counter -->

A console utility designed for validating CSV files against a strictly defined schema and validation rules outlined
Expand Down Expand Up @@ -409,6 +409,16 @@ columns:
is_time: true # Check if the cell value is a valid time in the format "HH:MM:SS AM/PM" / "HH:MM:SS" / "HH:MM". Case-insensitive.
is_leap_year: true # Check if the cell value is a leap year. Example: "2008", "2008-02-29 23:59:59 UTC".

# Date Intervals. Under the hood, the strings are converted to seconds and compared.
# See: https://www.php.net/manual/en/class.dateinterval.php
# See: https://www.php.net/manual/en/dateinterval.createfromdatestring.php
date_interval_min: PT0S # 0 seconds
date_interval_greater: 1day 1sec # 1 day and 1 second
date_interval_not: 100 days # Except for the 100 days
date_interval: P2W # Exactly 2 weeks
date_interval_less: PT23H59M59S # 23 hours, 59 minutes, and 59 seconds
date_interval_max: P1Y # 1 year

# Specific formats
is_bool: true # Allow only boolean values "true" and "false", case-insensitive.
is_uuid: true # Validates whether the input is a valid UUID. It also supports validation of specific versions 1, 3, 4 and 5.
Expand Down Expand Up @@ -830,8 +840,8 @@ Behind the scenes to what is outlined in the yml above, there are additional che
* Check that each row matches the number of columns.
* With `strict_column_order` rule, you can check that the columns are in the correct order.
* With `allow_extra_columns` rule, you can check that there are no extra columns in the CSV file.
* If `csv.header: true`. Schema contains an unknown column `name` that is not found in the CSV file.
* If `csv.header: false`. Compare the number of columns in the schema and the CSV file.
* If `csv.header: true`. Schema contains an unknown column `name` that is not found in the CSV file.
* If `csv.header: false`. Compare the number of columns in the schema and the CSV file.

<!-- /extra-rules -->

Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name" : "jbzoo/csv-blueprint",
"type" : "project",
"description" : "CLI Utility for Validating and Generating CSV Files Based on Custom Rules. It ensures your data meets specified criteria, streamlining data management and integrity checks.",
"type" : "project",
"description" : "CLI Utility for Validating and Generating CSV files based on custom rules. It ensures your data meets specified criteria, streamlining data management and integrity checks.",
"license" : "MIT",
"keywords" : [
"jbzoo",
Expand Down
1 change: 1 addition & 0 deletions csv-blueprint.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

// Convert all errors to exceptions. Looks like we have critical case, and we need to stop or handle it.
// We have to do it becase tool uses 3rd-party libraries, and we can't trust them.
// So, we need to catch all errors and handle them.
\set_error_handler(static function ($severity, $message, $file, $line): void {
throw new \ErrorException($message, 0, $severity, $file, $line);
});
Expand Down
11 changes: 9 additions & 2 deletions schema-examples/full.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name" : "CSV Blueprint Schema Example",
"description" : "This YAML file provides a detailed description and validation rules for CSV files\nto be processed by CSV Blueprint tool. It includes specifications for file name patterns,\nCSV formatting options, and extensive validation criteria for individual columns and their values,\nsupporting a wide range of data validation rules from basic type checks to complex regex validations.\nThis example serves as a comprehensive guide for creating robust CSV file validations.\n",
"description" : "This YAML file provides a detailed description and validation rules for CSV files\nto be processed by CSV Blueprint tool. It includes specifications for file name patterns,\nCSV formatting options, and extensive validation criteria for individual columns and their values,\nsupporting a wide range of data validation rules from basic type checks to complex regex validations.\nThis example serves as a comprehensive guide for creating robust CSV file validations.\n",

"filename_pattern" : "\/demo(-\\d+)?\\.csv$\/i",

Expand All @@ -23,7 +23,7 @@
"name" : "Column Name (header)",
"description" : "Lorem ipsum",
"example" : "Some example",
"required" : true,
"required" : true,

"rules" : {
"not_empty" : true,
Expand Down Expand Up @@ -89,6 +89,13 @@
"is_time" : true,
"is_leap_year" : true,

"date_interval_min" : "PT0S",
"date_interval_greater" : "1day 1sec",
"date_interval_not" : "100 days",
"date_interval" : "P2W",
"date_interval_less" : "PT23H59M59S",
"date_interval_max" : "P1Y",

"is_bool" : true,
"is_uuid" : true,
"is_slug" : true,
Expand Down
7 changes: 7 additions & 0 deletions schema-examples/full.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@
'is_time' => true,
'is_leap_year' => true,

'date_interval_min' => 'PT0S',
'date_interval_greater' => '1day 1sec',
'date_interval_not' => '100 days',
'date_interval' => 'P2W',
'date_interval_less' => 'PT23H59M59S',
'date_interval_max' => 'P1Y',

'is_bool' => true,
'is_uuid' => true,
'is_slug' => true,
Expand Down
10 changes: 10 additions & 0 deletions schema-examples/full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,16 @@ columns:
is_time: true # Check if the cell value is a valid time in the format "HH:MM:SS AM/PM" / "HH:MM:SS" / "HH:MM". Case-insensitive.
is_leap_year: true # Check if the cell value is a leap year. Example: "2008", "2008-02-29 23:59:59 UTC".

# Date Intervals. Under the hood, the strings are converted to seconds and compared.
# See: https://www.php.net/manual/en/class.dateinterval.php
# See: https://www.php.net/manual/en/dateinterval.createfromdatestring.php
date_interval_min: PT0S # 0 seconds
date_interval_greater: 1day 1sec # 1 day and 1 second
date_interval_not: 100 days # Except for the 100 days
date_interval: P2W # Exactly 2 weeks
date_interval_less: PT23H59M59S # 23 hours, 59 minutes, and 59 seconds
date_interval_max: P1Y # 1 year

# Specific formats
is_bool: true # Allow only boolean values "true" and "false", case-insensitive.
is_uuid: true # Validates whether the input is a valid UUID. It also supports validation of specific versions 1, 3, 4 and 5.
Expand Down
7 changes: 7 additions & 0 deletions schema-examples/full_clean.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ columns:
is_time: true
is_leap_year: true

date_interval_min: PT0S
date_interval_greater: '1day 1sec'
date_interval_not: '100 days'
date_interval: P2W
date_interval_less: PT23H59M59S
date_interval_max: P1Y

is_bool: true
is_uuid: true
is_slug: true
Expand Down
124 changes: 124 additions & 0 deletions src/Rules/Cell/ComboDateInterval.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

/**
* JBZoo Toolbox - Csv-Blueprint.
*
* This file is part of the JBZoo Toolbox project.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT
* @copyright Copyright (C) JBZoo.com, All rights reserved.
* @see https://github.com/JBZoo/Csv-Blueprint
*/

declare(strict_types=1);

namespace JBZoo\CsvBlueprint\Rules\Cell;

final class ComboDateInterval extends AbstractCellRuleCombo
{
protected const NAME = 'date interval';

protected const INVALID_DATEINTERVAL_ACTUAL = -1;
protected const INVALID_DATEINTERVAL_EXPECTED = -2;

public function getHelpMeta(): array
{
return [
[
'Date Intervals. Under the hood, the strings are converted to seconds and compared.',
'See: https://www.php.net/manual/en/class.dateinterval.php',
'See: https://www.php.net/manual/en/dateinterval.createfromdatestring.php',
],
[
self::MIN => ['PT0S', '0 seconds'],
self::GREATER => ['1day 1sec', '1 day and 1 second'],
self::EQ => ['P2W', 'Exactly 2 weeks'],
self::NOT => ['100 days', 'Except for the 100 days'],
self::LESS => ['PT23H59M59S', '23 hours, 59 minutes, and 59 seconds'],
self::MAX => ['P1Y', '1 year'],
],
];
}

protected function getActualCell(string $cellValue): float
{
try {
$seconds = self::dateIntervalToSeconds($cellValue);
} catch (\Exception) {
return self::INVALID_DATEINTERVAL_ACTUAL;
}

return $seconds;
}

protected function getExpected(): float
{
$expectedValue = $this->getOptionAsString();

try {
$seconds = self::dateIntervalToSeconds($expectedValue);
} catch (\Exception) {
return self::INVALID_DATEINTERVAL_EXPECTED;
}

return $seconds;
}

protected function getExpectedStr(): string
{
$expectedValue = $this->getOptionAsString();

try {
$seconds = self::dateIntervalToSeconds($expectedValue);
} catch (\Exception $exception) {
return "<red>{$exception->getMessage()}</red>";
}

return "{$seconds} ({$expectedValue}) seconds";
}

protected function getCurrentStr(string $cellValue): string
{
try {
$seconds = self::dateIntervalToSeconds($cellValue);
} catch (\Exception $exception) {
return "<red>{$exception->getMessage()}</red>";
}

return "parsed as \"{$seconds}\" seconds";
}

private static function dateIntervalToSeconds(string $dateIntervalOrAsString): int
{
try {
$interval = new \DateInterval($dateIntervalOrAsString);
} catch (\Exception) {
try {
$interval = \DateInterval::createFromDateString($dateIntervalOrAsString);
} catch (\Exception) {
throw new \RuntimeException("Can't parse date interval: {$dateIntervalOrAsString}");
}
}

if (!$interval instanceof \DateInterval) {
throw new \RuntimeException("Can't parse date interval: {$dateIntervalOrAsString}");
}

$daysPerYear = 365.25; // Average considering leap years
$daysPerMonth = 30; // Average. "365.25 / 12 ~ 30.4166666667"
$hoursPerDay = 24;
$minutesPerHour = 60;
$secondsPerMinute = 60;

$yearsToDays = $interval->y * $daysPerYear;
$monthsToDays = $interval->m * $daysPerMonth;
$days = $interval->d + $yearsToDays + $monthsToDays;
$hours = $interval->h + ($days * $hoursPerDay);
$minutes = $interval->i + ($hours * $minutesPerHour);
$seconds = $interval->s + ($minutes * $secondsPerMinute);

return (int)$seconds;
}
}
58 changes: 58 additions & 0 deletions tests/Rules/Cell/ComboDateIntervalTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

/**
* JBZoo Toolbox - Csv-Blueprint.
*
* This file is part of the JBZoo Toolbox project.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT
* @copyright Copyright (C) JBZoo.com, All rights reserved.
* @see https://github.com/JBZoo/Csv-Blueprint
*/

declare(strict_types=1);

namespace JBZoo\PHPUnit\Rules\Cell;

use JBZoo\CsvBlueprint\Rules\AbstarctRule as Combo;
use JBZoo\CsvBlueprint\Rules\Cell\ComboDateInterval;
use JBZoo\PHPUnit\Rules\TestAbstractCellRuleCombo;

use function JBZoo\PHPUnit\isSame;

class ComboDateIntervalTest extends TestAbstractCellRuleCombo
{
protected string $ruleClass = ComboDateInterval::class;

public function testEqual(): void
{
$rule = $this->create('1 day', Combo::EQ);

isSame('', $rule->test(''));
isSame('', $rule->test('1 day'));

$rule = $this->create('1 day', Combo::EQ);
isSame('', $rule->test('1 day'));

foreach ($rule->getHelpMeta()[1] as $examples) {
$rule = $this->create($examples[0], Combo::EQ);
isSame('', $rule->test($examples[0]), $examples[0]);
}

isSame(
'The date interval of the value "<c>qwerty</c>" is ' .
'<red>Can\'t parse date interval: qwerty</red>, ' .
'which is not equal than the expected "<green>31557600 (P1Y) seconds</green>"',
$rule->test('qwerty', true),
);

$rule = $this->create('P2W', Combo::EQ);
isSame(
'The date interval of the value "1 day" is parsed as "86400" seconds, ' .
'which is not equal than the expected "1209600 (P2W) seconds"',
$rule->test('1 day'),
);
}
}
1 change: 0 additions & 1 deletion tests/schemas/todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ columns:
# Dates
age: 35
dateperiod: 1
dateinterval: 1

# Codes
subdivision_code: [ ] # https://github.com/Respect/Validation/blob/main/docs/rules/SubdivisionCode.md
Expand Down

0 comments on commit e958ad9

Please sign in to comment.