Issues (65)

src/Display/Table.php (4 issues)

Severity
1
<?php
2
/**
3
 * This file is part of graze/parallel-process.
4
 *
5
 * Copyright © 2018 Nature Delivered Ltd. <https://www.graze.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license https://github.com/graze/parallel-process/blob/master/LICENSE.md
11
 * @link    https://github.com/graze/parallel-process
12
 */
13
14
namespace Graze\ParallelProcess\Display;
15
16
use Exception;
17
use Graze\DiffRenderer\DiffConsoleOutput;
18
use Graze\DiffRenderer\Terminal\TerminalInterface;
19
use Graze\ParallelProcess\Event\PoolRunEvent;
20
use Graze\ParallelProcess\Event\RunEvent;
21
use Graze\ParallelProcess\OutputterInterface;
22
use Graze\ParallelProcess\PoolInterface;
23
use Graze\ParallelProcess\PriorityPool;
24
use Graze\ParallelProcess\ProcessRun;
25
use Graze\ParallelProcess\RunInterface;
26
use Symfony\Component\Console\Output\OutputInterface;
27
28
class Table
29
{
30
    use TagsTrait;
31
32
    const SPINNER = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏";
33
34
    /** @var PoolInterface */
35
    private $pool;
36
    /** @var string[] */
37
    private $rows = [];
38
    /** @var Exception[] */
39
    private $exceptions;
40
    /** @var DiffConsoleOutput */
41
    private $output;
42
    /** @var TerminalInterface */
43
    private $terminal;
44
    /** @var bool */
45
    private $showOutput = true;
46
    /** @var bool */
47
    private $showSummary = true;
48
49
    /**
50
     * Table constructor.
51
     *
52
     * @param OutputInterface    $output
53
     * @param PoolInterface|null $pool
54
     */
55 16
    public function __construct(OutputInterface $output, PoolInterface $pool = null)
56
    {
57 16
        $this->pool = $pool ?: new PriorityPool();
58 16
        if (!$output instanceof DiffConsoleOutput) {
59 3
            $this->output = new DiffConsoleOutput($output);
60 3
            $this->output->setTrim(true);
61 3
        } else {
62 16
            $this->output = $output;
63
        }
64 16
        $this->terminal = $this->output->getTerminal();
65 16
        $this->exceptions = [];
66
67 16
        $this->pool->addListener(
68 16
            PoolRunEvent::POOL_RUN_ADDED,
69
            function (PoolRunEvent $event) {
70 13
                $this->add($event->getRun());
71 13
            }
72 16
        );
73
74 16
        array_map([$this, 'add'], $this->pool->getAll());
75 16
    }
76
77
    /**
78
     * @param RunInterface $run
79
     * @param string       $status
80
     *
81
     * @return string
82
     */
83 13
    private function formatRow(RunInterface $run, $status)
84
    {
85 13
        $tags = $this->formatTags($run->getTags());
86 13
        $extra = ($this->showOutput && $run instanceof OutputterInterface && mb_strlen($run->getLastMessage()) > 0)
87 13
            ? '  ' . $this->terminal->filter($run->getLastMessage())
88 13
            : '';
89 13
        return sprintf("%s (<comment>%6.2fs</comment>) %s%s", $tags, $run->getDuration(), $status, $extra);
90
    }
91
92
    /**
93
     * @param RunInterface $run
94
     */
95 13
    public function add(RunInterface $run)
96
    {
97 13
        $index = count($this->rows);
98 13
        $this->rows[$index] = $this->formatRow($run, '');
99 13
        $spinner = 0;
100 13
        $bar = new TinyProgressBar(2, TinyProgressBar::FORMAT_DEFAULT, 1);
101
102 13
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
103 10
            $run->addListener(
104 10
                RunEvent::UPDATED,
105
                function (RunEvent $event) use ($index, &$spinner, $bar) {
106 10
                    $run = $event->getRun();
107 10
                    $progress = $run->getProgress();
108 10
                    $status = (!is_null($progress))
109 10
                        ? $bar->setPosition($progress[2])
110 1
                              ->setPosition($progress[0])
111 1
                              ->setMax($progress[1])
112 1
                              ->render()
113 10
                        : mb_substr(static::SPINNER, $spinner++, 1);
114 10
                    $this->rows[$index] = $this->formatRow(
115 10
                        $run,
116
                        $status
117 10
                    );
118 10
                    if ($spinner > mb_strlen(static::SPINNER) - 1) {
119 1
                        $spinner = 0;
120 1
                    }
121 10
                    $this->render();
122 10
                }
123 10
            );
124 10
        }
125
126 13
        $run->addListener(
127 13
            RunEvent::SUCCESSFUL,
128
            function (RunEvent $event) use ($index, &$bar, &$spinner) {
0 ignored issues
show
The import $spinner is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
The import $bar is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
129 11
                $this->rows[$index] = $this->formatRow($event->getRun(), "<info>✓</info>");
130 11
                $this->render($index);
131 11
            }
132 13
        );
133 13
        $run->addListener(
134 13
            RunEvent::FAILED,
135 3
            function (RunEvent $event) use ($index, &$bar, &$spinner) {
0 ignored issues
show
The import $spinner is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
The import $bar is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
136 3
                $run = $event->getRun();
137 3
                $this->rows[$index] = $this->formatRow($run, "<error>x</error>");
138 3
                $this->render($index);
139 3
                $this->exceptions += $run->getExceptions();
140 3
            }
141 13
        );
142 13
        if ($run instanceof ProcessRun) {
143 13
            $run->setUpdateOnProcessOutput(false);
144 13
        }
145 13
        $this->updateRowKeyLengths($run->getTags());
146 13
    }
