Passed
Push — master ( c0f32c...e879f4 )
by Chema
04:17 queued 41s
created

NotifyCommand::printNotifyResult()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 17
c 1
b 0
f 0
dl 0
loc 28
ccs 18
cts 18
cp 1
rs 9.0777
cc 6
nc 6
nop 1
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chemaclass\StockTicker\Infrastructure\Command;
6
7
use Chemaclass\StockTicker\Domain\Notifier\NotifierPolicy;
8
use Chemaclass\StockTicker\Domain\Notifier\NotifyResult;
9
use Chemaclass\StockTicker\Domain\Notifier\Policy\Condition\RecentNewsWasFound;
10
use Chemaclass\StockTicker\Domain\Notifier\Policy\PolicyGroup;
11
use Chemaclass\StockTicker\Infrastructure\Command\ReadModel\NotifyCommandInput;
0 ignored issues
show
Bug introduced by
The type Chemaclass\StockTicker\I...odel\NotifyCommandInput was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Chemaclass\StockTicker\StockTickerConfig;
13
use Chemaclass\StockTicker\StockTickerFacade;
14
use Chemaclass\StockTicker\StockTickerFactory;
15
use Symfony\Component\Console\Command\Command;
16
use Symfony\Component\Console\Input\InputArgument;
17
use Symfony\Component\Console\Input\InputInterface;
18
use Symfony\Component\Console\Output\OutputInterface;
19
20
final class NotifyCommand extends Command
21
{
22
    private const DEFAULT_MAX_NEWS_TO_FETCH = 3;
23
    private const DEFAULT_MAX_REPETITIONS = PHP_INT_MAX;
24
    private const DEFAULT_SLEEPING_TIME_IN_SECONDS = 60;
25
    private const DEFAULT_CHANNEL = 'email';
26
27
    /**
28
     * @psalm-suppress PropertyNotSetInConstructor
29
     */
30
    private OutputInterface $output;
31
32 2
    protected function configure(): void
33
    {
34
        $this
35 2
            ->setName('notify')
36 2
            ->setDescription('It notifies based on conditions applied to the crawled result')
37 2
            ->addArgument(
38 2
                'symbols',
39 2
                InputArgument::IS_ARRAY|InputArgument::REQUIRED,
40 2
                'Which stock symbols do you want to crawl?'
41
            )
42 2
            ->addOption(
43 2
                'maxNews',
44 2
                'm',
45 2
                InputArgument::OPTIONAL,
46 2
                'Max number of news to fetch',
47 2
                self::DEFAULT_MAX_NEWS_TO_FETCH
48
            )
49 2
            ->addOption(
50 2
                'maxRepetitions',
51 2
                'r',
52 2
                InputArgument::OPTIONAL,
53 2
                'Max number repetitions for the loop. 0 for non-end.',
54 2
                self::DEFAULT_MAX_REPETITIONS
55
            )
56 2
            ->addOption(
57 2
                'channels',
58 2
                'c',
59 2
                InputArgument::OPTIONAL,
60 2
                'Channels to notify separated by comma [email,slack]',
61 2
                self::DEFAULT_CHANNEL
62
            )
63 2
            ->addOption(
64 2
                'sleepingTime',
65 2
                's',
66 2
                InputArgument::OPTIONAL,
67 2
                'Sleeping time in seconds',
68 2
                self::DEFAULT_SLEEPING_TIME_IN_SECONDS
69
            );
70 2
    }
71
72 2
    protected function execute(InputInterface $input, OutputInterface $output): int
73
    {
74 2
        $this->output = $output;
75
76 2
        $commandInput = NotifyCommandInput::createFromInput($input);
77
78 2
        $policy = $this->createPolicyForSymbols($commandInput->getSymbols());
79 2
        $facade = $this->createStockTickerFacade();
80
81 2
        for ($i = 0; $i < $commandInput->getMaxRepetitions(); $i++) {
82 2
            $this->printStartingIteration($commandInput->getSymbols(), $i, $commandInput->getMaxRepetitions());
83
84 2
            $notifyResult = $facade->sendNotifications($commandInput->getChannelNames(), $policy, $commandInput->getMaxNews());
85 2
            $this->printNotifyResult($notifyResult);
86 2
            $this->sleepWithPrompt($commandInput->getSleepingTime());
87
        }
88
89 2
        $output->writeln('Already reached the max repetition limit of ' . $commandInput->getMaxRepetitions());
90
91 2
        return Command::SUCCESS;
92
    }
93
94
    /**
95
     * Create the same policy group to all symbols.
96
     *
97
     * @param string[] $symbols
98
     */
99 2
    private function createPolicyForSymbols(array $symbols): NotifierPolicy
100
    {
101 2
        $conditions = array_fill_keys($symbols, new PolicyGroup([
102 2
            'More news was found' => new RecentNewsWasFound(),
103
        ]));
104
105 2
        return new NotifierPolicy($conditions);
106
    }
107
108 2
    private function createStockTickerFacade(): StockTickerFacade
109
    {
110 2
        return new StockTickerFacade(
111 2
            new StockTickerFactory(
112 2
                new StockTickerConfig(
113 2
                    dirname(__DIR__) . '/../Presentation/notification',
114 2
                    $_ENV
115
                )
116
            ),
117
        );
118
    }
119
120 2
    private function printStartingIteration(array $symbols, int $actualIteration, int $maxRepetitions): void
121
    {
122 2
        $this->output->writeln(sprintf('Looking for news in %s ...', implode(', ', $symbols)));
123 2
        $this->output->writeln(sprintf('Completed %d of %d', $actualIteration, $maxRepetitions));
124 2
    }
125
126 2
    private function printNotifyResult(NotifyResult $notifyResult): void
127
    {
128 2
        if ($notifyResult->isEmpty()) {
129 1
            $this->output->writeln(' ~~~ Nothing new here...');
130
131 1
            return;
132
        }
133
134 1
        $this->output->writeln('===========================');
135 1
        $this->output->writeln('====== Notify result ======');
136 1
        $this->output->writeln('===========================');
137
138 1
        foreach ($notifyResult->conditionNamesGroupBySymbol() as $symbol => $conditionNames) {
139 1
            $quote = $notifyResult->quoteBySymbol($symbol);
140
141 1
            $companyName = (null !== $quote->getCompanyName())
142 1
                ? $quote->getCompanyName()->getLongName() ?? ''
143 1
                : '';
144
145 1
            $symbol = $quote->getSymbol() ?: '';
146 1
            $this->output->writeln(sprintf('%s (%s)', $companyName, $symbol));
147
148 1
            foreach ($conditionNames as $conditionName) {
149 1
                $this->output->writeln("  - $conditionName");
150
            }
151 1
            $this->output->writeln('');
152
        }
153 1
        $this->output->writeln('');
154 1
    }
155
156 2
    private function sleepWithPrompt(int $sec): void
157
    {
158 2
        $this->output->writeln("Sleeping {$sec} seconds...");
159 2
        $len = mb_strlen((string) $sec);
160
161 2
        for ($i = $sec; $i > 0; $i--) {
162
            $this->output->write(sprintf("%0{$len}d\r", $i));
163
            sleep(1);
164
        }
165
166 2
        $this->output->writeln('Awake again!');
167 2
    }
168
}
169