Passed
Push — master ( a1ed3f...a64a0e )
by
unknown
05:21
created

globalEditCountsFromCentralAuth()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 5
nop 2
dl 0
loc 40
rs 8.9688
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file contains only the EditCounterRepository class.
4
 */
5
6
namespace Xtools;
7
8
use Mediawiki\Api\SimpleRequest;
9
10
/**
11
 * An EditCounterRepository is responsible for retrieving edit count information from the
12
 * databases and API. It doesn't do any post-processing of that information.
13
 * @codeCoverageIgnore
14
 */
15
class EditCounterRepository extends UserRightsRepository
16
{
17
    /**
18
     * Get data about revisions, pages, etc.
19
     * @param Project $project The project.
20
     * @param User $user The user.
21
     * @return string[] With keys: 'deleted', 'live', 'total', '24h', '7d', '30d',
22
     * '365d', 'small', 'large', 'with_comments', and 'minor_edits', ...
23
     */
24
    public function getPairData(Project $project, User $user)
25
    {
26
        // Set up cache.
27
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_pairdata');
28
        if ($this->cache->hasItem($cacheKey)) {
29
            return $this->cache->getItem($cacheKey)->get();
30
        }
31
32
        // Prepare the queries and execute them.
33
        $archiveTable = $this->getTableName($project->getDatabaseName(), 'archive');
34
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
35
36
        // For IPs we use rev_user_text, and for accounts rev_user which is slightly faster.
37
        $revUserClause = $user->isAnon() ? 'rev_user_text = :username' : 'rev_user = :userId';
38
        $arUserClause = $user->isAnon() ? 'ar_user_text = :username' : 'ar_user = :userId';
39
40
        $sql = "
41
            -- Revision counts.
42
            (SELECT 'deleted' AS `key`, COUNT(ar_id) AS val FROM $archiveTable
43
                WHERE $arUserClause
44
            ) UNION (
45
            SELECT 'live' AS `key`, COUNT(rev_id) AS val FROM $revisionTable
46
                WHERE $revUserClause
47
            ) UNION (
48
            SELECT 'day' AS `key`, COUNT(rev_id) AS val FROM $revisionTable
49
                WHERE $revUserClause 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 $revUserClause 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 $revUserClause 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 $revUserClause 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 $revUserClause AND rev_comment != ''
62
            ) UNION (
63
            SELECT 'minor' AS `key`, COUNT(rev_id) AS val FROM $revisionTable
64
                WHERE $revUserClause AND rev_minor_edit = 1
65
66
            -- Page counts.
67
            ) UNION (
68
            SELECT 'edited-live' AS `key`, COUNT(DISTINCT rev_page) AS `val`
69
                FROM $revisionTable
70
                WHERE $revUserClause
71
            ) UNION (
72
            SELECT 'edited-deleted' AS `key`, COUNT(DISTINCT ar_page_id) AS `val`
73
                FROM $archiveTable
74
                WHERE $arUserClause
75
            ) UNION (
76
            SELECT 'created-live' AS `key`, COUNT(DISTINCT rev_page) AS `val`
77
                FROM $revisionTable
78
                WHERE $revUserClause AND rev_parent_id = 0
79
            ) UNION (
80
            SELECT 'created-deleted' AS `key`, COUNT(DISTINCT ar_page_id) AS `val`
81
                FROM $archiveTable
82
                WHERE $arUserClause AND ar_parent_id = 0
83
            )
84
        ";
85
86
        $params = $user->isAnon() ? ['username' => $user->getUsername()] : ['userId' => $user->getId($project)];
87
        $resultQuery = $this->executeProjectsQuery($sql, $params);
88
89
        $revisionCounts = [];
90
        while ($result = $resultQuery->fetch()) {
91
            $revisionCounts[$result['key']] = $result['val'];
92
        }
93
94
        // Cache and return.
95
        return $this->setCache($cacheKey, $revisionCounts);
96
    }
97
98
    /**
99
     * Get log totals for a user.
100
     * @param Project $project The project.
101
     * @param User $user The user.
102
     * @return integer[] Keys are "<log>-<action>" strings, values are counts.
103
     */
104
    public function getLogCounts(Project $project, User $user)
105
    {
106
        // Set up cache.
107
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_logcounts');
108
        if ($this->cache->hasItem($cacheKey)) {
109
            return $this->cache->getItem($cacheKey)->get();
110
        }
111
        $this->stopwatch->start($cacheKey, 'XTools');
112
113
        // Query.
114
        $loggingTable = $this->getTableName($project->getDatabaseName(), 'logging');
115
        $sql = "
116
        (SELECT CONCAT(log_type, '-', log_action) AS source, COUNT(log_id) AS value
117
            FROM $loggingTable
118
            WHERE log_user_text = :username
119
            GROUP BY log_type, log_action
120
        )";
121
122
        $results = $this->executeProjectsQuery($sql, [
123
            'username' => $user->getUsername(),
124
        ])->fetchAll();
125
126
        $logCounts = array_combine(
127
            array_map(function ($e) {
128
                return $e['source'];
129
            }, $results),
130
            array_map(function ($e) {
131
                return $e['value'];
132
            }, $results)
133
        );
134
135
        // Make sure there is some value for each of the wanted counts.
136
        $requiredCounts = [
137
            'thanks-thank',
138
            'review-approve',
139
            'newusers-create2',
140
            'newusers-byemail',
141
            'patrol-patrol',
142
            'block-block',
143
            'block-reblock',
144
            'block-unblock',
145
            'protect-protect',
146
            'protect-modify',
147
            'protect-unprotect',
148
            'rights-rights',
149
            'move-move',
150
            'delete-delete',
151
            'delete-revision',
152
            'delete-restore',
153
            'import-import',
154
            'import-interwiki',
155
            'import-upload',
156
            'upload-upload',
157
            'upload-overwrite',
158
        ];
159
        foreach ($requiredCounts as $req) {
160
            if (!isset($logCounts[$req])) {
161
                $logCounts[$req] = 0;
162
            }
163
        }
164
165
        // Add Commons upload count, if applicable.
166
        $logCounts['files_uploaded_commons'] = 0;
167
        if ($this->isLabs()) {
168
            $commons = ProjectRepository::getProject('commonswiki', $this->container);
169
            $userId = $user->getId($commons);
170
            if ($userId) {
171
                $sql = "SELECT COUNT(log_id) FROM commonswiki_p.logging_userindex
172
                    WHERE log_type = 'upload' AND log_action = 'upload' AND log_user = :userId";
173
                $resultQuery = $this->executeProjectsQuery($sql, ['userId' => $userId]);
174
                $logCounts['files_uploaded_commons'] = $resultQuery->fetchColumn();
175
            }
176
        }
177
178
        // Cache and return.
179
        $this->stopwatch->stop($cacheKey);
180
        return $this->setCache($cacheKey, $logCounts);
181
    }
182
183
    /**
184
     * Get the IDs and timestamps of the latest edit and logged action by the given user.
185
     * @param Project $project
186
     * @param User $user
187
     * @return string[] With keys 'rev_first', 'rev_latest', 'log_latest'.
188
     */
189
    public function getFirstAndLatestActions(Project $project, User $user)
190
    {
191
        $loggingTable = $project->getTableName('logging', 'userindex');
192
        $revisionTable = $project->getTableName('revision');
193
194
        $sql = "(
195
                    SELECT 'rev_first' AS `key`, rev_id AS `id`,
196
                        rev_timestamp AS `timestamp`, NULL as `type`
197
                    FROM $revisionTable
198
                    WHERE rev_user_text = :username
199
                    ORDER BY rev_timestamp ASC LIMIT 1
200
                ) UNION (
201
                    SELECT 'rev_latest' AS `key`, rev_id AS `id`,
202
                        rev_timestamp AS `timestamp`, NULL as `type`
203
                    FROM $revisionTable
204
                    WHERE rev_user_text = :username
205
                    ORDER BY rev_timestamp DESC LIMIT 1
206
                ) UNION (
207
                    SELECT 'log_latest' AS `key`, log_id AS `id`,
208
                        log_timestamp AS `timestamp`, log_type AS `type`
209
                    FROM $loggingTable
210
                    WHERE log_user_text = :username
211
                    ORDER BY log_timestamp DESC LIMIT 1
212
                )";
213
214
        $username = $user->getUsername();
215
        $resultQuery = $this->executeProjectsQuery($sql, [
216
            'username' => $username,
217
        ]);
218
219
        $actions = [];
220
        while ($result = $resultQuery->fetch()) {
221
            $actions[$result['key']] = [
222
                'id' => $result['id'],
223
                'timestamp' => $result['timestamp'],
224
                'type' => $result['type'],
225
            ];
226
        }
227
228
        return $actions;
229
    }
