Passed
Push — master ( 48da0f...5f527e )
by MusikAnimal
04:29
created

EditCounterRepository   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 576
Duplicated Lines 2.78 %

Importance

Changes 0
Metric Value
dl 16
loc 576
rs 9.2
c 0
b 0
f 0
wmc 34

12 Methods

Rating   Name   Duplication   Size   Complexity  
B getLogCounts() 0 84 6
A countAutomatedEdits() 0 5 1
B getRevisions() 0 53 4
A globalEditCountsFromDatabases() 0 21 2
A getBlocksReceived() 15 15 1
B getEditSizeData() 0 36 2
B getTimeCard() 0 39 4
B getPairData() 0 81 3
B getNamespaceTotals() 0 35 2
B globalEditCountsFromCentralAuth() 0 41 4
A globalEditCounts() 0 20 3
B getMonthCounts() 0 32 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * This file contains only the EditCounterRepository class.
4
 */
5
6
namespace Xtools;
7
8
use DateInterval;
9
use DateTime;
10
use Mediawiki\Api\SimpleRequest;
11
use Xtools\AutoEditsRepository;
12
13
/**
14
 * An EditCounterRepository is responsible for retrieving edit count information from the
15
 * databases and API. It doesn't do any post-processing of that information.
16
 * @codeCoverageIgnore
17
 */
