Skip to content

Commit

Permalink
Add age validation to the schema (#124)
Browse files Browse the repository at this point in the history
Check and validate ages from dates in a CSV cell. This addition provides
the ability to set age restrictions based on birthdays, offering
controls such as minimum, maximum, and specific values for age.
  • Loading branch information
SmetDenis committed Apr 2, 2024
1 parent 43f635e commit ee5b54f
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 7 deletions.
17 changes: 14 additions & 3 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-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-379-green?label=Total%20number%20of%20rules&labelColor=darkgreen&color=gray)](schema-examples/full.yml)
[![Static Badge](https://img.shields.io/badge/Rules-165-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-31/54/9-green?label=Plan%20to%20add&labelColor=gray&color=gray)](tests/schemas/todo.yml)
[![Static Badge](https://img.shields.io/badge/Rules-29/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 @@ -419,6 +419,17 @@ columns:
date_interval_less: PT23H59M59S # 23 hours, 59 minutes, and 59 seconds
date_interval_max: P1Y # 1 year

# Check an arbitrary date in a CSV cell for age (years).
# Actually it calculates the difference between the date and the current date.
# Convenient to use for age restrictions based on birthday.
# See the description of `date_*` functions for details on date formats.
date_age_min: 1 # x >= 1
date_age_greater: 14 # x > 14
date_age_not: 18 # x != 18
date_age: 21 # x == 21
date_age_less: 99 # x < 99
date_age_max: 100 # x <= 100

# 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.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@
"date_interval_less" : "PT23H59M59S",
"date_interval_max" : "P1Y",

"date_age_min" : 1,
"date_age_greater" : 14,
"date_age_not" : 18,
"date_age" : 21,
"date_age_less" : 99,
"date_age_max" : 100,

"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 @@ -117,6 +117,13 @@
'date_interval_less' => 'PT23H59M59S',
'date_interval_max' => 'P1Y',

'date_age_min' => 1,
'date_age_greater' => 14,
'date_age_not' => 18,
'date_age' => 21,
'date_age_less' => 99,
'date_age_max' => 100,

'is_bool' => true,
'is_uuid' => true,
'is_slug' => true,
Expand Down
11 changes: 11 additions & 0 deletions schema-examples/full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,17 @@ columns:
date_interval_less: PT23H59M59S # 23 hours, 59 minutes, and 59 seconds
date_interval_max: P1Y # 1 year

# Check an arbitrary date in a CSV cell for age (years).
# Actually it calculates the difference between the date and the current date.
# Convenient to use for age restrictions based on birthday.
# See the description of `date_*` functions for details on date formats.
date_age_min: 1 # x >= 1
date_age_greater: 14 # x > 14
date_age_not: 18 # x != 18
date_age: 21 # x == 21
date_age_less: 99 # x < 99
date_age_max: 100 # x <= 100

# 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 @@ -124,6 +124,13 @@ columns:
date_interval_less: PT23H59M59S
date_interval_max: P1Y

date_age_min: 1
date_age_greater: 14
date_age_not: 18
date_age: 21
date_age_less: 99
date_age_max: 100

is_bool: true
is_uuid: true
is_slug: true
Expand Down
85 changes: 85 additions & 0 deletions src/Rules/Cell/ComboDateAge.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?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 ComboDateAge extends AbstractCellRuleCombo
{
protected const NAME = 'age';

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

public function getHelpMeta(): array
{
return [
[
'Check an arbitrary date in a CSV cell for age (years).',
'Actually it calculates the difference between the date and the current date.',
'Convenient to use for age restrictions based on birthday.',
'See the description of `date_*` functions for details on date formats.',
],
[
self::MIN => [1, 'x >= 1'],
self::GREATER => [14, 'x > 14'],
self::NOT => [18, 'x != 18'],
self::EQ => [21, 'x == 21'],
self::LESS => [99, 'x < 99'],
self::MAX => [100, 'x <= 100'],
],
];
}

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

return $years;
}

protected function getExpected(): float
{
return $this->getOptionAsInt();
}

protected function getExpectedStr(): string
{
return "{$this->getOptionAsInt()} years";
}

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

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

private static function calculateAge(string $dateString): int
{
$birthDateTime = new \DateTimeImmutable($dateString);
$currentDateTime = new \DateTimeImmutable('now');

return $birthDateTime->diff($currentDateTime)->y; // Returns the total number of full years
}
}
74 changes: 74 additions & 0 deletions tests/Rules/Cell/ComboDateAgeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?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\ComboDateAge;
use JBZoo\PHPUnit\Rules\TestAbstractCellRuleCombo;

use function JBZoo\PHPUnit\isSame;

class ComboDateAgeTest extends TestAbstractCellRuleCombo
{
protected string $ruleClass = ComboDateAge::class;

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

isSame('', $rule->test(''));
isSame('', $rule->test('now'));

isSame(
'The age of the value "2020-10-02" is parsed as "3" years, ' .
'which is not equal than the expected "0 years"',
$rule->test('2020-10-02'),
);

isSame(
'The age of the value "<c>qwerty</c>" is ' .
'<red>Failed to parse time string (qwerty) at position 0 (q): ' .
'The timezone could not be found in the database</red>, ' .
'which is not equal than the expected "<green>0 years</green>"',
$rule->test('qwerty', true),
);
}

public function testMin(): void
{
$rule = $this->create(21, Combo::MIN);

isSame('', $rule->test(''));
isSame('', $rule->test('+22 years'));
isSame('', $rule->test('2100-01-01'));
isSame('', $rule->test('2100-01'));

isSame(
'The age of the value "2020-10-02" is parsed as "3" years, ' .
'which is less than the expected "21 years"',
$rule->test('2020-10-02'),
);

isSame(
'The age of the value "<c>qwerty</c>" is ' .
'<red>Failed to parse time string (qwerty) at position 0 (q): ' .
'The timezone could not be found in the database</red>, ' .
'which is less than the expected "<green>21 years</green>"',
$rule->test('qwerty', true),
);
}
}
4 changes: 0 additions & 4 deletions tests/schemas/todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ columns:
is_bool_value: true # https://github.com/Respect/Validation/blob/main/docs/rules/BoolVal.md
is_null: true # see empty_values

# Dates
age: 35
dateperiod: 1

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

Expand Down

0 comments on commit ee5b54f

Please sign in to comment.