230
231
    /**
232
     * Get data for all blocks set on the given user.
233
     * @param Project $project
234
     * @param User $user
235
     * @return array
236
     */
237
    public function getBlocksReceived(Project $project, User $user)
238
    {
239
        $loggingTable = $this->getTableName($project->getDatabaseName(), 'logging', 'logindex');
240
        $sql = "SELECT log_action, log_timestamp, log_params FROM $loggingTable
241
                WHERE log_type = 'block'
242
                AND log_action IN ('block', 'reblock', 'unblock')
243
                AND log_timestamp > 0
244
                AND log_title = :username
245
                AND log_namespace = 2
246
                ORDER BY log_timestamp ASC";
247
        $username = str_replace(' ', '_', $user->getUsername());
248
249
        return $this->executeProjectsQuery($sql, [
250
            'username' => $username
251
        ])->fetchAll();
252
    }
253
254
    /**
255
     * Get a user's total edit count on all projects.
256
     * @see EditCounterRepository::globalEditCountsFromCentralAuth()
257
     * @see EditCounterRepository::globalEditCountsFromDatabases()
258
     * @param User $user The user.
259
     * @param Project $project The project to start from.
260
     * @return mixed[] Elements are arrays with 'project' (Project), and 'total' (int).
261
     */
262
    public function globalEditCounts(User $user, Project $project)
