Skip to content

Commit

Permalink
ListCommand, ExplainCommand: add support for translation language
Browse files Browse the repository at this point in the history
  • Loading branch information
mabar committed Mar 22, 2024
1 parent 35f6fa0 commit 0e691cf
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 26 deletions.
10 changes: 7 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,7 @@ bin/console scheduler:list
bin/console scheduler:list --next=3
bin/console scheduler:list --timezone=Europe/Prague
bin/console scheduler:list --explain
bin/console scheduler:list --explain=en
```

Options:
Expand All @@ -730,8 +731,10 @@ Options:
- `-v` - display absolute times
- `--timezone` (or `-tz`) - display times in specified timezone instead of one used by application
- e.g. `--tz=UTC`
- `--explain` - explain whole expression, including [seconds](#seconds) and [timezones](#timezones)
- `--explain[=<language>]` - explain whole expression, including [seconds](#seconds) and [timezones](#timezones)
- [Explain command](#explain-command) with `--id` parameter can be used to explain specific job
- e.g. `--explain`
- e.g. `--explain=en` (to choose language)

### Worker command

Expand All @@ -752,8 +755,8 @@ Explain cron expression syntax
bin/console scheduler:explain
bin/console scheduler:explain --id="job id"
bin/console scheduler:explain --expression="0 22 * 12 *"
bin/console scheduler:explain --expression="* 8 * * *" --seconds=10 --timezone="Europe/Prague"
bin/console scheduler:explain -e="* 8 * * *" -s=10 -tz="Europe/Prague"
bin/console scheduler:explain --expression="* 8 * * *" --seconds=10 --timezone="Europe/Prague" --language=en
bin/console scheduler:explain -e="* 8 * * *" -s=10 -tz="Europe/Prague" -l=en
```

Options:
Expand All @@ -763,6 +766,7 @@ Options:
- `--expression=<expression>` (or `-e`) - explain expression
- `--seconds=<seconds>` (or `-s`) - repeat every n seconds
- `--timezone=<timezone>` (or `-tz`) - the timezone time should be displayed in
- `--language=<language>` (or `-l`) - explain in specified language

## Lazy loading

Expand Down
25 changes: 23 additions & 2 deletions src/Command/BaseExplainCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,43 @@

use DateTimeZone;
use Orisai\Clock\SystemClock;
use Orisai\CronExpressionExplainer\CronExpressionExplainer;
use Orisai\CronExpressionExplainer\DefaultCronExpressionExplainer;
use Orisai\Scheduler\Job\JobSchedule;
use Psr\Clock\ClockInterface;
use Symfony\Component\Console\Command\Command;
use function array_key_last;