147
148
    /**
149
     * @return string
150
     */
151 2
    private function getSummary()
152
    {
153 2
        $running = count($this->pool->getRunning());
154 2
        $finished = count($this->pool->getFinished());
155 2
        if ($running > 0 || $finished > 0) {
156 2
            if ($running > 0) {
157 2
                return sprintf(
158 2
                    '<comment>Total</comment>: %2d, <comment>Running</comment>: %2d, <comment>Waiting</comment>: %2d',
159 2
                    $this->pool->count(),
160 2
                    $running,
161 2
                    count($this->pool->getWaiting())
162 2
                );
163
            } else {
164 2
                return '';
165
            }
166
        } else {
167 2
            return 'waiting...';
168
        }
169
    }
170
171
    /**
172
     * Render a specific row
173
     *
174
     * @param int $row
175
     */
176 13
    private function render($row = 0)
177
    {
178 13
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
179 10
            $rows = ($this->showSummary ? array_merge($this->rows, [$this->getSummary()]) : $this->rows);
180 10
            $this->output->reWrite($rows, !$this->showSummary);
181 10
        } else {
182 3
            $this->output->writeln($this->rows[$row]);
183
        }
184 13
    }
185
186
    /**
187
     * @param float $checkInterval
188
     *
189
     * @return bool true if all processes were successful
190
     * @throws Exception
191
     */
192 13
    public function run($checkInterval = PriorityPool::CHECK_INTERVAL)
193
    {
194 13
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
195 10
            $this->render();
196 10
        }
197 13
        $output = $this->pool->run($checkInterval);
198 13
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE && $this->showSummary) {
199 2
            $this->render();
200 2
        }
201
202 13
        if (count($this->exceptions) > 0) {
203 3
            foreach ($this->exceptions as $exception) {
204 3
                $this->output->writeln($exception->getMessage());
205 3
            }
206
207 3
            throw reset($this->exceptions);
208
        }
209
210 10
        return $output;
211
    }
212
213
    /**
214
     * @return bool
215
     */
216 1
    public function isShowOutput()
217
    {
218 1
        return $this->showOutput;
219
    }
220
221
    /**
222
     * @param bool $showOutput
223
     *
224
     * @return $this
225
     */
226 11
    public function setShowOutput($showOutput)
227
    {
228 11
        $this->showOutput = $showOutput;
229 11
        return $this;
230
    }
231
232
    /**
233
     * @return bool
234
     */
235 1
    public function isShowSummary()
236
    {
237 1
        return $this->showSummary;
238
    }
239
240
    /**
241
     * @param bool $showSummary
242
     *
243
     * @return $this
244
     */
245 14
    public function setShowSummary($showSummary)
246
    {
247 14
        $this->showSummary = $showSummary;
248 14
        return $this;
249
    }
250
}
251