263
    {
264
        // Get the edit counts from CentralAuth or database.
265
        $editCounts = $this->globalEditCountsFromCentralAuth($user, $project);
266
        if ($editCounts === false) {
0 ignored issues
show
introduced by
The condition $editCounts === false is always false.
Loading history...
267
            $editCounts = $this->globalEditCountsFromDatabases($user, $project);
268
        }
269
270
        // Pre-populate all projects' metadata, to prevent each project call from fetching it.
271
        $project->getRepository()->getAll();
272
273
        // Compile the output.
274
        $out = [];
275
        foreach ($editCounts as $editCount) {
276
            $out[] = [
277
                'project' => ProjectRepository::getProject($editCount['dbName'], $this->container),
278
                'total' => $editCount['total'],
279
            ];
280
        }
281
        return $out;
282
    }
283
284
    /**
285
     * Get a user's total edit count on one or more project.
286
     * Requires the CentralAuth extension to be installed on the project.
287
     *
288
     * @param User $user The user.
289
     * @param Project $project The project to start from.
290
     * @return mixed[] Elements are arrays with 'dbName' (string), and 'total' (int).
291
     */
292
    protected function globalEditCountsFromCentralAuth(User $user, Project $project)
293
    {
294
        if ($user->isAnon() === true) {
295
            return false;
296
        }
297
298
        // Set up cache and stopwatch.
299
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_globaleditcounts');
300
        if ($this->cache->hasItem($cacheKey)) {
301
            return $this->cache->getItem($cacheKey)->get();
302
        }
303
        $this->stopwatch->start($cacheKey, 'XTools');
304
305
        $this->log->debug(__METHOD__." Getting global edit counts from for ".$user->getUsername());
306
307
        // Load all projects, so it doesn't have to request metadata about each one as it goes.
308
        $project->getRepository()->getAll();
309
310
        $api = $this->getMediawikiApi($project);
311
        $params = [
312
            'meta' => 'globaluserinfo',
313
            'guiprop' => 'editcount|merged',
314
            'guiuser' => $user->getUsername(),
315
        ];
316
        $query = new SimpleRequest('query', $params);
317
        $result = $api->getRequest($query);
318
        if (!isset($result['query']['globaluserinfo']['merged'])) {
319
            return [];
320
        }
321
        $out = [];
322
        foreach ($result['query']['globaluserinfo']['merged'] as $result) {
323
            $out[] = [
324
                'dbName' => $result['wiki'],
325
                'total' => $result['editcount'],
326
            ];
327
        }
328
329
        // Cache and return.
330
        $this->stopwatch->stop($cacheKey);
331
        return $this->setCache($cacheKey, $out);
332
    }
