Passed
Branch master (0917e1)
by MusikAnimal
11:12
created

Pages::getLastTimestamp()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
ccs 0
cts 6
cp 0
crap 6
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 mixed[] The list of pages including various statistics, keyed by namespace. */
27
    protected $pages;
28
29
    /** @var mixed[] Number of redirects/pages that were created/deleted, broken down by namespace. */
30
    protected $countsByNamespace;
31
32
    /**
33
     * Pages constructor.
34
     * @param Project $project
35
     * @param User $user
36
     * @param string|int $namespace Namespace ID or 'all'.
37
     * @param string $redirects One of 'noredirects', 'onlyredirects' or 'all' for both.
38
     * @param string $deleted One of 'live', 'deleted' or 'all' for both.
39
     * @param int|false $start Start date as Unix timestamp.
40
     * @param int|false $end End date as Unix timestamp.
41
     * @param int|false $offset Unix timestamp. Used for pagination.
42
     */
43 2
    public function __construct(
44
        Project $project,
45
        User $user,
46
        $namespace = 0,
47
        $redirects = 'noredirects',
48
        $deleted = 'all',
49
        $start = false,
50
        $end = false,
51
        $offset = false
52
    ) {
53 2
        $this->project = $project;
54 2
        $this->user = $user;
55 2
        $this->namespace = 'all' === $namespace ? 'all' : (string)$namespace;
56 2
        $this->start = $start;
57 2
        $this->end = $end;
58 2
        $this->redirects = $redirects ?: 'noredirects';
59 2
        $this->deleted = $deleted ?: 'all';
60 2
        $this->offset = $offset;
61 2
    }
62
63
    /**
64
     * The redirects option associated with this Pages instance.
65
     * @return string
66
     */
67 1
    public function getRedirects(): string
68
    {
69 1
        return $this->redirects;
70
    }
71
72
    /**
73
     * The deleted pages option associated with this Page instance.
74
     * @return string
75
     */
76
    public function getDeleted(): string
77
    {
78
        return $this->deleted;
79
    }
80
81
    /**
82
     * Fetch and prepare the pages created by the user.
83
     * @param bool $all Whether to get *all* results. This should only be used for
84
     *     export options. HTTP and JSON should paginate.
85
     * @return array
86
     * @codeCoverageIgnore
87
     */
88
    public function prepareData(bool $all = false): array
89
    {
90
        $this->pages = [];
91
92
        foreach ($this->getNamespaces() as $ns) {
93
            $data = $this->fetchPagesCreated($ns, $all);
94
            $this->pages[$ns] = count($data) > 0
95
                ? $this->formatPages($data)[$ns]
96
                : [];
97
        }
98
99
        return $this->pages;
100
    }
101
102
    /**
103
     * The public function to get the list of all pages created by the user,
104
     * up to self::resultsPerPage(), across all namespaces.
105
     * @param bool $all Whether to get *all* results. This should only be used for
106
     *     export options. HTTP and JSON should paginate.
107
     * @return array
108
     */
109 1
    public function getResults(bool $all = false): array
110
    {
111 1
        if (null === $this->pages) {
112
            $this->prepareData($all);
113
        }
114 1
        return $this->pages;
115
    }
116
117
    /**
118
     * Return a ISO 8601 timestamp of the last result. This is used for pagination purposes.
119
     * @return string|null
120
     */
121
    public function getLastTimestamp(): ?string
122
    {
123
        if ($this->isMultiNamespace()) {
124
            // No pagination in multi-namespace view.
125
            return null;
126
        }
127
128
        $numResults = count($this->getResults()[$this->getNamespace()]);
129
        $timestamp = new DateTime($this->getResults()[$this->getNamespace()][$numResults - 1]['rev_timestamp']);
130
        return $timestamp->format('Y-m-d\TH:i:s');
131
    }
132
133
    /**
134
     * Get the total number of pages the user has created.
135
     * @return int
136
     */
137
    public function getNumPages(): int
138
    {
139
        $total = 0;
140
        foreach (array_values($this->getCounts()) as $values) {
141
            $total += $values['count'];
142
        }
143
        return $total;
144
    }
145
146
    /**
147
     * Get the total number of pages we're showing data for.
148
     * @return int
149
     */
150 1
    public function getNumResults(): int
151
    {
152 1
        $total = 0;
153 1
        foreach (array_values($this->getResults()) as $pages) {
154 1
            $total += count($pages);
155
        }
156 1
        return $total;
157
    }
158
159
    /**
160
     * Get the total number of pages that are currently deleted.
161
     * @return int
162
     */
163 1
    public function getNumDeleted(): int
164
    {
165 1
        $total = 0;
166 1
        foreach (array_values($this->getCounts()) as $values) {
167 1
            $total += $values['deleted'];
168
        }
169 1
        return $total;
170
    }
171
172
    /**
173
     * Get the total number of pages that are currently redirects.
174
     * @return int
175
     */
176 1
    public function getNumRedirects(): int
177
    {
178 1
        $total = 0;
179 1
        foreach (array_values($this->getCounts()) as $values) {
180 1
            $total += $values['redirects'];
181
        }
182 1
        return $total;
183
    }
