Passed
Push — master ( c121a7...5fa1a0 )
by MusikAnimal
05:15
created

TopEditsRepository::countEditsNamespace()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 13
nc 2
nop 3
dl 0
loc 23
rs 9.8333
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file contains only the TopEditsRepository class.
4
 */
5
6
namespace Xtools;
7
8
use Symfony\Component\DependencyInjection\Container;
9
10
/**
11
 * TopEditsRepository is responsible for retrieving data from the database
12
 * about the top-edited pages of a user. It doesn't do any post-processing
13
 * of that information.
14
 * @codeCoverageIgnore
15
 */
16
class TopEditsRepository extends Repository
17
{
18
    /**
19
     * Expose the container to the TopEdits class.
20
     * @return Container
21
     */
22
    public function getContainer()
23
    {
24
        return $this->container;
25
    }
26
27
    /**
28
     * Get the top edits by a user in a single namespace.
29
     * @param Project $project
30
     * @param User $user
31
     * @param int $namespace Namespace ID.
32
     * @param int $limit Number of edits to fetch.
33
     * @param int $offset Number of results past the initial dataset. Used for pagination.
34
     * @return string[] page_namespace, page_title, page_is_redirect,
35
     *   count (number of edits), assessment (page assessment).
36
     */
37
    public function getTopEditsNamespace(Project $project, User $user, $namespace = 0, $limit = 1000, $offset = 0)
38
    {
39
        // Set up cache.
40
        $cacheKey = $this->getCacheKey(func_get_args(), 'topedits_ns');
41
        if ($this->cache->hasItem($cacheKey)) {
42
            return $this->cache->getItem($cacheKey)->get();
43
        }
44
45
        $pageTable = $this->getTableName($project->getDatabaseName(), 'page');
46
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
47
48
        $hasPageAssessments = $this->isLabs() && $project->hasPageAssessments() && $namespace === 0;
49
        $paTable = $this->getTableName($project->getDatabaseName(), 'page_assessments');
50
        $paSelect = $hasPageAssessments
51
            ?  ", (
52
                    SELECT pa_class
53
                    FROM $paTable
54
                    WHERE pa_page_id = page_id
55
                    AND pa_class != 'Unknown'
56
                    LIMIT 1
57
                ) AS pa_class"
58
            : '';
59
60
        $sql = "SELECT page_namespace, page_title, page_is_redirect, COUNT(page_title) AS count
61
                    $paSelect
62
                FROM $pageTable
63
                JOIN $revisionTable ON page_id = rev_page
64
                WHERE rev_user_text = :username
65
                AND page_namespace = :namespace
66
                GROUP BY page_namespace, page_title
67
                ORDER BY count DESC
68
                LIMIT $limit
69
                OFFSET $offset";
70
        $result = $this->executeProjectsQuery($sql, [
71
            'username' => $user->getUsername(),
72
            'namespace' => $namespace,
73
        ])->fetchAll();
74
75
        // Cache and return.
76
        return $this->setCache($cacheKey, $result);
77
    }
78
79
    /**
80
     * Count the number of edits in the given namespace.
81
     * @param Project $project
82
     * @param User $user
83
     * @param $namespace
84
     * @return mixed
85
     */
86
    public function countEditsNamespace(Project $project, User $user, $namespace)
87
    {
88
        // Set up cache.
89
        $cacheKey = $this->getCacheKey(func_get_args(), 'topedits_count_ns');
90
        if ($this->cache->hasItem($cacheKey)) {
91
            return $this->cache->getItem($cacheKey)->get();
92
        }
93
94
        $pageTable = $this->getTableName($project->getDatabaseName(), 'page');
95
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
96
97
        $sql = "SELECT COUNT(DISTINCT page_id) AS count
98
                FROM $pageTable
99
                JOIN $revisionTable ON page_id = rev_page
100
                WHERE rev_user_text = :username
101
                AND page_namespace = :namespace";
102
        $result = $this->executeProjectsQuery($sql, [
103
            'username' => $user->getUsername(),
104
            'namespace' => $namespace,
105
        ])->fetch()['count'];
106
107
        // Cache and return.
108
        return $this->setCache($cacheKey, $result);
109
    }
110
111
    /**
112
     * Get the top edits by a user across all namespaces.
113
     * @param Project $project
114
     * @param User $user
115
     * @param int $limit Number of edits to fetch.
116
     * @return string[] page_namespace, page_title, page_is_redirect,
117
     *   count (number of edits), assessment (page assessment).
118
     */
119
    public function getTopEditsAllNamespaces(Project $project, User $user, $limit = 10)
120
    {
121
        // Set up cache.
122
        $cacheKey = $this->getCacheKey(func_get_args(), 'topedits_all');
123
        if ($this->cache->hasItem($cacheKey)) {
124
            return $this->cache->getItem($cacheKey)->get();
125
        }
126
127
        $pageTable = $this->getTableName($project->getDatabaseName(), 'page');
128
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
129
        $hasPageAssessments = $this->isLabs() && $project->hasPageAssessments();
130
        $pageAssessmentsTable = $this->getTableName($project->getDatabaseName(), 'page_assessments');
131
        $paSelect = $hasPageAssessments
132
            ?  ", (
133
                    SELECT pa_class
134
                    FROM $pageAssessmentsTable
135
                    WHERE pa_page_id = e.page_id
136
                    LIMIT 1
137
                ) AS pa_class"