333
334
    /**
335
     * Get total edit counts from all projects for this user.
336
     * @see EditCounterRepository::globalEditCountsFromCentralAuth()
337
     * @param User $user The user.
338
     * @param Project $project The project to start from.
339
     * @return mixed[] Elements are arrays with 'dbName' (string), and 'total' (int).
340
     */
341
    protected function globalEditCountsFromDatabases(User $user, Project $project)
342
    {
343
        $this->log->debug(__METHOD__." Getting global edit counts for ".$user->getUsername());
344
        $allProjects = $project->getRepository()->getAll();
345
        $topEditCounts = [];
346
        foreach ($allProjects as $projectMeta) {
347
            $revisionTableName = $this->getTableName($projectMeta['dbName'], 'revision');
348
            $sql = "SELECT COUNT(rev_id) FROM $revisionTableName WHERE rev_user_text = :username";
349
350
            $resultQuery = $this->executeProjectsQuery($sql, [
351
                'username' => $user->getUsername()
352
            ]);
353
            $total = (int)$resultQuery->fetchColumn();
354
            $topEditCounts[] = [
355
                'dbName' => $projectMeta['dbName'],
356
                'total' => $total,
357
            ];
358
        }
359
        return $topEditCounts;
360
    }
361
362
    /**
363
     * Get the given user's total edit counts per namespace on the given project.
364
     * @param Project $project The project.
365
     * @param User $user The user.
366
     * @return integer[] Array keys are namespace IDs, values are the edit counts.
367
     */
368
    public function getNamespaceTotals(Project $project, User $user)
369
    {
370
        // Cache?
371
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_namespacetotals');
372
        $this->stopwatch->start($cacheKey, 'XTools');
373
        if ($this->cache->hasItem($cacheKey)) {
374
            return $this->cache->getItem($cacheKey)->get();
375
        }
376
377
        // Query.
378
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
379
        $revUserClause = $user->isAnon() ? 'r.rev_user_text = :username' : 'r.rev_user = :userId';
380
        $pageTable = $this->getTableName($project->getDatabaseName(), 'page');
381
        $sql = "SELECT page_namespace, COUNT(rev_id) AS total
382
            FROM $pageTable p JOIN $revisionTable r ON (r.rev_page = p.page_id)
383
            WHERE $revUserClause
384
            GROUP BY page_namespace";
385
386
        $params = $user->isAnon() ? ['username' => $user->getUsername()] : ['userId' => $user->getId($project)];
387
        $results = $this->executeProjectsQuery($sql, $params)->fetchAll();
388
389
        $namespaceTotals = array_combine(array_map(function ($e) {
390
            return $e['page_namespace'];
391
        }, $results), array_map(function ($e) {
392
            return $e['total'];
393
        }, $results));
394
395
        // Cache and return.
396
        $this->stopwatch->stop($cacheKey);
397
        return $this->setCache($cacheKey, $namespaceTotals);
398
    }
399
400
    /**
401
     * Get revisions by this user.
402
     * @param Project[] $projects The projects.
403
     * @param User $user The user.
404
     * @param int $limit The maximum number of revisions to fetch from each project.
405
     * @param int $offset Offset results by this number of rows.
406
     * @return array|mixed
407
     */
