Total Complexity | 121 |
Total Lines | 1031 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like ArticleInfo often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ArticleInfo, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
16 | class ArticleInfo extends ArticleInfoApi |
||
17 | { |
||
18 | /** @var I18nHelper For i18n and l10n. */ |
||
19 | protected $i18n; |
||
20 | |||
21 | /** @var int Number of revisions that were actually processed. */ |
||
22 | protected $numRevisionsProcessed; |
||
23 | |||
24 | /** |
||
25 | * Various statistics about editors to the page. These are not User objects |
||
26 | * so as to preserve memory. |
||
27 | * @var mixed[] |
||
28 | */ |
||
29 | protected $editors = []; |
||
30 | |||
31 | /** @var mixed[] The top 10 editors to the page by number of edits. */ |
||
32 | protected $topTenEditorsByEdits; |
||
33 | |||
34 | /** @var mixed[] The top 10 editors to the page by added text. */ |
||
35 | protected $topTenEditorsByAdded; |
||
36 | |||
37 | /** @var int Number of edits made by the top 10 editors. */ |
||
38 | protected $topTenCount; |
||
39 | |||
40 | /** @var mixed[] Various counts about each individual year and month of the page's history. */ |
||
41 | protected $yearMonthCounts; |
||
42 | |||
43 | /** @var string[] Localized labels for the years, to be used in the 'Year counts' chart. */ |
||
44 | protected $yearLabels = []; |
||
45 | |||
46 | /** @var string[] Localized labels for the months, to be used in the 'Month counts' chart. */ |
||
47 | protected $monthLabels = []; |
||
48 | |||
49 | /** @var Edit The first edit to the page. */ |
||
50 | protected $firstEdit; |
||
51 | |||
52 | /** @var Edit The last edit to the page. */ |
||
53 | protected $lastEdit; |
||
54 | |||
55 | /** @var Edit Edit that made the largest addition by number of bytes. */ |
||
56 | protected $maxAddition; |
||
57 | |||
58 | /** @var Edit Edit that made the largest deletion by number of bytes. */ |
||
59 | protected $maxDeletion; |
||
60 | |||
61 | /** |
||
62 | * Maximum number of edits that were created across all months. This is used as a comparison |
||
63 | * for the bar charts in the months section. |
||
64 | * @var int |
||
65 | */ |
||
66 | protected $maxEditsPerMonth; |
||
67 | |||
68 | /** @var string[][] List of (semi-)automated tools that were used to edit the page. */ |
||
69 | protected $tools; |
||
70 | |||
71 | /** |
||
72 | * Total number of bytes added throughout the page's history. This is used as a comparison |
||
73 | * when computing the top 10 editors by added text. |
||
74 | * @var int |
||
75 | */ |
||
76 | protected $addedBytes = 0; |
||
77 | |||
78 | /** @var int Number of days between first and last edit. */ |
||
79 | protected $totalDays; |
||
80 | |||
81 | /** @var int Number of minor edits to the page. */ |
||
82 | protected $minorCount = 0; |
||
83 | |||
84 | /** @var int Number of anonymous edits to the page. */ |
||
85 | protected $anonCount = 0; |
||
86 | |||
87 | /** @var int Number of automated edits to the page. */ |
||
88 | protected $automatedCount = 0; |
||
89 | |||
90 | /** @var int Number of edits to the page that were reverted with the subsequent edit. */ |
||
91 | protected $revertCount = 0; |
||
92 | |||
93 | /** @var int[] The "edits per <time>" counts. */ |
||
94 | protected $countHistory = [ |
||
95 | 'day' => 0, |
||
96 | 'week' => 0, |
||
97 | 'month' => 0, |
||
98 | 'year' => 0, |
||
99 | ]; |
||
100 | |||
101 | /** |
||
102 | * Make the I18nHelper accessible to ArticleInfo. |
||
103 | * @param I18nHelper $i18n |
||
104 | * @codeCoverageIgnore |
||
105 | */ |
||
106 | public function setI18nHelper(I18nHelper $i18n): void |
||
107 | { |
||
108 | $this->i18n = $i18n; |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * Get the day of last date we should show in the month/year sections, |
||
113 | * based on $this->end or the current date. |
||
114 | * @return int As Unix timestamp. |
||
115 | */ |
||
116 | private function getLastDay(): int |
||
117 | { |
||
118 | if (is_int($this->end)) { |
||
119 | return (new DateTime("@$this->end")) |
||
120 | ->modify('last day of this month') |
||
121 | ->getTimestamp(); |
||
122 | } else { |
||
123 | return strtotime('last day of this month'); |
||
124 | } |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * Return the start/end date values as associative array, with YYYY-MM-DD as the date format. |
||
129 | * This is used mainly as a helper to pass to the pageviews Twig macros. |
||
130 | * @return array |
||
131 | */ |
||
132 | public function getDateParams(): array |
||
133 | { |
||
134 | if (!$this->hasDateRange()) { |
||
135 | return []; |
||
136 | } |
||
137 | |||
138 | $ret = [ |
||
139 | 'start' => $this->firstEdit->getTimestamp()->format('Y-m-d'), |
||
140 | 'end' => $this->lastEdit->getTimestamp()->format('Y-m-d'), |
||
141 | ]; |
||
142 | |||
143 | if (is_int($this->start)) { |
||
144 | $ret['start'] = date('Y-m-d', $this->start); |
||
145 | } |
||
146 | if (is_int($this->end)) { |
||
147 | $ret['end'] = date('Y-m-d', $this->end); |
||
148 | } |
||
149 | |||
150 | return $ret; |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * Get the number of revisions that are actually getting processed. This goes by the app.max_page_revisions |
||
155 | * parameter, or the actual number of revisions, whichever is smaller. |
||
156 | * @return int |
||
157 | */ |
||
158 | public function getNumRevisionsProcessed(): int |
||
159 | { |
||
160 | if (isset($this->numRevisionsProcessed)) { |
||
161 | return $this->numRevisionsProcessed; |
||
162 | } |
||
163 | |||
164 | if ($this->tooManyRevisions()) { |
||
165 | $this->numRevisionsProcessed = $this->getMaxRevisions(); |
||
166 | } else { |
||
167 | $this->numRevisionsProcessed = $this->getNumRevisions(); |
||
168 | } |
||
169 | |||
170 | return $this->numRevisionsProcessed; |
||
171 | } |
||
172 | |||
173 | /** |
||
174 | * Fetch and store all the data we need to show the ArticleInfo view. |
||
175 | * @codeCoverageIgnore |
||
176 | */ |
||
177 | public function prepareData(): void |
||
178 | { |
||
179 | $this->parseHistory(); |
||
180 | $this->setLogsEvents(); |
||
181 | |||
182 | // Bots need to be set before setting top 10 counts. |
||
183 | $this->bots = $this->getBots(); |
||
184 | |||
185 | $this->doPostPrecessing(); |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Get the number of editors that edited the page. |
||
190 | * @return int |
||
191 | */ |
||
192 | public function getNumEditors(): int |
||
193 | { |
||
194 | return count($this->editors); |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * Get the number of days between the first and last edit. |
||
199 | * @return int |
||
200 | */ |
||
201 | public function getTotalDays(): int |
||
202 | { |
||
203 | if (isset($this->totalDays)) { |
||
204 | return $this->totalDays; |
||
205 | } |
||
206 | $dateFirst = $this->firstEdit->getTimestamp(); |
||
207 | $dateLast = $this->lastEdit->getTimestamp(); |
||
208 | $interval = date_diff($dateLast, $dateFirst, true); |
||
209 | $this->totalDays = (int)$interval->format('%a'); |
||
210 | return $this->totalDays; |
||
211 | } |
||
212 | |||
213 | /** |
||
214 | * Returns length of the page. |
||
215 | * @return int |
||
216 | */ |
||
217 | public function getLength(): int |
||
218 | { |
||
219 | if ($this->hasDateRange()) { |
||
220 | return $this->lastEdit->getLength(); |
||
221 | } |
||
222 | |||
223 | return $this->page->getLength(); |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Get the average number of days between edits to the page. |
||
228 | * @return float |
||
229 | */ |
||
230 | public function averageDaysPerEdit(): float |
||
231 | { |
||
232 | return round($this->getTotalDays() / $this->getNumRevisionsProcessed(), 1); |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Get the average number of edits per day to the page. |
||
237 | * @return float |
||
238 | */ |
||
239 | public function editsPerDay(): float |
||
240 | { |
||
241 | $editsPerDay = $this->getTotalDays() |
||
242 | ? $this->getNumRevisionsProcessed() / ($this->getTotalDays() / (365 / 12 / 24)) |
||
243 | : 0; |
||
244 | return round($editsPerDay, 1); |
||
245 | } |
||
246 | |||
247 | /** |
||
248 | * Get the average number of edits per month to the page. |
||
249 | * @return float |
||
250 | */ |
||
251 | public function editsPerMonth(): float |
||
252 | { |
||
253 | $editsPerMonth = $this->getTotalDays() |
||
254 | ? $this->getNumRevisionsProcessed() / ($this->getTotalDays() / (365 / 12)) |
||
255 | : 0; |
||
256 | return min($this->getNumRevisionsProcessed(), round($editsPerMonth, 1)); |
||
257 | } |
||
258 | |||
259 | /** |
||
260 | * Get the average number of edits per year to the page. |
||
261 | * @return float |
||
262 | */ |
||
263 | public function editsPerYear(): float |
||
264 | { |
||
265 | $editsPerYear = $this->getTotalDays() |
||
266 | ? $this->getNumRevisionsProcessed() / ($this->getTotalDays() / 365) |
||
267 | : 0; |
||
268 | return min($this->getNumRevisionsProcessed(), round($editsPerYear, 1)); |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * Get the average number of edits per editor. |
||
273 | * @return float |
||
274 | */ |
||
275 | public function editsPerEditor(): float |
||
276 | { |
||
277 | if (count($this->editors) > 0) { |
||
278 | return round($this->getNumRevisionsProcessed() / count($this->editors), 1); |
||
279 | } |
||
280 | |||
281 | // To prevent division by zero error; can happen if all usernames are removed (see T303724). |
||
282 | return 0; |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * Get the percentage of minor edits to the page. |
||
287 | * @return float |
||
288 | */ |
||
289 | public function minorPercentage(): float |
||
290 | { |
||
291 | return round( |
||
292 | ($this->minorCount / $this->getNumRevisionsProcessed()) * 100, |
||
293 | 1 |
||
294 | ); |
||
295 | } |
||
296 | |||
297 | /** |
||
298 | * Get the percentage of anonymous edits to the page. |
||
299 | * @return float |
||
300 | */ |
||
301 | public function anonPercentage(): float |
||
302 | { |
||
303 | return round( |
||
304 | ($this->anonCount / $this->getNumRevisionsProcessed()) * 100, |
||
305 | 1 |
||
306 | ); |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * Get the percentage of edits made by the top 10 editors. |
||
311 | * @return float |
||
312 | */ |
||
313 | public function topTenPercentage(): float |
||
314 | { |
||
315 | return round(($this->topTenCount / $this->getNumRevisionsProcessed()) * 100, 1); |
||
316 | } |
||
317 | |||
318 | /** |
||
319 | * Get the number of automated edits made to the page. |
||
320 | * @return int |
||
321 | */ |
||
322 | public function getAutomatedCount(): int |
||
323 | { |
||
324 | return $this->automatedCount; |
||
325 | } |
||
326 | |||
327 | /** |
||
328 | * Get the number of edits to the page that were reverted with the subsequent edit. |
||
329 | * @return int |
||
330 | */ |
||
331 | public function getRevertCount(): int |
||
332 | { |
||
333 | return $this->revertCount; |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * Get the number of edits to the page made by logged out users. |
||
338 | * @return int |
||
339 | */ |
||
340 | public function getAnonCount(): int |
||
341 | { |
||
342 | return $this->anonCount; |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * Get the number of minor edits to the page. |
||
347 | * @return int |
||
348 | */ |
||
349 | public function getMinorCount(): int |
||
350 | { |
||
351 | return $this->minorCount; |
||
352 | } |
||
353 | |||
354 | /** |
||
355 | * Get the number of edits to the page made in the past day, week, month and year. |
||
356 | * @return int[] With keys 'day', 'week', 'month' and 'year'. |
||
357 | */ |
||
358 | public function getCountHistory(): array |
||
359 | { |
||
360 | return $this->countHistory; |
||
361 | } |
||
362 | |||
363 | /** |
||
364 | * Get the number of edits to the page made by the top 10 editors. |
||
365 | * @return int |
||
366 | */ |
||
367 | public function getTopTenCount(): int |
||
368 | { |
||
369 | return $this->topTenCount; |
||
370 | } |
||
371 | |||
372 | /** |
||
373 | * Get the first edit to the page. |
||
374 | * @return Edit |
||
375 | */ |
||
376 | public function getFirstEdit(): Edit |
||
377 | { |
||
378 | return $this->firstEdit; |
||
379 | } |
||
380 | |||
381 | /** |
||
382 | * Get the last edit to the page. |
||
383 | * @return Edit |
||
384 | */ |
||
385 | public function getLastEdit(): Edit |
||
386 | { |
||
387 | return $this->lastEdit; |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * Get the edit that made the largest addition to the page (by number of bytes). |
||
392 | * @return Edit|null |
||
393 | */ |
||
394 | public function getMaxAddition(): ?Edit |
||
395 | { |
||
396 | return $this->maxAddition; |
||
397 | } |
||
398 | |||
399 | /** |
||
400 | * Get the edit that made the largest removal to the page (by number of bytes). |
||
401 | * @return Edit|null |
||
402 | */ |
||
403 | public function getMaxDeletion(): ?Edit |
||
404 | { |
||
405 | return $this->maxDeletion; |
||
406 | } |
||
407 | |||
408 | /** |
||
409 | * Get the list of editors to the page, including various statistics. |
||
410 | * @return mixed[] |
||
411 | */ |
||
412 | public function getEditors(): array |
||
413 | { |
||
414 | return $this->editors; |
||
415 | } |
||
416 | |||
417 | /** |
||
418 | * Get usernames of human editors (not bots). |
||
419 | * @param int|null $limit |
||
420 | * @return string[] |
||
421 | */ |
||
422 | public function getHumans(?int $limit = null): array |
||
423 | { |
||
424 | return array_slice(array_diff(array_keys($this->getEditors()), array_keys($this->getBots())), 0, $limit); |
||
425 | } |
||
426 | |||
427 | /** |
||
428 | * Get the list of the top editors to the page (by edits), including various statistics. |
||
429 | * @return mixed[] |
||
430 | */ |
||
431 | public function topTenEditorsByEdits(): array |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * Get the list of the top editors to the page (by added text), including various statistics. |
||
438 | * @return mixed[] |
||
439 | */ |
||
440 | public function topTenEditorsByAdded(): array |
||
441 | { |
||
442 | return $this->topTenEditorsByAdded; |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * Get various counts about each individual year and month of the page's history. |
||
447 | * @return mixed[] |
||
448 | */ |
||
449 | public function getYearMonthCounts(): array |
||
450 | { |
||
451 | return $this->yearMonthCounts; |
||
452 | } |
||
453 | |||
454 | /** |
||
455 | * Get the localized labels for the 'Year counts' chart. |
||
456 | * @return string[] |
||
457 | */ |
||
458 | public function getYearLabels(): array |
||
461 | } |
||
462 | |||
463 | /** |
||
464 | * Get the localized labels for the 'Month counts' chart. |
||
465 | * @return string[] |
||
466 | */ |
||
467 | public function getMonthLabels(): array |
||
468 | { |
||
469 | return $this->monthLabels; |
||
470 | } |
||
471 | |||
472 | /** |
||
473 | * Get the maximum number of edits that were created across all months. This is used as a |
||
474 | * comparison for the bar charts in the months section. |
||
475 | * @return int |
||
476 | */ |
||
477 | public function getMaxEditsPerMonth(): int |
||
478 | { |
||
479 | return $this->maxEditsPerMonth; |
||
480 | } |
||
481 | |||
482 | /** |
||
483 | * Get a list of (semi-)automated tools that were used to edit the page, including |
||
484 | * the number of times they were used, and a link to the tool's homepage. |
||
485 | * @return string[] |
||
486 | */ |
||
487 | public function getTools(): array |
||
488 | { |
||
489 | return $this->tools; |
||
490 | } |
||
491 | |||
492 | /** |
||
493 | * Parse the revision history, collecting our core statistics. |
||
494 | * |
||
495 | * Untestable because it relies on getting a PDO statement. All the important |
||
496 | * logic lives in other methods which are tested. |
||
497 | * @codeCoverageIgnore |
||
498 | */ |
||
499 | private function parseHistory(): void |
||
500 | { |
||
501 | $limit = $this->tooManyRevisions() ? $this->getMaxRevisions() : null; |
||
502 | |||
503 | // Third parameter is ignored if $limit is null. |
||
504 | $revStmt = $this->page->getRevisionsStmt( |
||
505 | null, |
||
506 | $limit, |
||
507 | $this->getNumRevisions(), |
||
508 | $this->start, |
||
509 | $this->end |
||
510 | ); |
||
511 | $revCount = 0; |
||
512 | |||
513 | /** |
||
514 | * Data about previous edits so that we can use them as a basis for comparison. |
||
515 | * @var Edit[] |
||
516 | */ |
||
517 | $prevEdits = [ |
||
518 | // The previous Edit, used to discount content that was reverted. |
||
519 | 'prev' => null, |
||
520 | |||
521 | // The SHA-1 of the edit *before* the previous edit. Used for more |
||
522 | // accurate revert detection. |
||
523 | 'prevSha' => null, |
||
524 | |||
525 | // The last edit deemed to be the max addition of content. This is kept track of |
||
526 | // in case we find out the next edit was reverted (and was also a max edit), |
||
527 | // in which case we'll want to discount it and use this one instead. |
||
528 | 'maxAddition' => null, |
||
529 | |||
530 | // Same as with maxAddition, except the maximum amount of content deleted. |
||
531 | // This is used to discount content that was reverted. |
||
532 | 'maxDeletion' => null, |
||
533 | ]; |
||
534 | |||
535 | while ($rev = $revStmt->fetch()) { |
||
|
|||
536 | $edit = new Edit($this->page, $rev); |
||
537 | |||
538 | if (0 === $revCount) { |
||
539 | $this->firstEdit = $edit; |
||
540 | } |
||
541 | |||
542 | // Sometimes, with old revisions (2001 era), the revisions from 2002 come before 2001 |
||
543 | if ($edit->getTimestamp() < $this->firstEdit->getTimestamp()) { |
||
544 | $this->firstEdit = $edit; |
||
545 | } |
||
546 | |||
547 | $prevEdits = $this->updateCounts($edit, $prevEdits); |
||
548 | |||
549 | $revCount++; |
||
550 | } |
||
551 | |||
552 | $this->numRevisionsProcessed = $revCount; |
||
553 | |||
554 | // Various sorts |
||
555 | arsort($this->editors); |
||
556 | ksort($this->yearMonthCounts); |
||
557 | if ($this->tools) { |
||
558 | arsort($this->tools); |
||
559 | } |
||
560 | } |
||
561 | |||
562 | /** |
||
563 | * Update various counts based on the current edit. |
||
564 | * @param Edit $edit |
||
565 | * @param Edit[] $prevEdits With 'prev', 'prevSha', 'maxAddition' and 'maxDeletion' |
||
566 | * @return Edit[] Updated version of $prevEdits. |
||
567 | */ |
||
568 | private function updateCounts(Edit $edit, array $prevEdits): array |
||
569 | { |
||
570 | // Update the counts for the year and month of the current edit. |
||
571 | $this->updateYearMonthCounts($edit); |
||
572 | |||
573 | // Update counts for the user who made the edit. |
||
574 | $this->updateUserCounts($edit); |
||
575 | |||
576 | // Update the year/month/user counts of anon and minor edits. |
||
577 | $this->updateAnonMinorCounts($edit); |
||
578 | |||
579 | // Update counts for automated tool usage, if applicable. |
||
580 | $this->updateToolCounts($edit); |
||
581 | |||
582 | // Increment "edits per <time>" counts |
||
583 | $this->updateCountHistory($edit); |
||
584 | |||
585 | // Update figures regarding content addition/removal, and the revert count. |
||
586 | $prevEdits = $this->updateContentSizes($edit, $prevEdits); |
||
587 | |||
588 | // Now that we've updated all the counts, we can reset |
||
589 | // the prev and last edits, which are used for tracking. |
||
590 | // But first, let's copy over the SHA of the actual previous edit |
||
591 | // and put it in our $prevEdits['prev'], so that we'll know |
||
592 | // that content added after $prevEdit['prev'] was reverted. |
||
593 | if (null !== $prevEdits['prev']) { |
||
594 | $prevEdits['prevSha'] = $prevEdits['prev']->getSha(); |
||
595 | } |
||
596 | $prevEdits['prev'] = $edit; |
||
597 | $this->lastEdit = $edit; |
||
598 | |||
599 | return $prevEdits; |
||
600 | } |
||
601 | |||
602 | /** |
||
603 | * Update various figures about content sizes based on the given edit. |
||
604 | * @param Edit $edit |
||
605 | * @param Edit[] $prevEdits With 'prev', 'prevSha', 'maxAddition' and 'maxDeletion'. |
||
606 | * @return Edit[] Updated version of $prevEdits. |
||
607 | */ |
||
608 | private function updateContentSizes(Edit $edit, array $prevEdits): array |
||
609 | { |
||
610 | // Check if it was a revert |
||
611 | if ($this->isRevert($edit, $prevEdits)) { |
||
612 | $edit->setReverted(true); |
||
613 | return $this->updateContentSizesRevert($prevEdits); |
||
614 | } else { |
||
615 | return $this->updateContentSizesNonRevert($edit, $prevEdits); |
||
616 | } |
||
617 | } |
||
618 | |||
619 | /** |
||
620 | * Is the given Edit a revert? |
||
621 | * @param Edit $edit |
||
622 | * @param Edit[] $prevEdits With 'prev', 'prevSha', 'maxAddition' and 'maxDeletion'. |
||
623 | * @return bool |
||
624 | */ |
||
625 | private function isRevert(Edit $edit, array $prevEdits): bool |
||
626 | { |
||
627 | return $edit->getSha() === $prevEdits['prevSha'] || $edit->isRevert($this->container); |
||
628 | } |
||
629 | |||
630 | /** |
||
631 | * Updates the figures on content sizes assuming the given edit was a revert of the previous one. |
||
632 | * In such a case, we don't want to treat the previous edit as legit content addition or removal. |
||
633 | * @param Edit[] $prevEdits With 'prev', 'prevSha', 'maxAddition' and 'maxDeletion'. |
||
634 | * @return Edit[] Updated version of $prevEdits, for tracking. |
||
635 | */ |
||
636 | private function updateContentSizesRevert(array $prevEdits): array |
||
637 | { |
||
638 | $this->revertCount++; |
||
639 | |||
640 | // Adjust addedBytes given this edit was a revert of the previous one. |
||
641 | if ($prevEdits['prev'] && !$prevEdits['prev']->isReverted() && $prevEdits['prev']->getSize() > 0) { |
||
642 | $this->addedBytes -= $prevEdits['prev']->getSize(); |
||
643 | |||
644 | // Also deduct from the user's individual added byte count. |
||
645 | // We don't do this if the previous edit was reverted, since that would make the net bytes zero. |
||
646 | if ($prevEdits['prev']->getUser()) { |
||
647 | $username = $prevEdits['prev']->getUser()->getUsername(); |
||
648 | $this->editors[$username]['added'] -= $prevEdits['prev']->getSize(); |
||
649 | } |
||
650 | } |
||
651 | |||
652 | // @TODO: Test this against an edit war (use your sandbox). |
||
653 | // Also remove as max added or deleted, if applicable. |
||
654 | if ($this->maxAddition && $prevEdits['prev']->getId() === $this->maxAddition->getId()) { |
||
655 | $this->maxAddition = $prevEdits['maxAddition']; |
||
656 | $prevEdits['maxAddition'] = $prevEdits['prev']; // In the event of edit wars. |
||
657 | } elseif ($this->maxDeletion && $prevEdits['prev']->getId() === $this->maxDeletion->getId()) { |
||
658 | $this->maxDeletion = $prevEdits['maxDeletion']; |
||
659 | $prevEdits['maxDeletion'] = $prevEdits['prev']; // In the event of edit wars. |
||
660 | } |
||
661 | |||
662 | return $prevEdits; |
||
663 | } |
||
664 | |||
665 | /** |
||
666 | * Updates the figures on content sizes assuming the given edit was NOT a revert of the previous edit. |
||
667 | * @param Edit $edit |
||
668 | * @param Edit[] $prevEdits With 'prev', 'prevSha', 'maxAddition' and 'maxDeletion'. |
||
669 | * @return Edit[] Updated version of $prevEdits, for tracking. |
||
670 | */ |
||
671 | private function updateContentSizesNonRevert(Edit $edit, array $prevEdits): array |
||
672 | { |
||
673 | $editSize = $this->getEditSize($edit, $prevEdits); |
||
674 | |||
675 | // Edit was not a revert, so treat size > 0 as content added. |
||
676 | if ($editSize > 0) { |
||
677 | $this->addedBytes += $editSize; |
||
678 | |||
679 | if ($edit->getUser()) { |
||
680 | $this->editors[$edit->getUser()->getUsername()]['added'] += $editSize; |
||
681 | } |
||
682 | |||
683 | // Keep track of edit with max addition. |
||
684 | if (!$this->maxAddition || $editSize > $this->maxAddition->getSize()) { |
||
685 | // Keep track of old maxAddition in case we find out the next $edit was reverted |
||
686 | // (and was also a max edit), in which case we'll want to use this one ($edit). |
||
687 | $prevEdits['maxAddition'] = $this->maxAddition; |
||
688 | |||
689 | $this->maxAddition = $edit; |
||
690 | } |
||
691 | } elseif ($editSize < 0 && (!$this->maxDeletion || $editSize < $this->maxDeletion->getSize())) { |
||
692 | // Keep track of old maxDeletion in case we find out the next edit was reverted |
||
693 | // (and was also a max deletion), in which case we'll want to use this one. |
||
694 | $prevEdits['maxDeletion'] = $this->maxDeletion; |
||
695 | |||
696 | $this->maxDeletion = $edit; |
||
697 | } |
||
698 | |||
699 | return $prevEdits; |
||
700 | } |
||
701 | |||
702 | /** |
||
703 | * Get the size of the given edit, based on the previous edit (if present). |
||
704 | * We also don't return the actual edit size if last revision had a length of null. |
||
705 | * This happens when the edit follows other edits that were revision-deleted. |
||
706 | * @see T148857 for more information. |
||
707 | * @todo Remove once T101631 is resolved. |
||
708 | * @param Edit $edit |
||
709 | * @param Edit[] $prevEdits With 'prev', 'prevSha', 'maxAddition' and 'maxDeletion'. |
||
710 | * @return int |
||
711 | */ |
||
712 | private function getEditSize(Edit $edit, array $prevEdits): int |
||
713 | { |
||
714 | if ($prevEdits['prev'] && null === $prevEdits['prev']->getLength()) { |
||
715 | return 0; |
||
716 | } else { |
||
717 | return $edit->getSize(); |
||
718 | } |
||
719 | } |
||
720 | |||
721 | /** |
||
722 | * Update counts of automated tool usage for the given edit. |
||
723 | * @param Edit $edit |
||
724 | */ |
||
725 | private function updateToolCounts(Edit $edit): void |
||
726 | { |
||
727 | $automatedTool = $edit->getTool($this->container); |
||
728 | |||
729 | if (false === $automatedTool) { |
||
730 | // Nothing to do. |
||
731 | return; |
||
732 | } |
||
733 | |||
734 | $editYear = $edit->getYear(); |
||
735 | $editMonth = $edit->getMonth(); |
||
736 | |||
737 | $this->automatedCount++; |
||
738 | $this->yearMonthCounts[$editYear]['automated']++; |
||
739 | $this->yearMonthCounts[$editYear]['months'][$editMonth]['automated']++; |
||
740 | |||
741 | if (!isset($this->tools[$automatedTool['name']])) { |
||
742 | $this->tools[$automatedTool['name']] = [ |
||
743 | 'count' => 1, |
||
744 | 'link' => $automatedTool['link'], |
||
745 | ]; |
||
746 | } else { |
||
747 | $this->tools[$automatedTool['name']]['count']++; |
||
748 | } |
||
749 | } |
||
750 | |||
751 | /** |
||
752 | * Update various counts for the year and month of the given edit. |
||
753 | * @param Edit $edit |
||
754 | */ |
||
755 | private function updateYearMonthCounts(Edit $edit): void |
||
756 | { |
||
757 | $editYear = $edit->getYear(); |
||
758 | $editMonth = $edit->getMonth(); |
||
759 | |||
760 | // Fill in the blank arrays for the year and 12 months if needed. |
||
761 | if (!isset($this->yearMonthCounts[$editYear])) { |
||
762 | $this->addYearMonthCountEntry($edit); |
||
763 | } |
||
764 | |||
765 | // Increment year and month counts for all edits |
||
766 | $this->yearMonthCounts[$editYear]['all']++; |
||
767 | $this->yearMonthCounts[$editYear]['months'][$editMonth]['all']++; |
||
768 | // This will ultimately be the size of the page by the end of the year |
||
769 | $this->yearMonthCounts[$editYear]['size'] = $edit->getLength(); |
||
770 | |||
771 | // Keep track of which month had the most edits |
||
772 | $editsThisMonth = $this->yearMonthCounts[$editYear]['months'][$editMonth]['all']; |
||
773 | if ($editsThisMonth > $this->maxEditsPerMonth) { |
||
774 | $this->maxEditsPerMonth = $editsThisMonth; |
||
775 | } |
||
776 | } |
||
777 | |||
778 | /** |
||
779 | * Add a new entry to $this->yearMonthCounts for the given year, |
||
780 | * with blank values for each month. This called during self::parseHistory(). |
||
781 | * @param Edit $edit |
||
782 | */ |
||
783 | private function addYearMonthCountEntry(Edit $edit): void |
||
784 | { |
||
785 | $this->yearLabels[] = $this->i18n->dateFormat($edit->getTimestamp(), 'yyyy'); |
||
786 | $editYear = $edit->getYear(); |
||
787 | |||
788 | // Beginning of the month at 00:00:00. |
||
789 | $firstEditTime = mktime(0, 0, 0, (int)$this->firstEdit->getMonth(), 1, (int)$this->firstEdit->getYear()); |
||
790 | |||
791 | $this->yearMonthCounts[$editYear] = [ |
||
792 | 'all' => 0, |
||
793 | 'minor' => 0, |
||
794 | 'anon' => 0, |
||
795 | 'automated' => 0, |
||
796 | 'size' => 0, // Keep track of the size by the end of the year. |
||
797 | 'events' => [], |
||
798 | 'months' => [], |
||
799 | ]; |
||
800 | |||
801 | for ($i = 1; $i <= 12; $i++) { |
||
802 | $timeObj = mktime(0, 0, 0, $i, 1, (int)$editYear); |
||
803 | |||
804 | // Don't show zeros for months before the first edit or after the current month. |
||
805 | if ($timeObj < $firstEditTime || $timeObj > $this->getLastDay()) { |
||
806 | continue; |
||
807 | } |
||
808 | |||
809 | $this->monthLabels[] = $this->i18n->dateFormat($timeObj, 'yyyy-MM'); |
||
810 | $this->yearMonthCounts[$editYear]['months'][sprintf('%02d', $i)] = [ |
||
811 | 'all' => 0, |
||
812 | 'minor' => 0, |
||
813 | 'anon' => 0, |
||
814 | 'automated' => 0, |
||
815 | ]; |
||
816 | } |
||
817 | } |
||
818 | |||
819 | /** |
||
820 | * Update the counts of anon and minor edits for year, month, and user of the given edit. |
||
821 | * @param Edit $edit |
||
822 | */ |
||
823 | private function updateAnonMinorCounts(Edit $edit): void |
||
824 | { |
||
825 | $editYear = $edit->getYear(); |
||
826 | $editMonth = $edit->getMonth(); |
||
827 | |||
828 | // If anonymous, increase counts |
||
829 | if ($edit->isAnon()) { |
||
830 | $this->anonCount++; |
||
831 | $this->yearMonthCounts[$editYear]['anon']++; |
||
832 | $this->yearMonthCounts[$editYear]['months'][$editMonth]['anon']++; |
||
833 | } |
||
834 | |||
835 | // If minor edit, increase counts |
||
836 | if ($edit->isMinor()) { |
||
837 | $this->minorCount++; |
||
838 | $this->yearMonthCounts[$editYear]['minor']++; |
||
839 | $this->yearMonthCounts[$editYear]['months'][$editMonth]['minor']++; |
||
840 | } |
||
841 | } |
||
842 | |||
843 | /** |
||
844 | * Update various counts for the user of the given edit. |
||
845 | * @param Edit $edit |
||
846 | */ |
||
847 | private function updateUserCounts(Edit $edit): void |
||
877 | } |
||
878 | } |
||
879 | |||
880 | /** |
||
881 | * Increment "edits per <time>" counts based on the given edit. |
||
882 | * @param Edit $edit |
||
883 | */ |
||
884 | private function updateCountHistory(Edit $edit): void |
||
885 | { |
||
886 | $editTimestamp = $edit->getTimestamp(); |
||
887 | |||
888 | if ($editTimestamp > new DateTime('-1 day')) { |
||
889 | $this->countHistory['day']++; |
||
890 | } |
||
891 | if ($editTimestamp > new DateTime('-1 week')) { |
||
892 | $this->countHistory['week']++; |
||
893 | } |
||
894 | if ($editTimestamp > new DateTime('-1 month')) { |
||
895 | $this->countHistory['month']++; |
||
896 | } |
||
897 | if ($editTimestamp > new DateTime('-1 year')) { |
||
898 | $this->countHistory['year']++; |
||
899 | } |
||
900 | } |
||
901 | |||
902 | /** |
||
903 | * Query for log events during each year of the article's history, and set the results in $this->yearMonthCounts. |
||
904 | */ |
||
905 | private function setLogsEvents(): void |
||
906 | { |
||
907 | $logData = $this->getRepository()->getLogEvents( |
||
908 | $this->page, |
||
909 | $this->start, |
||
910 | $this->end |
||
911 | ); |
||
912 | |||
913 | foreach ($logData as $event) { |
||
914 | $time = strtotime($event['timestamp']); |
||
915 | $year = date('Y', $time); |
||
916 | |||
917 | if (!isset($this->yearMonthCounts[$year])) { |
||
918 | break; |
||
919 | } |
||
920 | |||
921 | $yearEvents = $this->yearMonthCounts[$year]['events']; |
||
922 | |||
923 | // Convert log type value to i18n key. |
||
924 | switch ($event['log_type']) { |
||
925 | // count pending-changes protections along with normal protections. |
||
926 | case 'stable': |
||
927 | case 'protect': |
||
928 | $action = 'protections'; |
||
929 | break; |
||
930 | case 'delete': |
||
931 | $action = 'deletions'; |
||
932 | break; |
||
933 | case 'move': |
||
934 | $action = 'moves'; |
||
935 | break; |
||
936 | } |
||
937 | |||
938 | if (empty($yearEvents[$action])) { |
||
939 | $yearEvents[$action] = 1; |
||
940 | } else { |
||
941 | $yearEvents[$action]++; |
||
942 | } |
||
943 | |||
944 | $this->yearMonthCounts[$year]['events'] = $yearEvents; |
||
945 | } |
||
946 | } |
||
947 | |||
948 | /** |
||
949 | * Set statistics about the top 10 editors by added text and number of edits. |
||
950 | * This is ran *after* parseHistory() since we need the grand totals first. |
||
951 | * Various stats are also set for each editor in $this->editors to be used in the charts. |
||
952 | */ |
||
953 | private function doPostPrecessing(): void |
||
954 | { |
||
955 | $topTenCount = $counter = 0; |
||
956 | $topTenEditorsByEdits = []; |
||
957 | |||
958 | foreach ($this->editors as $editor => $info) { |
||
959 | // Count how many users are in the top 10% by number of edits, excluding bots. |
||
960 | if ($counter < 10 && !in_array($editor, array_keys($this->bots))) { |
||
961 | $topTenCount += $info['all']; |
||
962 | $counter++; |
||
963 | |||
964 | // To be used in the Top Ten charts. |
||
965 | $topTenEditorsByEdits[] = [ |
||
966 | 'label' => $editor, |
||
967 | 'value' => $info['all'], |
||
968 | ]; |
||
969 | } |
||
970 | |||
971 | // Compute the percentage of minor edits the user made. |
||
972 | $this->editors[$editor]['minorPercentage'] = $info['all'] |
||
973 | ? ($info['minor'] / $info['all']) * 100 |
||
974 | : 0; |
||
975 | |||
976 | if ($info['all'] > 1) { |
||
977 | // Number of seconds/days between first and last edit. |
||
978 | $secs = $info['last']->getTimestamp() - $info['first']->getTimestamp(); |
||
979 | $days = $secs / (60 * 60 * 24); |
||
980 | |||
981 | // Average time between edits (in days). |
||
982 | $this->editors[$editor]['atbe'] = round($days / $info['all'], 1); |
||
983 | } |
||
984 | } |
||
985 | |||
986 | // Loop through again and add percentages. |
||
987 | $this->topTenEditorsByEdits = array_map(function ($editor) use ($topTenCount) { |
||
988 | $editor['percentage'] = 100 * ($editor['value'] / $topTenCount); |
||
989 | return $editor; |
||
990 | }, $topTenEditorsByEdits); |
||
991 | |||
992 | $this->topTenEditorsByAdded = $this->getTopTenByAdded(); |
||
993 | |||
994 | $this->topTenCount = $topTenCount; |
||
995 | } |
||
996 | |||
997 | /** |
||
998 | * Get the top ten editors by added text. |
||
999 | * @return array With keys 'label', 'value' and 'percentage', ready to be used by the pieChart Twig helper. |
||
1000 | */ |
||
1001 | private function getTopTenByAdded(): array |
||
1002 | { |
||
1003 | // First sort editors array by the amount of text they added. |
||
1004 | $topTenEditorsByAdded = $this->editors; |
||
1005 | uasort($topTenEditorsByAdded, function ($a, $b) { |
||
1006 | if ($a['added'] === $b['added']) { |
||
1007 | return 0; |
||
1008 | } |
||
1009 | return $a['added'] > $b['added'] ? -1 : 1; |
||
1010 | }); |
||
1011 | |||
1012 | // Slice to the top 10. |
||
1013 | $topTenEditorsByAdded = array_keys(array_slice($topTenEditorsByAdded, 0, 10, true)); |
||
1014 | |||
1015 | // Get the sum of added text so that we can add in percentages. |
||
1016 | $topTenTotalAdded = array_sum(array_map(function ($editor) { |
||
1017 | return $this->editors[$editor]['added']; |
||
1018 | }, $topTenEditorsByAdded)); |
||
1019 | |||
1020 | // Then build a new array of top 10 editors by added text in the data structure needed for the chart. |
||
1021 | return array_map(function ($editor) use ($topTenTotalAdded) { |
||
1022 | $added = $this->editors[$editor]['added']; |
||
1023 | return [ |
||
1024 | 'label' => $editor, |
||
1025 | 'value' => $added, |
||
1026 | 'percentage' => 0 === $this->addedBytes |
||
1027 | ? 0 |
||
1028 | : 100 * ($added / $topTenTotalAdded), |
||
1029 | ]; |
||
1030 | }, $topTenEditorsByAdded); |
||
1031 | } |
||
1032 | |||
1033 | /** |
||
1034 | * Get the number of times the page has been viewed in the given timeframe. If the ArticleInfo instance has a |
||
1035 | * date range, it is used instead of the value of the $latest parameter. |
||
1036 | * @param int $latest Last N days. |
||
1037 | * @return int |
||
1038 | */ |
||
1039 | public function getPageviews(int $latest): int |
||
1047 | } |
||
1048 | } |
||
1049 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.