/**
* @internal
*/
abstract class BaseExplainCommand extends Command
{

protected CronExpressionExplainer $explainer;

protected ClockInterface $clock;

public function __construct(?ClockInterface $clock)
public function __construct(?CronExpressionExplainer $explainer, ?ClockInterface $clock)
{
parent::__construct();
$this->explainer = $explainer ?? new DefaultCronExpressionExplainer();
$this->clock = $clock ?? new SystemClock();
parent::__construct();
}

protected function getSupportedLanguages(): string
{
$string = '';
$languages = $this->explainer->getSupportedLanguages();
$last = array_key_last($languages);
foreach ($languages as $code => $name) {
$string .= "$code ($name)";
if ($code !== $last) {
$string .= ', ';
}
}

return $string;
}

protected function computeTimeZone(JobSchedule $jobSchedule, DateTimeZone $renderedTimeZone): ?DateTimeZone
Expand Down
42 changes: 33 additions & 9 deletions src/Command/ExplainCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

use DateTimeZone;
use Orisai\CronExpressionExplainer\CronExpressionExplainer;
use Orisai\CronExpressionExplainer\DefaultCronExpressionExplainer;
use Orisai\CronExpressionExplainer\Exception\InvalidExpression;
use Orisai\CronExpressionExplainer\Exception\UnsupportedExpression;
use Orisai\Scheduler\Scheduler;
use Psr\Clock\ClockInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use function array_key_exists;
use function assert;
use function in_array;
use function is_string;
Expand All @@ -22,17 +22,14 @@ final class ExplainCommand extends BaseExplainCommand

private Scheduler $scheduler;

private CronExpressionExplainer $explainer;

public function __construct(
Scheduler $scheduler,
?CronExpressionExplainer $explainer = null,
?ClockInterface $clock = null
)
{
parent::__construct($clock);
parent::__construct($explainer, $clock);
$this->scheduler = $scheduler;
$this->explainer = $explainer ?? new DefaultCronExpressionExplainer();
}

public static function getDefaultName(): string
Expand All @@ -53,6 +50,12 @@ protected function configure(): void
$this->addOption('expression', 'e', InputOption::VALUE_REQUIRED, 'Expression to explain');
$this->addOption('seconds', 's', InputOption::VALUE_REQUIRED, 'Repeat every n seconds');
$this->addOption('timezone', 'tz', InputOption::VALUE_REQUIRED, 'The timezone time should be displayed in');
$this->addOption(
'language',
'l',
InputOption::VALUE_REQUIRED,
"Translate expression in given language - {$this->getSupportedLanguages()}",
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
Expand All @@ -66,20 +69,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$expression = $options['expression'];
$seconds = $options['seconds'];
$timezone = $options['timezone'];
$language = $options['language'];

if ($id !== null) {
return $this->explainJobWithId($id, $output);
}

if ($expression !== null) {
return $this->explainExpression($expression, $seconds, $timezone, $output);
return $this->explainExpression($expression, $seconds, $timezone, $language, $output);
}

return $this->explainSyntax($output);
}

/**
* @return array{id: string|null, expression: string|null, seconds: int<0, 59>|null, timezone: DateTimeZone|null}|null
* @return array{id: string|null, expression: string|null, seconds: int<0, 59>|null, timezone: DateTimeZone|null, language: string|null}|null
*/
private function validateOptions(InputInterface $input, OutputInterface $output): ?array
{
Expand Down Expand Up @@ -141,6 +145,23 @@ private function validateOptions(InputInterface $input, OutputInterface $output)
}
}

$language = $input->getOption('language');
assert($language === null || is_string($language));
if ($language !== null) {
if (!array_key_exists($language, $this->explainer->getSupportedLanguages())) {
$hasErrors = true;
$output->writeln(
"<error>Option --language expects no value or one of supported languages, '$language' given."
. ' Use --help to list available languages.</error>',
);
}

if ($expression === null) {
$hasErrors = true;
$output->writeln('<error>Option --language must be used with --expression.</error>');
}
}

if ($hasErrors) {
return null;
}
Expand All @@ -154,6 +175,7 @@ private function validateOptions(InputInterface $input, OutputInterface $output)
'expression' => $expression,
'seconds' => $seconds,
'timezone' => $timezone,
'language' => $language,
];
}

Expand All @@ -164,6 +186,7 @@ private function explainExpression(
string $expression,
?int $seconds,
?DateTimeZone $timeZone,
?string $language,
OutputInterface $output
): int
{
Expand All @@ -172,8 +195,9 @@ private function explainExpression(
$expression,
$seconds,
$timeZone,
$language,
));
} catch (InvalidExpression $exception) {
} catch (UnsupportedExpression $exception) {
$output->writeln("<error>{$exception->getMessage()}</error>");

return self::FAILURE;
Expand Down
37 changes: 26 additions & 11 deletions src/Command/ListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use DateTimeInterface;
use DateTimeZone;
use Orisai\CronExpressionExplainer\CronExpressionExplainer;
use Orisai\CronExpressionExplainer\DefaultCronExpressionExplainer;
use Orisai\Scheduler\Job\JobSchedule;
use Orisai\Scheduler\Scheduler;
use Psr\Clock\ClockInterface;
Expand All @@ -16,6 +15,7 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Terminal;
use function abs;
use function array_key_exists;
use function assert;
use function floor;
use function in_array;
Expand All @@ -39,17 +39,14 @@ final class ListCommand extends BaseExplainCommand

private Scheduler $scheduler;

private CronExpressionExplainer $explainer;

public function __construct(
Scheduler $scheduler,
?ClockInterface $clock = null,
?CronExpressionExplainer $explainer = null
)
{
parent::__construct($clock);
parent::__construct($explainer, $clock);
$this->scheduler = $scheduler;
$this->explainer = $explainer ?? new DefaultCronExpressionExplainer();
}

public static function getDefaultName(): string
Expand All @@ -68,7 +65,12 @@ protected function configure(): void
parent::configure();
$this->addOption('next', null, InputOption::VALUE_OPTIONAL, 'Sort jobs by their next execution time', false);
$this->addOption('timezone', 'tz', InputOption::VALUE_REQUIRED, 'The timezone times should be displayed in');
$this->addOption('explain', null, InputOption::VALUE_NONE, 'Explain expression');
$this->addOption(
'explain',
null,
InputOption::VALUE_OPTIONAL,
"Explain expression - {$this->getSupportedLanguages()}",
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
Expand Down Expand Up @@ -135,20 +137,23 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$nextDueDate,
));