408
    public function getRevisions(array $projects, User $user, $limit = 30, $offset = 0)
409
    {
410
        // Check cache.
411
        $cacheKey = $this->getCacheKey('ec_globalcontribs.'.$user->getCacheKey().'.'.$limit.'.'.$offset);
412
        $this->stopwatch->start($cacheKey, 'XTools');
413
        if ($this->cache->hasItem($cacheKey)) {
414
            return $this->cache->getItem($cacheKey)->get();
415
        }
416
417
        // Assemble queries.
418
        $queries = [];
419
        foreach ($projects as $project) {
420
            $revisionTable = $project->getTableName('revision');
421
            $pageTable = $project->getTableName('page');
422
            $sql = "SELECT
423
                    '".$project->getDatabaseName()."' AS project_name,
424
                    revs.rev_id AS id,
425
                    revs.rev_timestamp AS timestamp,
426
                    UNIX_TIMESTAMP(revs.rev_timestamp) AS unix_timestamp,
427
                    revs.rev_minor_edit AS minor,
428
                    revs.rev_deleted AS deleted,
429
                    revs.rev_len AS length,
430
                    (CAST(revs.rev_len AS SIGNED) - IFNULL(parentrevs.rev_len, 0)) AS length_change,
431
                    revs.rev_parent_id AS parent_id,
432
                    revs.rev_comment AS comment,
433
                    revs.rev_user_text AS username,
434
                    page.page_title,
435
                    page.page_namespace
436
                FROM $revisionTable AS revs
437
                    JOIN $pageTable AS page ON (rev_page = page_id)
438
                    LEFT JOIN $revisionTable AS parentrevs ON (revs.rev_parent_id = parentrevs.rev_id)
439
                WHERE revs.rev_user_text = :username
440
                ORDER BY revs.rev_timestamp DESC";
441
            $queries[] = $sql;
442
        }
443
        $sql = "SELECT * FROM ((\n" . join("\n) UNION (\n", $queries) . ")) a ORDER BY timestamp DESC LIMIT $limit";
444
445
        if (is_numeric($offset)) {
0 ignored issues
show
introduced by
The condition is_numeric($offset) is always true.
Loading history...
446
            $sql .= " OFFSET $offset";
447
        }
448
449
        $revisions = $this->executeProjectsQuery($sql, [
450
            'username' => $user->getUsername(),
451
        ])->fetchAll();
452
453
        // Cache and return.
454
        $this->stopwatch->stop($cacheKey);
455
        return $this->setCache($cacheKey, $revisions);
456
    }
457
458
    /**
459
     * Get data for a bar chart of monthly edit totals per namespace.
460
     * @param Project $project The project.
461
     * @param User $user The user.
462
     * @return string[] [
463
     *                      [
464
     *                          'year' => <year>,
465
     *                          'month' => <month>,
466
     *                          'page_namespace' => <namespace>,
467
     *                          'count' => <count>,
468
     *                      ],
469
     *                      ...
470
     *                  ]
471
     */
472
    public function getMonthCounts(Project $project, User $user)
473
    {
474
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_monthcounts');
475
        $this->stopwatch->start($cacheKey, 'XTools');
476
        if ($this->cache->hasItem($cacheKey)) {
477
            return $this->cache->getItem($cacheKey)->get();
478
        }
479
480
        $revisionTable = $project->getTableName('revision');
481
        $pageTable = $project->getTableName('page');
482
        $sql =
483
            "SELECT "
484
            . "     YEAR(rev_timestamp) AS `year`,"
485
            . "     MONTH(rev_timestamp) AS `month`,"
486
            . "     page_namespace,"
487
            . "     COUNT(rev_id) AS `count` "
488
            .  " FROM $revisionTable JOIN $pageTable ON (rev_page = page_id)"
489
            . " WHERE rev_user_text = :username"
490
            . " GROUP BY YEAR(rev_timestamp), MONTH(rev_timestamp), page_namespace";
491
492
        $totals = $this->executeProjectsQuery($sql, [
493
            'username' => $user->getUsername(),
494
        ])->fetchAll();
495
496
        // Cache and return.
497
        $this->stopwatch->stop($cacheKey);
498
        return $this->setCache($cacheKey, $totals);
499
    }