184
185
    /**
186
     * Get the namespaces in which this user has created pages.
187
     * @return int[] The IDs.
188
     */
189 1
    public function getNamespaces(): array
190
    {
191 1
        return array_keys($this->getCounts());
192
    }
193
194
    /**
195
     * Number of namespaces being reported.
196
     * @return int
197
     */
198 1
    public function getNumNamespaces(): int
199
    {
200 1
        return count(array_keys($this->getCounts()));
201
    }
202
203
    /**
204
     * Are there more than one namespace in the results?
205
     * @return bool
206
     */
207 1
    public function isMultiNamespace(): bool
208
    {
209 1
        return $this->getNumNamespaces() > 1;
210
    }
211
212
    /**
213
     * Get the sum of all page sizes, across all specified namespaces.
214
     * @return int
215
     */
216
    public function getTotalPageSize(): int
217
    {
218
        return array_sum(array_column($this->getCounts(), 'total_length'));
219
    }
220
221
    /**
222
     * Get average size across all pages.
223
     * @return float
224
     */
225
    public function averagePageSize(): float
226
    {
227
        return $this->getTotalPageSize() / $this->getNumPages();
228
    }
229
230
    /**
231
     * Number of redirects/pages that were created/deleted, broken down by namespace.
232
     * @return array Namespace IDs as the keys, with values 'count', 'deleted' and 'redirects'.
233
     */
234 1
    public function getCounts(): array
235
    {
236 1
        if (null !== $this->countsByNamespace) {
237 1
            return $this->countsByNamespace;
238
        }
239
240 1
        $counts = [];
241
242 1
        foreach ($this->countPagesCreated() as $row) {
243 1
            $ns = (int)$row['namespace'];
244 1
            $count = (int)$row['count'];
245 1
            $totalLength = (int)$row['total_length'];
246 1
            $counts[$ns] = [
247 1
                'count' => $count,
248 1
                'total_length' => $totalLength,
249 1
                'avg_length' => $count > 0 ? $totalLength / $count : 0,
250
            ];
251 1
            if ('live' !== $this->deleted) {
252 1
                $counts[$ns]['deleted'] = (int)$row['deleted'];
253
            }
254 1
            if ('noredirects' !== $this->redirects) {
255 1
                $counts[$ns]['redirects'] = (int)$row['redirects'];
256
            }
257
        }
258
259 1
        $this->countsByNamespace = $counts;
260 1
        return $this->countsByNamespace;
261
    }
262
263
    /**
264
     * Get the number of pages the user created by assessment.
265
     * @return array Keys are the assessment class, values are the counts.
266
     */
267
    public function getAssessmentCounts(): array
268
    {
269
        if ($this->getNumPages() > $this->resultsPerPage()) {
270
            $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

270
            $counts = $this->getRepository()->/** @scrutinizer ignore-call */ getAssessmentCounts(
Loading history...
271
                $this->project,
272
                $this->user,
273
                $this->namespace,
274
                $this->redirects
275
            );
276
        } else {
277
            $counts = [];
278
            foreach ($this->pages as $nsPages) {
279
                foreach ($nsPages as $page) {
280
                    if (!isset($counts[$page['pa_class']])) {
281
                        $counts[$page['pa_class']] = 1;
282
                    } else {
283
                        $counts[$page['pa_class']]++;
284
                    }
285
                }
286
            }
287
        }
288
289
        arsort($counts);
290
291
        return $counts;
292
    }
293
294
    /**
295
     * Number of results to show, depending on the namespace.
296
     * @param bool $all Whether to get *all* results. This should only be used for
297
     *     export options. HTTP and JSON should paginate.
298
     * @return int|false
299
     */
300 1
    public function resultsPerPage(bool $all = false)
301
    {
302 1
        if (true === $all) {
303
            return false;
304
        }
305 1
        if ('all' === $this->namespace) {
306
            return self::RESULTS_LIMIT_ALL_NAMESPACES;
307
        }
308 1
        return self::RESULTS_LIMIT_SINGLE_NAMESPACE;
309
    }
310
311
    /**
312
     * Run the query to get pages created by the user with options.
313
     * This is ran independently for each namespace if $this->namespace is 'all'.
314
     * @param int $namespace Namespace ID.
315
     * @param bool $all Whether to get *all* results. This should only be used for
316
     *     export options. HTTP and JSON should paginate.
317
     * @return array
318
     */
319 1
    private function fetchPagesCreated(int $namespace, bool $all = false): array
320
    {
321 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

321
        return $this->getRepository()->/** @scrutinizer ignore-call */ getPagesCreated(
Loading history...
322 1
            $this->project,
323 1
            $this->user,
324 1
            $namespace,
325 1
            $this->redirects,
326 1
            $this->deleted,
327 1
            $this->start,
328 1
            $this->end,
329 1
            $this->resultsPerPage($all),
330 1
            $this->offset
331
        );
332
    }
333
334
    /**
335
     * Run the query to get the number of pages created by the user with given options.
336
     * @return array
337
     */
338 1
    private function countPagesCreated(): array
339
    {
340 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

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