18
class EditCounterRepository extends Repository
19
{
20
21
    /**
22
     * Get data about revisions, pages, etc.
23
     * @param Project $project The project.
24
     * @param User $user The user.
25
     * @returns string[] With keys: 'deleted', 'live', 'total', 'first', 'last', '24h', '7d', '30d',
26
     * '365d', 'small', 'large', 'with_comments', and 'minor_edits', ...
27
     */
28
    public function getPairData(Project $project, User $user)
29
    {
30
        // Set up cache.
31
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_pairdata');
32
        if ($this->cache->hasItem($cacheKey)) {
33
            return $this->cache->getItem($cacheKey)->get();
34
        }
35
36
        // Prepare the queries and execute them.
37
        $archiveTable = $this->getTableName($project->getDatabaseName(), 'archive');
38
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
39
        $queries = "
40
41
            -- Revision counts.
42
            (SELECT 'deleted' AS `key`, COUNT(ar_id) AS val FROM $archiveTable
43
                WHERE ar_user = :userId
44
            ) UNION (
45
            SELECT 'live' AS `key`, COUNT(rev_id) AS val FROM $revisionTable
46
                WHERE rev_user = :userId
47
            ) UNION (
48
            SELECT 'day' AS `key`, COUNT(rev_id) AS val FROM $revisionTable
49
                WHERE rev_user = :userId AND rev_timestamp >= DATE_SUB(NOW(), INTERVAL 1 DAY)
50
            ) UNION (
51
            SELECT 'week' AS `key`, COUNT(rev_id) AS val FROM $revisionTable
52
                WHERE rev_user = :userId AND rev_timestamp >= DATE_SUB(NOW(), INTERVAL 1 WEEK)
53
            ) UNION (
54
            SELECT 'month' AS `key`, COUNT(rev_id) AS val FROM $revisionTable
55
                WHERE rev_user = :userId AND rev_timestamp >= DATE_SUB(NOW(), INTERVAL 1 MONTH)
56
            ) UNION (
57
            SELECT 'year' AS `key`, COUNT(rev_id) AS val FROM $revisionTable
58
                WHERE rev_user = :userId AND rev_timestamp >= DATE_SUB(NOW(), INTERVAL 1 YEAR)
59
            ) UNION (
60
            SELECT 'with_comments' AS `key`, COUNT(rev_id) AS val FROM $revisionTable
61
                WHERE rev_user = :userId AND rev_comment != ''
62
            ) UNION (
63
            SELECT 'minor' AS `key`, COUNT(rev_id) AS val FROM $revisionTable
64
                WHERE rev_user = :userId AND rev_minor_edit = 1
65
66
            -- Dates.
67
            ) UNION (
68
            SELECT 'first' AS `key`, rev_timestamp AS `val` FROM $revisionTable
69
                WHERE rev_user = :userId ORDER BY rev_timestamp ASC LIMIT 1
70
            ) UNION (
71
            SELECT 'last' AS `key`, rev_timestamp AS `date` FROM $revisionTable
72
                WHERE rev_user = :userId ORDER BY rev_timestamp DESC LIMIT 1
73
74
            -- Page counts.
75
            ) UNION (
76
            SELECT 'edited-live' AS `key`, COUNT(DISTINCT rev_page) AS `val`
77
                FROM $revisionTable
78
                WHERE rev_user = :userId
79
            ) UNION (
80
            SELECT 'edited-deleted' AS `key`, COUNT(DISTINCT ar_page_id) AS `val`
81
                FROM $archiveTable
82
                WHERE ar_user = :userId
83
            ) UNION (
84
            SELECT 'created-live' AS `key`, COUNT(DISTINCT rev_page) AS `val`
85
                FROM $revisionTable
86
                WHERE rev_user = :userId AND rev_parent_id = 0
87
            ) UNION (
88
            SELECT 'created-deleted' AS `key`, COUNT(DISTINCT ar_page_id) AS `val`
89
                FROM $archiveTable
90
                WHERE ar_user = :userId AND ar_parent_id = 0
91
            )
92
        ";
93
        $resultQuery = $this->getProjectsConnection()->prepare($queries);
94
        $userId = $user->getId($project);
95
        $resultQuery->bindParam("userId", $userId);
96
        $resultQuery->execute();
97
        $revisionCounts = [];
98
        while ($result = $resultQuery->fetch()) {
99
            $revisionCounts[$result['key']] = $result['val'];
100
        }
101
102
        // Cache for 10 minutes, and return.
103
        $cacheItem = $this->cache->getItem($cacheKey)
104
                ->set($revisionCounts)
105
                ->expiresAfter(new DateInterval('PT10M'));
106
        $this->cache->save($cacheItem);
107
108
        return $revisionCounts;
109
    }
110
111
    /**
112
     * Get log totals for a user.
113
     * @param Project $project The project.
114
     * @param User $user The user.
115
     * @return integer[] Keys are "<log>-<action>" strings, values are counts.
116
     */
117
    public function getLogCounts(Project $project, User $user)
118
    {
119
        // Set up cache.
120
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_logcounts');
121
        if ($this->cache->hasItem($cacheKey)) {
122
            return $this->cache->getItem($cacheKey)->get();
123
        }
124
        $this->stopwatch->start($cacheKey, 'XTools');
125
126
        // Query.
127
        $loggingTable = $this->getTableName($project->getDatabaseName(), 'logging');
128
        $sql = "
129
        (SELECT CONCAT(log_type, '-', log_action) AS source, COUNT(log_id) AS value
130
            FROM $loggingTable
131
            WHERE log_user = :userId
132
            GROUP BY log_type, log_action
133
        )";
134
        $resultQuery = $this->getProjectsConnection()->prepare($sql);
135
        $userId = $user->getId($project);
136
        $resultQuery->bindParam('userId', $userId);
137
        $resultQuery->execute();
138
        $results = $resultQuery->fetchAll();
139
        $logCounts = array_combine(
140
            array_map(function ($e) {
141
                return $e['source'];
142
            }, $results),
143
            array_map(function ($e) {
144
                return $e['value'];
145
            }, $results)
146
        );
147
148
        // Make sure there is some value for each of the wanted counts.
149
        $requiredCounts = [
150
            'thanks-thank',
151
            'review-approve',
152
            'newusers-create2',
153
            'newusers-byemail',
154
            'patrol-patrol',
155
            'block-block',
156
            'block-reblock',
157
            'block-unblock',
158
            'protect-protect',
159
            'protect-modify',
160
            'protect-unprotect',
161
            'rights-rights',
162
            'move-move',
163
            'delete-delete',
164
            'delete-revision',
165
            'delete-restore',
166
            'import-import',
167
            'import-interwiki',
168
            'import-upload',
169
            'upload-upload',
170
            'upload-overwrite',
171
        ];
172
        foreach ($requiredCounts as $req) {
173
            if (!isset($logCounts[$req])) {
174
                $logCounts[$req] = 0;
175
            }
176
        }
177
178
        // Add Commons upload count, if applicable.
179
        $logCounts['files_uploaded_commons'] = 0;
180
        if ($this->isLabs()) {
181
            $commons = ProjectRepository::getProject('commonswiki', $this->container);
182
            $userId = $user->getId($commons);
183
            if ($userId) {
184
                $sql = "SELECT COUNT(log_id) FROM commonswiki_p.logging_userindex
185
                    WHERE log_type = 'upload' AND log_action = 'upload' AND log_user = :userId";
186
                $resultQuery = $this->getProjectsConnection()->prepare($sql);
187
                $resultQuery->bindParam('userId', $userId);
188
                $resultQuery->execute();
189
                $logCounts['files_uploaded_commons'] = $resultQuery->fetchColumn();
190
            }
191
        }
192
193
        // Cache for 10 minutes, and return.
194
        $cacheItem = $this->cache->getItem($cacheKey)
195
            ->set($logCounts)
196
            ->expiresAfter(new DateInterval('PT10M'));
197
        $this->cache->save($cacheItem);
198
        $this->stopwatch->stop($cacheKey);
199
200
        return $logCounts;
201
    }
202
203
    /**
204
     * Get data for all blocks set on the given user.
205
     * @param Project $project
206
     * @param User $user
207
     * @return array
208
     */
209 View Code Duplication
    public function getBlocksReceived(Project $project, User $user)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
210
    {
211
        $loggingTable = $this->getTableName($project->getDatabaseName(), 'logging', 'logindex');
212
        $sql = "SELECT log_action, log_timestamp, log_params FROM $loggingTable
213
                WHERE log_type = 'block'
214
                AND log_action IN ('block', 'reblock', 'unblock')
215
                AND log_timestamp > 0
216
                AND log_title = :username
217
                AND log_namespace = 2
218
                ORDER BY log_timestamp ASC";
219
        $resultQuery = $this->getProjectsConnection()->prepare($sql);
220
        $username = str_replace(' ', '_', $user->getUsername());
221
        $resultQuery->bindParam('username', $username);
222
        $resultQuery->execute();
223
        return $resultQuery->fetchAll();
224
    }
225
226
    /**
227
     * Get a user's total edit count on all projects.
228
     * @see EditCounterRepository::globalEditCountsFromCentralAuth()
229
     * @see EditCounterRepository::globalEditCountsFromDatabases()
230
     * @param User $user The user.
231
     * @param Project $project The project to start from.
232
     * @return mixed[] Elements are arrays with 'project' (Project), and 'total' (int).
233
     */
234
    public function globalEditCounts(User $user, Project $project)
235
    {
236
        // Get the edit counts from CentralAuth or database.
237
        $editCounts = $this->globalEditCountsFromCentralAuth($user, $project);
238
        if ($editCounts === false) {
239
            $editCounts = $this->globalEditCountsFromDatabases($user, $project);
240
        }
241
242
        // Pre-populate all projects' metadata, to prevent each project call from fetching it.
243
        $project->getRepository()->getAll();
1 ignored issue
show
Bug introduced by
The method getAll() does not exist on Xtools\Repository. It seems like you code against a sub-type of Xtools\Repository such as Xtools\ProjectRepository. ( Ignorable by Annotation )

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

243
        $project->getRepository()->/** @scrutinizer ignore-call */ getAll();
Loading history...
244
245
        // Compile the output.
246
        $out = [];
247
        foreach ($editCounts as $editCount) {
248
            $out[] = [
249
                'project' => ProjectRepository::getProject($editCount['dbName'], $this->container),
250
                'total' => $editCount['total'],
251
            ];
252
        }
253
        return $out;
254
    }
255
256
    /**
257
     * Get a user's total edit count on one or more project.
258
     * Requires the CentralAuth extension to be installed on the project.
259
     *
260
     * @param User $user The user.
261
     * @param Project $project The project to start from.
262
     * @return mixed[] Elements are arrays with 'dbName' (string), and 'total' (int).
263
     */
264
    protected function globalEditCountsFromCentralAuth(User $user, Project $project)
265
    {
266
        // Set up cache and stopwatch.
267
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_globaleditcounts');
268
        if ($this->cache->hasItem($cacheKey)) {
269
            return $this->cache->getItem($cacheKey)->get();
270
        }
271
        $this->stopwatch->start($cacheKey, 'XTools');
272
273
        $this->log->debug(__METHOD__." Getting global edit counts from for ".$user->getUsername());
274
275
        // Load all projects, so it doesn't have to request metadata about each one as it goes.
276
        $project->getRepository()->getAll();
277
278
        $api = $this->getMediawikiApi($project);
279
        $params = [
280
            'meta' => 'globaluserinfo',
281
            'guiprop' => 'editcount|merged',
282
            'guiuser' => $user->getUsername(),
283
        ];
284
        $query = new SimpleRequest('query', $params);
285
        $result = $api->getRequest($query);
286
        if (!isset($result['query']['globaluserinfo']['merged'])) {
287
            return [];
288
        }
289
        $out = [];
290
        foreach ($result['query']['globaluserinfo']['merged'] as $result) {
291
            $out[] = [
292
                'dbName' => $result['wiki'],
293
                'total' => $result['editcount'],
294
            ];
295
        }
296
297
        // Cache for 10 minutes, and return.
298
        $cacheItem = $this->cache->getItem($cacheKey)
299
            ->set($out)
300
            ->expiresAfter(new DateInterval('PT10M'));
301
        $this->cache->save($cacheItem);
302
        $this->stopwatch->stop($cacheKey);
303
304
        return $out;
305
    }
306
307
    /**
308
     * Get total edit counts from all projects for this user.
309
     * @see EditCounterRepository::globalEditCountsFromCentralAuth()
310
     * @param User $user The user.
311
     * @param Project $project The project to start from.
312
     * @return mixed[] Elements are arrays with 'dbName' (string), and 'total' (int).
313
     */
314
    protected function globalEditCountsFromDatabases(User $user, Project $project)
315
    {
316
        $this->log->debug(__METHOD__." Getting global edit counts for ".$user->getUsername());
317
        $stopwatchName = 'globalRevisionCounts.'.$user->getUsername();
318
        $allProjects = $project->getRepository()->getAll();
319
        $topEditCounts = [];
320
        $username = $user->getUsername();
321
        foreach ($allProjects as $projectMeta) {
322
            $revisionTableName = $this->getTableName($projectMeta['dbName'], 'revision');
323
            $sql = "SELECT COUNT(rev_id) FROM $revisionTableName WHERE rev_user_text=:username";
324
            $stmt = $this->getProjectsConnection()->prepare($sql);
325
            $stmt->bindParam('username', $username);
326
            $stmt->execute();
327
            $total = (int)$stmt->fetchColumn();
328
            $topEditCounts[] = [
329
                'dbName' => $projectMeta['dbName'],
330
                'total' => $total,
331
            ];
332
            $this->stopwatch->lap($stopwatchName);
333
        }
334
        return $topEditCounts;
335
    }
336
337
    /**
338
     * Get the given user's total edit counts per namespace on the given project.
339
     * @param Project $project The project.
340
     * @param User $user The user.
341
     * @return integer[] Array keys are namespace IDs, values are the edit counts.
342
     */
343
    public function getNamespaceTotals(Project $project, User $user)
344
    {
345
        // Cache?
346
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_namespacetotals');
347
        $this->stopwatch->start($cacheKey, 'XTools');
348
        if ($this->cache->hasItem($cacheKey)) {
349
            return $this->cache->getItem($cacheKey)->get();
350
        }
351
352
        $userId = $user->getId($project);
353
354
        // Query.
355
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
356
        $pageTable = $this->getTableName($project->getDatabaseName(), 'page');
357
        $sql = "SELECT page_namespace, COUNT(rev_id) AS total
358
            FROM $pageTable p JOIN $revisionTable r ON (r.rev_page = p.page_id)
359
            WHERE r.rev_user = :id
360
            GROUP BY page_namespace";
361
        $resultQuery = $this->getProjectsConnection()->prepare($sql);
362
        $resultQuery->bindParam(":id", $userId);
363
        $resultQuery->execute();
364
        $results = $resultQuery->fetchAll();
365
        $namespaceTotals = array_combine(array_map(function ($e) {
366
            return $e['page_namespace'];
367
        }, $results), array_map(function ($e) {
368
            return $e['total'];
369
        }, $results));
370
371
        // Cache and return.
372
        $cacheItem = $this->cache->getItem($cacheKey);
373
        $cacheItem->set($namespaceTotals);
374
        $cacheItem->expiresAfter(new DateInterval('PT15M'));
375
        $this->cache->save($cacheItem);
376
        $this->stopwatch->stop($cacheKey);
377
        return $namespaceTotals;
378
    }
379
380
    /**
381
     * Get revisions by this user.
382
     * @param Project[] $projects The projects.
383
     * @param User $user The user.
384
     * @param int $lim The maximum number of revisions to fetch from each project.
385
     * @return array|mixed
386
     */
387
    public function getRevisions($projects, User $user, $lim = 40)
388
    {
389
        // Check cache.
390
        $cacheKey = $this->getCacheKey('ec_globalcontribs.'.$user->getCacheKey().'.'.$lim);
391
        $this->stopwatch->start($cacheKey, 'XTools');
392
        if ($this->cache->hasItem($cacheKey)) {
393
            return $this->cache->getItem($cacheKey)->get();
394
        }
395
396
        // Assemble queries.
397
        $username = $user->getUsername();
398
        $queries = [];
399
        foreach ($projects as $project) {
400
            $revisionTable = $project->getTableName('revision');
401
            $pageTable = $project->getTableName('page');
402
            $sql = "SELECT
403
                    '".$project->getDatabaseName()."' AS project_name,
404
                    revs.rev_id AS id,
405
                    revs.rev_timestamp AS timestamp,
406
                    UNIX_TIMESTAMP(revs.rev_timestamp) AS unix_timestamp,
407
                    revs.rev_minor_edit AS minor,
408
                    revs.rev_deleted AS deleted,
409
                    revs.rev_len AS length,
410
                    (CAST(revs.rev_len AS SIGNED) - IFNULL(parentrevs.rev_len, 0)) AS length_change,
411
                    revs.rev_parent_id AS parent_id,
412
                    revs.rev_comment AS comment,
413
                    revs.rev_user_text AS username,
414
                    page.page_title,
415
                    page.page_namespace
416
                FROM $revisionTable AS revs
417
                    JOIN $pageTable AS page ON (rev_page = page_id)
418
                    LEFT JOIN $revisionTable AS parentrevs ON (revs.rev_parent_id = parentrevs.rev_id)
419
                WHERE revs.rev_user_text = :username
420
                ORDER BY revs.rev_timestamp DESC";
421
            if (is_numeric($lim)) {
422
                $sql .= " LIMIT $lim";
423
            }
424
            $queries[] = $sql;
425
        }
426
        $sql = "(\n" . join("\n) UNION (\n", $queries) . ")\n";
427
        $resultQuery = $this->getProjectsConnection()->prepare($sql);
428
        $resultQuery->bindParam(":username", $username);
429
        $resultQuery->execute();
430
        $revisions = $resultQuery->fetchAll();
431
432
        // Cache this.
433
        $cacheItem = $this->cache->getItem($cacheKey);
434
        $cacheItem->set($revisions);
435
        $cacheItem->expiresAfter(new DateInterval('PT15M'));
436
        $this->cache->save($cacheItem);
437
438
        $this->stopwatch->stop($cacheKey);
439
        return $revisions;
440
    }
441
442
    /**
443
     * Get data for a bar chart of monthly edit totals per namespace.
444
     * @param Project $project The project.
445
     * @param User $user The user.
446
     * @return string[] [
447
     *                      [
448
     *                          'year' => <year>,
449
     *                          'month' => <month>,
450
     *                          'page_namespace' => <namespace>,
451
     *                          'count' => <count>,
452
     *                      ],
453
     *                      ...
454
     *                  ]
455
     */
456
    public function getMonthCounts(Project $project, User $user)
457
    {
458
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_monthcounts');
459
        $this->stopwatch->start($cacheKey, 'XTools');
460
        if ($this->cache->hasItem($cacheKey)) {
461
            return $this->cache->getItem($cacheKey)->get();
462
        }
463
464
        $username = $user->getUsername();
465
        $revisionTable = $project->getTableName('revision');
466
        $pageTable = $project->getTableName('page');
467
        $sql =
468
            "SELECT "
469
            . "     YEAR(rev_timestamp) AS `year`,"
470
            . "     MONTH(rev_timestamp) AS `month`,"
471
            . "     page_namespace,"
472
            . "     COUNT(rev_id) AS `count` "
473
            .  " FROM $revisionTable JOIN $pageTable ON (rev_page = page_id)"
474
            . " WHERE rev_user_text = :username"
475
            . " GROUP BY YEAR(rev_timestamp), MONTH(rev_timestamp), page_namespace";
476
        $resultQuery = $this->getProjectsConnection()->prepare($sql);
477
        $resultQuery->bindParam(":username", $username);
478
        $resultQuery->execute();
479
        $totals = $resultQuery->fetchAll();
480
481
        $cacheItem = $this->cache->getItem($cacheKey);
482
        $cacheItem->expiresAfter(new DateInterval('PT10M'));
483
        $cacheItem->set($totals);
484
        $this->cache->save($cacheItem);
485
486
        $this->stopwatch->stop($cacheKey);
487
        return $totals;
488
    }
489
490
    /**
491
     * Get data for the timecard chart, with totals grouped by day and to the nearest two-hours.
492
     * @param Project $project
493
     * @param User $user
494
     * @return string[]
495
     */
496
    public function getTimeCard(Project $project, User $user)
497
    {
498
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_timecard');
499
        $this->stopwatch->start($cacheKey, 'XTools');
500
        if ($this->cache->hasItem($cacheKey)) {
501
            return $this->cache->getItem($cacheKey)->get();
502
        }
503
504
        $username = $user->getUsername();
505
        $hourInterval = 2;
506
        $xCalc = "ROUND(HOUR(rev_timestamp)/$hourInterval) * $hourInterval";
507
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
508
        $sql = "SELECT "
509
            . "     DAYOFWEEK(rev_timestamp) AS `y`, "
510
            . "     $xCalc AS `x`, "
511
            . "     COUNT(rev_id) AS `value` "
512
            . " FROM $revisionTable"
513
            . " WHERE rev_user_text = :username"
514
            . " GROUP BY DAYOFWEEK(rev_timestamp), $xCalc ";
515
        $resultQuery = $this->getProjectsConnection()->prepare($sql);
516
        $resultQuery->bindParam(":username", $username);
517
        $resultQuery->execute();
518
        $totals = $resultQuery->fetchAll();
519
        // Scale the radii: get the max, then scale each radius.
520
        // This looks inefficient, but there's a max of 72 elements in this array.
521
        $max = 0;
522
        foreach ($totals as $total) {
523
            $max = max($max, $total['value']);
524
        }
525
        foreach ($totals as &$total) {
526
            $total['value'] = round($total['value'] / $max * 100);
527
        }
528
        $cacheItem = $this->cache->getItem($cacheKey);
529
        $cacheItem->expiresAfter(new DateInterval('PT10M'));
530
        $cacheItem->set($totals);
531
        $this->cache->save($cacheItem);
532
533
        $this->stopwatch->stop($cacheKey);
534
        return $totals;
535
    }
536
537
    /**
538
     * Get various data about edit sizes of the past 5,000 edits.
539
     * Will cache the result for 10 minutes.
540
     * @param Project $project The project.
541
     * @param User $user The user.
542
     * @return string[] Values with for keys 'average_size',
543
     *                  'small_edits' and 'large_edits'
544
     */
545
    public function getEditSizeData(Project $project, User $user)
546
    {
547
        // Set up cache.
548
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_editsizes');
549
        $this->stopwatch->start($cacheKey, 'XTools');
550
        if ($this->cache->hasItem($cacheKey)) {
551
            return $this->cache->getItem($cacheKey)->get();
552
        }
553
554
        // Prepare the queries and execute them.
555
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
556
        $userId = $user->getId($project);
557
        $sql = "SELECT AVG(sizes.size) AS average_size,
558
                COUNT(CASE WHEN sizes.size < 20 THEN 1 END) AS small_edits,
559
                COUNT(CASE WHEN sizes.size > 1000 THEN 1 END) AS large_edits
560
                FROM (
561
                    SELECT (CAST(revs.rev_len AS SIGNED) - IFNULL(parentrevs.rev_len, 0)) AS size
562
                    FROM $revisionTable AS revs
563
                    LEFT JOIN $revisionTable AS parentrevs ON (revs.rev_parent_id = parentrevs.rev_id)
564
                    WHERE revs.rev_user = :userId
565
                    ORDER BY revs.rev_timestamp DESC
566
                    LIMIT 5000
567
                ) sizes";
568
        $resultQuery = $this->getProjectsConnection()->prepare($sql);
569
        $resultQuery->bindParam('userId', $userId);
570
        $resultQuery->execute();
571
        $results = $resultQuery->fetchAll()[0];
572
573
        // Cache for 10 minutes.
574
        $cacheItem = $this->cache->getItem($cacheKey);
575
        $cacheItem->set($results);
576
        $cacheItem->expiresAfter(new DateInterval('PT10M'));
577
        $this->cache->save($cacheItem);
578
579
        $this->stopwatch->stop($cacheKey);
580
        return $results;
581
    }
582
583
    /**
584
     * Get the number of edits this user made using semi-automated tools.
585
     * @param Project $project
586
     * @param User $user
587
     * @return int Result of query, see below.
588
     */
589
    public function countAutomatedEdits(Project $project, User $user)
590
    {
591
        $autoEditsRepo = new AutoEditsRepository();
592
        $autoEditsRepo->setContainer($this->container);
593
        return $autoEditsRepo->countAutomatedEdits($project, $user);
594
    }
595
}
596