Passed
Push — master ( 7f6d09...5bcfa0 )
by MusikAnimal
10:58
created

Pages::getNumRedirects()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file contains only the Pages class.
4
 */
5
6
declare(strict_types = 1);
7
8
namespace AppBundle\Model;
9
10
use DateTime;
11
12
/**
13
 * A Pages provides statistics about the pages created by a given User.
14
 */
15
class Pages extends Model
16
{
17
    private const RESULTS_LIMIT_SINGLE_NAMESPACE = 1000;
18
    private const RESULTS_LIMIT_ALL_NAMESPACES = 50;
19
20
    /** @var string One of 'noredirects', 'onlyredirects' or 'all' for both. */
21
    protected $redirects;
22
23
    /** @var string One of 'live', 'deleted' or 'all' for both. */
24
    protected $deleted;
25
26
    /** @var int Pagination offset. */
27
    protected $offset;
28
29
    /** @var mixed[] The list of pages including various statistics, keyed by namespace. */
30
    protected $pages;
31
32
    /** @var mixed[] Number of redirects/pages that were created/deleted, broken down by namespace. */
33
    protected $countsByNamespace;
34
35
    /**
36
     * Pages constructor.
37
     * @param Project $project
38
     * @param User $user
39
     * @param string|int $namespace Namespace ID or 'all'.
40
     * @param string $redirects One of 'noredirects', 'onlyredirects' or 'all' for both.
41
     * @param string $deleted One of 'live', 'deleted' or 'all' for both.
42
     * @param int|false $start Start date in a format accepted by strtotime()
43
     * @param int|false $end End date in a format accepted by strtotime()
44
     * @param int $offset Pagination offset.
45
     */
46 2
    public function __construct(
47
        Project $project,
48
        User $user,
49
        $namespace = 0,
50
        $redirects = 'noredirects',
51
        $deleted = 'all',
52
        $start = false,
53
        $end = false,
54
        $offset = 0
55
    ) {
56 2
        $this->project = $project;
57 2
        $this->user = $user;
58 2
        $this->namespace = 'all' === $namespace ? 'all' : (string)$namespace;
59 2
        $this->start = false === $start ? '' : date('Y-m-d', $start);
60 2
        $this->end = false === $end ? '' : date('Y-m-d', $end);
61 2
        $this->redirects = $redirects ?: 'noredirects';
62 2
        $this->deleted = $deleted ?: 'all';
63 2
        $this->offset = $offset;
64 2
    }
65
66
    /**
67
     * The redirects option associated with this Pages instance.
68
     * @return string
69
     */
70 1
    public function getRedirects(): string
71
    {
72 1
        return $this->redirects;
73
    }
74
75
    /**
76
     * The deleted pages option associated with this Page instance.
77
     * @return string
78
     */
79
    public function getDeleted(): string
80
    {
81
        return $this->deleted;
82
    }
83
84
    /**
85
     * Fetch and prepare the pages created by the user.
86
     * @param bool $all Whether to get *all* results. This should only be used for
87
     *     export options. HTTP and JSON should paginate.
88
     * @return array
89
     * @codeCoverageIgnore
90
     */
91
    public function prepareData(bool $all = false): array
92
    {
93
        $this->pages = [];
94
95
        foreach ($this->getNamespaces() as $ns) {
96
            $data = $this->fetchPagesCreated($ns, $all);
97
            $this->pages[$ns] = count($data) > 0
98
                ? $this->formatPages($data)[$ns]
99
                : [];
100
        }
101
102
        return $this->pages;
103
    }
104
105
    /**
106
     * The public function to get the list of all pages created by the user,
107
     * up to self::resultsPerPage(), across all namespaces.
108
     * @param bool $all Whether to get *all* results. This should only be used for
109
     *     export options. HTTP and JSON should paginate.
110
     * @return array
111
     */
112 1
    public function getResults(bool $all = false): array
113
    {
114 1
        if (null === $this->pages) {
115
            $this->prepareData($all);
116
        }
117 1
        return $this->pages;
118
    }
119
120
    /**
121
     * Get the total number of pages the user has created.
122
     * @return int
123
     */
124
    public function getNumPages(): int
125
    {
126
        $total = 0;
127
        foreach (array_values($this->getCounts()) as $values) {
128
            $total += $values['count'];
129
        }
130
        return $total;
131
    }
132
133
    /**
134
     * Get the total number of pages we're showing data for.
135
     * @return int
136
     */
137 1
    public function getNumResults(): int
138
    {
139 1
        $total = 0;
140 1
        foreach (array_values($this->getResults()) as $pages) {
141 1
            $total += count($pages);
142
        }
143 1
        return $total;
144
    }
145
146
    /**
147
     * Get the total number of pages that are currently deleted.
148
     * @return int
149
     */
150 1
    public function getNumDeleted(): int
151
    {
152 1
        $total = 0;
153 1
        foreach (array_values($this->getCounts()) as $values) {
154 1
            $total += $values['deleted'];
155
        }
156 1
        return $total;
157
    }
158
159
    /**
160
     * Get the total number of pages that are currently redirects.
161
     * @return int
162
     */
163 1
    public function getNumRedirects(): int
164
    {
165 1
        $total = 0;
166 1
        foreach (array_values($this->getCounts()) as $values) {
167 1
            $total += $values['redirects'];
168
        }
169 1
        return $total;
170
    }
171
172
    /**
173
     * Get the namespaces in which this user has created pages.
174
     * @return string[] The IDs.
175
     */
176 1
    public function getNamespaces(): array
177
    {
178 1
        return array_keys($this->getCounts());
179
    }
180
181
    /**
182
     * Number of namespaces being reported.
183
     * @return int
184
     */
185 1
    public function getNumNamespaces(): int
186
    {
187 1
        return count(array_keys($this->getCounts()));
188
    }
189
190
    /**
191
     * Are there more than one namespace in the results?
192
     * @return bool
193
     */
194 1
    public function isMultiNamespace(): bool
195
    {
196 1
        return $this->getNumNamespaces() > 1;
197
    }
198
199
    /**
200
     * Get the sum of all page sizes, across all specified namespaces.
201
     * @return int
202
     */
203
    public function getTotalPageSize(): int
204
    {
205
        return array_sum(array_column($this->getCounts(), 'total_length'));
206
    }
207
208
    /**
209
     * Get average size across all pages.
210
     * @return float
211
     */
212
    public function averagePageSize(): float
213
    {
214
        return $this->getTotalPageSize() / $this->getNumPages();
215
    }
216
217
    /**
218
     * Number of redirects/pages that were created/deleted, broken down by namespace.
219
     * @return array Namespace IDs as the keys, with values 'count', 'deleted' and 'redirects'.
220
     */
221 1
    public function getCounts(): array
222
    {
223 1
        if (null !== $this->countsByNamespace) {
224 1
            return $this->countsByNamespace;
225
        }
226
227 1
        $counts = [];
228
229 1
        foreach ($this->countPagesCreated() as $row) {
230 1
            $count = (int)$row['count'];
231 1
            $totalLength = (int)$row['total_length'];
232 1
            $counts[$row['namespace']] = [
233 1
                'count' => $count,
234 1
                'total_length' => $totalLength,
235 1
                'avg_length' => $count > 0 ? $totalLength / $count : 0,
236
            ];
237 1
            if ('live' !== $this->deleted) {
238 1
                $counts[$row['namespace']]['deleted'] = (int)$row['deleted'];
239
            }
240 1
            if ('noredirects' !== $this->redirects) {
241 1
                $counts[$row['namespace']]['redirects'] = (int)$row['redirects'];
242
            }
243
        }
244
245 1
        $this->countsByNamespace = $counts;
246 1
        return $this->countsByNamespace;
247
    }
248
249
    /**
250
     * Get the number of pages the user created by assessment.
251
     * @return array Keys are the assessment class, values are the counts.
252
     */
253
    public function getAssessmentCounts(): array
254
    {
255
        if ($this->getNumPages() > $this->resultsPerPage()) {
256
            $counts = $this->getRepository()->getAssessmentCounts(
0 ignored issues
show
Bug introduced by
The method getAssessmentCounts() does not exist on AppBundle\Repository\Repository. It seems like you code against a sub-type of AppBundle\Repository\Repository such as AppBundle\Repository\PagesRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

256
            $counts = $this->getRepository()->/** @scrutinizer ignore-call */ getAssessmentCounts(
Loading history...
257
                $this->project,
258
                $this->user,
259
                $this->namespace,
260
                $this->redirects
261
            );
262
        } else {
263
            $counts = [];
264
            foreach ($this->pages as $nsPages) {
265
                foreach ($nsPages as $page) {
266
                    if (!isset($counts[$page['pa_class']])) {
267
                        $counts[$page['pa_class']] = 1;
268
                    } else {
269
                        $counts[$page['pa_class']]++;
270
                    }
271
                }
272
            }
273
        }
274
275
        arsort($counts);
276
277
        return $counts;
278
    }
279
280
    /**
281
     * Number of results to show, depending on the namespace.
282
     * @param bool $all Whether to get *all* results. This should only be used for
283
     *     export options. HTTP and JSON should paginate.
284
     * @return int|false
285
     */
286 1
    public function resultsPerPage(bool $all = false)
287
    {
288 1
        if (true === $all) {
289
            return false;
290
        }
291 1
        if ('all' === $this->namespace) {
292
            return self::RESULTS_LIMIT_ALL_NAMESPACES;
293
        }
294 1
        return self::RESULTS_LIMIT_SINGLE_NAMESPACE;
295
    }
296
297
    /**
298
     * Run the query to get pages created by the user with options.
299
     * This is ran independently for each namespace if $this->namespace is 'all'.
300
     * @param int $namespace Namespace ID.
301
     * @param bool $all Whether to get *all* results. This should only be used for
302
     *     export options. HTTP and JSON should paginate.
303
     * @return array
304
     */
305 1
    private function fetchPagesCreated(int $namespace, bool $all = false): array
306
    {
307 1
        return $this->getRepository()->getPagesCreated(
0 ignored issues
show
Bug introduced by
The method getPagesCreated() does not exist on AppBundle\Repository\Repository. It seems like you code against a sub-type of AppBundle\Repository\Repository such as AppBundle\Repository\PagesRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

307
        return $this->getRepository()->/** @scrutinizer ignore-call */ getPagesCreated(
Loading history...
308 1
            $this->project,
309 1
            $this->user,
310 1
            $namespace,
311 1
            $this->redirects,
312 1
            $this->deleted,
313 1
            $this->start,
314 1
            $this->end,
315 1
            $this->resultsPerPage($all),
316 1
            $this->offset * $this->resultsPerPage()
317
        );
318
    }
319
320
    /**
321
     * Run the query to get the number of pages created by the user with given options.
322
     * @return array
323
     */
324 1
    private function countPagesCreated(): array
325
    {
326 1
        return $this->getRepository()->countPagesCreated(
0 ignored issues
show
Bug introduced by
The method countPagesCreated() does not exist on AppBundle\Repository\Repository. It seems like you code against a sub-type of AppBundle\Repository\Repository such as AppBundle\Repository\PagesRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

326
        return $this->getRepository()->/** @scrutinizer ignore-call */ countPagesCreated(
Loading history...
327 1
            $this->project,
328 1
            $this->user,
329 1
            $this->namespace,
330 1
            $this->redirects,
331 1
            $this->deleted,
332 1
            $this->start,
333 1
            $this->end
334
        );
335
    }
336
337
    /**
338
     * Format the data, adding humanized timestamps, page titles, assessment badges,
339
     * and sorting by namespace and then timestamp.
340
     * @param array $pages As returned by self::fetchPagesCreated()
341
     * @return array
342
     */
343 1
    private function formatPages(array $pages): array
344
    {
345 1
        $results = [];
346
347 1
        foreach ($pages as $row) {
348 1
            $datetime = DateTime::createFromFormat('YmdHis', $row['rev_timestamp']);
349 1
            $datetimeHuman = $datetime->format('Y-m-d H:i');
350
351 1
            $pageData = array_merge($row, [
352 1
                'raw_time' => $row['rev_timestamp'],
353 1
                'human_time' => $datetimeHuman,
354 1
                'page_title' => str_replace('_', ' ', $row['page_title']),
355
            ]);
356
357 1
            if ($this->project->hasPageAssessments()) {
358
                $pageData['badge'] = $this->project
359
                    ->getPageAssessments()
360
                    ->getBadgeURL($pageData['pa_class']);
361
                $pageData['badge_file'] = $this->project
362
                    ->getPageAssessments()
363
                    ->getBadgeURL($pageData['pa_class'], true);
364
            }
365
366 1
            $results[$row['namespace']][] = $pageData;
367
        }
368
369 1
        return $results;
370
    }
371
}
372