Completed
Push — master ( 07e153...ca5d76 )
by Stanislav
02:20
created

TorrentListUtils::printTable()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 17
rs 9.2
cc 4
eloc 11
nc 4
nop 4
1
<?php
2
3
namespace Popstas\Transmission\Console\Helpers;
4
5
use Martial\Transmission\API\Argument\Torrent;
6
use Symfony\Component\Console\Helper\Table;
7
use Symfony\Component\Console\Helper\TableSeparator;
8
use Symfony\Component\Console\Output\OutputInterface;
9
10
class TorrentListUtils
11
{
12
    public static function getTorrentsSize(array $torrentList, $fieldName = Torrent\Get::TOTAL_SIZE)
13
    {
14
        $torrentSize = 0;
15
        foreach ($torrentList as $torrent) {
16
            $torrentSize += $torrent[$fieldName];
17
        }
18
        return $torrentSize;
19
    }
20
21
    public static function getTorrentsField(array $torrentList, $fieldName)
22
    {
23
        $fields = [];
24
        foreach ($torrentList as $torrent) {
25
            $fields[] = $torrent[$fieldName];
26
        }
27
        return $fields;
28
    }
29
30
    /**
31
     * @param array $torrentList
32
     *
33
     * @param array $filters
34
     * filters[
35
     * - age
36
     * - age_min
37
     * - age_max
38
     * ]
39
     *
40
     * @return array
41
     */
42
    public static function filterTorrents(array $torrentList, array $filters)
43
    {
44
        if (isset($filters['name'])) {
45
            $filters[Torrent\Get::NAME] = ['type' => 'regex', 'value' => $filters['name']];
46
        }
47
        if (isset($filters['age'])) {
48
            $filters['age'] = ['type' => 'numeric', 'value' => $filters['age']];
49
            // TODO: move to getTorrentData()
50
            $torrentList = array_map(function ($torrent) {
51
                $torrent['age'] = TorrentUtils::getTorrentAgeInDays($torrent);
52
                return $torrent;
53
            }, $torrentList);
54
        }
55
56
        $filters = self::parseFilters($filters);
57
        $torrentList = self::filterRows($torrentList, $filters);
58
        return $torrentList;
59
    }
60
61
    private static function parseFilters(array $filters)
62
    {
63
        foreach ($filters as $columnKey => $filter) {
64
            if (is_array($filter) && !isset($filter['type'])) {
65
                throw new \InvalidArgumentException('Unknown filter type');
66
            }
67
            if (!isset($filter) || !isset($filter['value'])) {
68
                unset($filters[$columnKey]);
69
                continue;
70
            }
71
            switch ($filter['type']) {
72
                case 'numeric':
73
                    $filters[$columnKey] = self::parseNumericFilter($filter['value'])
74
                        + $filters[$columnKey];
75
                    break;
76
                case 'regex':
77
                    $filters[$columnKey] = self::parseRegexFilter($filter['value'])
78
                        + $filters[$columnKey];
79
                    break;
80
                default:
81
                    throw new \InvalidArgumentException('Unknown filter type');
82
            }
83
        }
84
        return $filters;
85
    }
86
87
    private static function parseNumericFilter($filterString)
88
    {
89
        $filter = [];
90
        preg_match_all('/([<>])\s?([\d\.]+)/', $filterString, $results, PREG_SET_ORDER);
91
        if ($results) {
92
            foreach ($results as $result) {
93
                $operator = $result[1];
94
                $value = $result[2];
95
                if ($operator == '<') {
96
                    $filter['max'] = $value;
97
                }
98
                if ($operator == '>') {
99
                    $filter['min'] = $value;
100
                }
101
            }
102
        }
103
        return $filter;
104
    }
105
106
    public static function parseRegexFilter($filterString)
107
    {
108
        $filter = [];
109
        $filter['regex'] = str_replace(['/', '.', '*'], ['\/', '\.', '.*?'], $filterString);
110
        return $filter;
111
    }
112
113
    public static function filterRows(array $rows, $filters)
114
    {
115
        $filters = self::parseFilters($filters);
116
117
        return array_filter($rows, function ($row) use ($filters) {
118
            foreach ($filters as $columnKey => $filter) {
119
                if (!isset($row[$columnKey])) {
120
                    continue;
121
                }
122
                $columnValue = $row[$columnKey];
123
124
                if ($filter['type'] == 'numeric') {
125
                    if ((isset($filter['min']) && $columnValue <= $filter['min']) ||
126
                        (isset($filter['max']) && $columnValue >= $filter['max'])
127
                    ) {
128
                        return false;
129
                    }
130
                }
131
132
                if ($filter['type'] == 'regex') {
133
                    if (!preg_match('/' . $filter['regex'] . '/i', $columnValue)) {
134
                        return false;
135
                    }
136
                }
137
            }
138
            return true;
139
        });
140
    }
141
142
    public static function sortRowsByColumnNumber(array $rows, $columnNumber)
143
    {
144
        $rowsSorted = $rows;
145
        $columnsTotal = count(end($rows));
146
147
        $sortOrder = $columnNumber > 0 ? 1 : -1;
148
149
        $columnIndex = max(1, min(
150
            $columnsTotal,
151
            abs($columnNumber)
152
        )) - 1;
153
154
        usort($rowsSorted, function ($first, $second) use ($columnIndex, $sortOrder) {
155
            return $first[$columnIndex] > $second[$columnIndex] ? $sortOrder : $sortOrder * -1;
156
        });
157
158
        return $rowsSorted;
159
    }
160
161
    public static function buildTableData(array $torrentList)
162
    {
163
        $headers = ['Name', 'Id', 'Age', 'Size', 'Uploaded', 'Per day'];
164
        $rows = [];
165
166
        foreach ($torrentList as $torrent) {
167
            $age = TorrentUtils::getTorrentAgeInDays($torrent);
168
            $perDay = $age ? TorrentUtils::getSizeInGb($torrent[Torrent\Get::UPLOAD_EVER] / $age) : 0;
169
170
            $rows[] = [
171
                $torrent[Torrent\Get::NAME],
172
                $torrent[Torrent\Get::ID],
173
                $age,
174
                TorrentUtils::getSizeInGb($torrent[Torrent\Get::TOTAL_SIZE]),
175
                TorrentUtils::getSizeInGb($torrent[Torrent\Get::UPLOAD_EVER]),
176
                $perDay,
177
            ];
178
        }
179
180
        $totals = [
181
            'Total',
182
            '',
183
            '',
184
            TorrentUtils::getSizeInGb(self::getTorrentsSize($torrentList, Torrent\Get::TOTAL_SIZE)),
185
            TorrentUtils::getSizeInGb(self::getTorrentsSize($torrentList, Torrent\Get::UPLOAD_EVER)),
186
            ''
187
        ];
188
189
        return [
190
            'headers' => $headers,
191
            'rows' => $rows,
192
            'totals' => $totals
193
        ];
194
    }
195
196
    public static function printTorrentsTable(
197
        array $torrentList,
198
        OutputInterface $output,
199
        $sortColumnNumber = 1,
200
        $limit = 0
201
    ) {
202
        $data = self::buildTableData($torrentList);
203
        self::printTable($data, $output, $sortColumnNumber, $limit);
204
    }
205
206
    public static function printTable(array $tableData, OutputInterface $output, $sortColumnNumber = 1, $limit = 0)
207
    {
208
        $tableData['rows'] = self::sortRowsByColumnNumber($tableData['rows'], $sortColumnNumber);
209
210
        if ($limit && $limit < count($tableData['rows'])) {
211
            $tableData['rows'] = array_slice($tableData['rows'], 0, $limit);
212
        }
213
214
        $table = new Table($output);
215
        $table->setHeaders($tableData['headers']);
216
        $table->setRows($tableData['rows']);
217
        $table->addRow(new TableSeparator());
218
        if (isset($tableData['totals'])) {
219
            $table->addRow($tableData['totals']);
220
        }
221
        $table->render();
222
    }
223
224
    public static function getObsoleteTorrents(array $torrentList)
225
    {
226
        $all = [];
227
        $obsolete = [];
228
229
        foreach ($torrentList as $torrent) {
230
            $name = $torrent[Torrent\Get::NAME];
231
            if (!isset($all[$name])) {
232
                $all[$name] = $torrent;
233
                continue;
234
            }
235
236
            $obs = self::detectObsoleteTorrent($all[$name], $torrent);
237
            if ($obs) {
238
                $obsolete[] = $obs;
239
                if ($obs[Torrent\Get::ID] !== $torrent[Torrent\Get::ID]) {
240
                    $all[$name] = $torrent;
241
                }
242
            }
243
        }
244
245
        return $obsolete;
246
    }
247
248
    private static function detectObsoleteTorrent($torrentInList, $torrentNotInList)
249
    {
250
        if ($torrentInList[Torrent\Get::DOWNLOAD_DIR] !== $torrentNotInList[Torrent\Get::DOWNLOAD_DIR]) {
251
            return false;
252
        }
253
254
        return $torrentInList[Torrent\Get::TOTAL_SIZE] < $torrentNotInList[Torrent\Get::TOTAL_SIZE] ?
255
            $torrentInList : $torrentNotInList;
256
    }
257
}
258