138
            : ', NULL as pa_class';
139
140
        $sql = "SELECT c.page_namespace, e.page_title, c.page_is_redirect, c.count $paSelect
141
                FROM
142
                (
143
                    SELECT b.page_namespace, b.page_is_redirect, b.rev_page, b.count
144
                        ,@rn := if(@ns = b.page_namespace, @rn + 1, 1) AS row_number
145
                        ,@ns := b.page_namespace AS dummy
146
                    FROM
147
                    (
148
                        SELECT page_namespace, page_is_redirect, rev_page, count(rev_page) AS count
149
                        FROM $revisionTable
150
                        JOIN $pageTable ON page_id = rev_page
151
                        WHERE rev_user_text = :username
152
                        GROUP BY page_namespace, rev_page
153
                    ) AS b
154
                    JOIN (SELECT @ns := NULL, @rn := 0) AS vars
155
                    ORDER BY b.page_namespace ASC, b.count DESC
156
                ) AS c
157
                JOIN $pageTable e ON e.page_id = c.rev_page
158
                WHERE c.row_number < $limit";
159
        $result = $this->executeProjectsQuery($sql, [
160
            'username' => $user->getUsername(),
161
        ])->fetchAll();
162
163
        // Cache and return.
164
        return $this->setCache($cacheKey, $result);
165
    }
166
167
    /**
168
     * Get the top edits by a user to a single page.
169
     * @param Page $page
170
     * @param User $user
171
     * @return string[] Each row with keys 'id', 'timestamp', 'minor', 'length',
172
     *   'length_change', 'reverted', 'user_id', 'username', 'comment', 'parent_comment'
173
     */
174
    public function getTopEditsPage(Page $page, User $user)
175
    {
176
        // Set up cache.
177
        $cacheKey = $this->getCacheKey(func_get_args(), 'topedits_page');
178
        if ($this->cache->hasItem($cacheKey)) {
179
            return $this->cache->getItem($cacheKey)->get();
180
        }
181
182
        $results = $this->queryTopEditsPage($page, $user, true);
183
184
        // Now we need to get the most recent revision, since the childrevs stuff excludes it.
185
        $lastRev = $this->queryTopEditsPage($page, $user, false);
186
        if (empty($results) || $lastRev[0]['id'] !== $results[0]['id']) {
187
            $results = array_merge($lastRev, $results);
188
        }
189
190
        // Cache and return.
191
        return $this->setCache($cacheKey, $results);
192
    }
193
194
    /**
195
     * The actual query to get the top edits by the user to the page.
196
     * Because of the way the main query works, we aren't given the most recent revision,
197
     * so we have to call this twice, once with $childRevs set to true and once with false.
198
     * @param Page $page
199
     * @param User $user
200
     * @param boolean $childRevs Whether to include child revisions.
201
     * @return string[] Each row with keys 'id', 'timestamp', 'minor', 'length',
202
     *   'length_change', 'reverted', 'user_id', 'username', 'comment', 'parent_comment'
203
     */
204
    private function queryTopEditsPage(Page $page, User $user, $childRevs = false)
205
    {
206
        $revTable = $this->getTableName($page->getProject()->getDatabaseName(), 'revision');
207
208
        if ($childRevs) {
209
            $childSelect = ', (CASE WHEN childrevs.rev_sha1 = parentrevs.rev_sha1 THEN 1 ELSE 0 END) AS reverted,
210
                    childrevs.rev_comment AS parent_comment';
211
            $childJoin = "LEFT JOIN $revTable AS childrevs ON (revs.rev_id = childrevs.rev_parent_id)";
212
            $childWhere = 'AND childrevs.rev_page = :pageid';
213
            $childLimit = '';
214
        } else {
215
            $childSelect = ', "" AS parent_comment, 0 AS reverted';
216
            $childJoin = '';
217
            $childWhere = '';
218
            $childLimit = 'LIMIT 1';
219
        }
220
221
        $sql = "SELECT
222
                    revs.rev_id AS id,
223
                    revs.rev_timestamp AS timestamp,
224
                    revs.rev_minor_edit AS minor,
225
                    revs.rev_len AS length,
226
                    (CAST(revs.rev_len AS SIGNED) - IFNULL(parentrevs.rev_len, 0)) AS length_change,
227
                    revs.rev_user AS user_id,
228
                    revs.rev_user_text AS username,
229
                    revs.rev_comment AS comment
230
                    $childSelect
231
                FROM $revTable AS revs
232
                LEFT JOIN $revTable AS parentrevs ON (revs.rev_parent_id = parentrevs.rev_id)
233
                $childJoin
234
                WHERE revs.rev_user_text = :username
235
                AND revs.rev_page = :pageid
236
                $childWhere
237
                ORDER BY revs.rev_timestamp DESC
238
                $childLimit";
239
240
        return $this->executeProjectsQuery($sql, [
241
            'pageid' => $page->getId(),
242
            'username' => $user->getUsername(),
243
        ])->fetchAll();
244
    }
245
246
    /**
247
     * Get the display titles of the given pages.
248
     * @param Project $project
249
     * @param string[] $titles List of page titles.
250
     * @return string[] Keys are the original supplied titles, and values are the display titles.
251
     */
252
    public function getDisplayTitles(Project $project, $titles)
253
    {
254
        return PageRepository::displayTitles($project, $titles);
255
    }
256
}
257