if ($explain) {
$output->writeln($this->explainer->explain(
if ($explain !== null) {
$explainedExpression = $this->explainer->explain(
$jobSchedule->getExpression()->getExpression(),
$jobSchedule->getRepeatAfterSeconds(),
$computedTimeZone,
));
$explain === true ? null : $explain,
);

$output->writeln($explainedExpression);
}
}

return self::SUCCESS;
}

/**
* @return array{next: int<1, max>|bool, timezone: DateTimeZone, explain: bool}|null
* @return array{next: int<1, max>|bool, timezone: DateTimeZone, explain: true|string|null}|null
*/
private function validateOptions(InputInterface $input, OutputInterface $output): ?array
{
Expand Down Expand Up @@ -185,7 +190,17 @@ private function validateOptions(InputInterface $input, OutputInterface $output)
}

$explain = $input->getOption('explain');
assert(is_bool($explain));
assert($explain === null || $explain === true || is_string($explain));
if (
is_string($explain)
&& !array_key_exists($explain, $this->explainer->getSupportedLanguages())
) {
$hasErrors = true;
$output->writeln(
"<error>Option --explain expects no value or one of supported languages, '$explain' given."
. ' Use --help to list available languages.</error>',
);
}

if ($hasErrors) {
return null;
Expand Down
36 changes: 36 additions & 0 deletions tests/Unit/Command/ExplainCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,17 @@ public function provideExplainExpression(): Generator
<<<'MSG'
At every 59 seconds in UTC time zone.

MSG,
];

yield [
[
'--expression' => '* * * * *',
'--language' => 'en',
],
<<<'MSG'
At every minute.

MSG,
];

Expand All @@ -237,6 +248,7 @@ public function provideExplainExpression(): Generator
'-e' => '* * * * *',
'-s' => '59',
'-tz' => 'UTC',
'-l' => 'en',
],
<<<'MSG'
At every 59 seconds in UTC time zone.
Expand Down Expand Up @@ -348,6 +360,27 @@ public function provideInputError(): Generator
<<<'MSG'
Option --timezone must be used with --expression.

MSG,
];

yield [
[
'--expression' => '* * * *',
'--language' => 'noop',
],
<<<'MSG'
Option --language expects no value or one of supported languages, 'noop' given. Use --help to list available languages.

MSG,
];

yield [
[
'--language' => 'en',
],
<<<'MSG'
Option --language must be used with --expression.

MSG,
];

Expand All @@ -356,6 +389,7 @@ public function provideInputError(): Generator
'--id' => 'id',
'--seconds' => 'bad seconds',
'--timezone' => 'bad timezone',
'--language' => 'noop',
],
<<<'MSG'
Option --seconds expects an int<0, 59>, 'bad seconds' given.
Expand All @@ -364,6 +398,8 @@ public function provideInputError(): Generator
Option --timezone expects a valid timezone, 'bad timezone' given.
Option --timezone cannot be used with --id.
Option --timezone must be used with --expression.
Option --language expects no value or one of supported languages, 'noop' given. Use --help to list available languages.
Option --language must be used with --expression.

MSG,
];
Expand Down
Loading

0 comments on commit 0e691cf

Please sign in to comment.