500
501
    /**
502
     * Get data for the timecard chart, with totals grouped by day and to the nearest two-hours.
503
     * @param Project $project
504
     * @param User $user
505
     * @return string[]
506
     */
507
    public function getTimeCard(Project $project, User $user)
508
    {
509
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_timecard');
510
        $this->stopwatch->start($cacheKey, 'XTools');
511
        if ($this->cache->hasItem($cacheKey)) {
512
            return $this->cache->getItem($cacheKey)->get();
513
        }
514
515
        $hourInterval = 2;
516
        $xCalc = "ROUND(HOUR(rev_timestamp)/$hourInterval) * $hourInterval";
517
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
518
        $sql = "SELECT "
519
            . "     DAYOFWEEK(rev_timestamp) AS `y`, "
520
            . "     $xCalc AS `x`, "
521
            . "     COUNT(rev_id) AS `value` "
522
            . " FROM $revisionTable"
523
            . " WHERE rev_user_text = :username"
524
            . " GROUP BY DAYOFWEEK(rev_timestamp), $xCalc ";
525
526
        $totals = $this->executeProjectsQuery($sql, [
527
            'username' => $user->getUsername(),
528
        ])->fetchAll();
529
530
        // Cache and return.
531
        $this->stopwatch->stop($cacheKey);
532
        return $this->setCache($cacheKey, $totals);
533
    }
534
535
    /**
536
     * Get various data about edit sizes of the past 5,000 edits.
537
     * Will cache the result for 10 minutes.
538
     * @param Project $project The project.
539
     * @param User $user The user.
540
     * @return string[] Values with for keys 'average_size',
541
     *                  'small_edits' and 'large_edits'
542
     */
543
    public function getEditSizeData(Project $project, User $user)
544
    {
545
        // Set up cache.
546
        $cacheKey = $this->getCacheKey(func_get_args(), 'ec_editsizes');
547
        $this->stopwatch->start($cacheKey, 'XTools');
548
        if ($this->cache->hasItem($cacheKey)) {
549
            return $this->cache->getItem($cacheKey)->get();
550
        }
551
552
        // Prepare the queries and execute them.
553
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
554
        $revUserClause = $user->isAnon() ? 'revs.rev_user_text = :username' : 'revs.rev_user = :userId';
555
        $sql = "SELECT AVG(sizes.size) AS average_size,
556
                COUNT(CASE WHEN sizes.size < 20 THEN 1 END) AS small_edits,
557
                COUNT(CASE WHEN sizes.size > 1000 THEN 1 END) AS large_edits
558
                FROM (
559
                    SELECT (CAST(revs.rev_len AS SIGNED) - IFNULL(parentrevs.rev_len, 0)) AS size
560
                    FROM $revisionTable AS revs
561
                    LEFT JOIN $revisionTable AS parentrevs ON (revs.rev_parent_id = parentrevs.rev_id)
562
                    WHERE $revUserClause
563
                    ORDER BY revs.rev_timestamp DESC
564
                    LIMIT 5000
565
                ) sizes";
566
567
        $params = $user->isAnon() ? ['username' => $user->getUsername()] : ['userId' => $user->getId($project)];
568
        $results = $this->executeProjectsQuery($sql, $params)->fetch();
569
570
        // Cache and return.
571
        $this->stopwatch->stop($cacheKey);
572
        return $this->setCache($cacheKey, $results);
573
    }
574
575
    /**
576
     * Get the number of edits this user made using semi-automated tools.
577
     * @param Project $project
578
     * @param User $user
579
     * @return int Result of query, see below.
580
     */
581
    public function countAutomatedEdits(Project $project, User $user)
582
    {
583
        $autoEditsRepo = new AutoEditsRepository();
584
        $autoEditsRepo->setContainer($this->container);
585
        return $autoEditsRepo->countAutomatedEdits($project, $user);
586
    }
587
}
588