1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace AppBundle\Helper; |
4
|
|
|
|
5
|
|
|
use AppBundle\Twig\AppExtension; |
6
|
|
|
use DateInterval; |
7
|
|
|
use Doctrine\DBAL\Connection; |
8
|
|
|
use Exception; |
9
|
|
|
use Symfony\Component\DependencyInjection\Container; |
10
|
|
|
use Symfony\Component\VarDumper\VarDumper; |
11
|
|
|
|
12
|
|
|
class EditCounterHelper extends HelperBase |
13
|
|
|
{ |
14
|
|
|
|
15
|
|
|
/** @var Container */ |
16
|
|
|
protected $container; |
17
|
|
|
|
18
|
|
|
/** @var Connection */ |
19
|
|
|
protected $replicas; |
20
|
|
|
|
21
|
|
|
/** @var LabsHelper */ |
22
|
|
|
protected $labsHelper; |
23
|
|
|
|
24
|
|
|
public function __construct(Container $container) |
25
|
|
|
{ |
26
|
|
|
$this->container = $container; |
27
|
|
|
$this->replicas = $container->get('doctrine')->getManager('replicas')->getConnection(); |
28
|
|
|
$this->labsHelper = $container->get('app.labs_helper'); |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Get the ID of a user. |
33
|
|
|
* @param string $usernameOrIp The username or IP address. |
34
|
|
|
* @return integer The user's ID. |
35
|
|
|
*/ |
36
|
|
|
public function getUserId($usernameOrIp) |
37
|
|
|
{ |
38
|
|
|
$userTable = $this->labsHelper->getTable('user'); |
39
|
|
|
$sql = "SELECT user_id FROM $userTable WHERE user_name = :username LIMIT 1"; |
40
|
|
|
$resultQuery = $this->replicas->prepare($sql); |
41
|
|
|
$resultQuery->bindParam("username", $usernameOrIp); |
42
|
|
|
$resultQuery->execute(); |
43
|
|
|
$userId = (int)$resultQuery->fetchColumn(); |
44
|
|
|
return $userId; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Get total edit counts for the top 10 projects for this user. |
49
|
|
|
* @param string $username The username. |
50
|
|
|
* @return string[] Elements are arrays with 'dbName', 'url', 'name', and 'total'. |
51
|
|
|
*/ |
52
|
|
|
public function getTopProjectsEditCounts($username, $numProjects = 10) |
53
|
|
|
{ |
54
|
|
|
$topEditCounts = []; |
55
|
|
|
foreach ($this->labsHelper->allProjects() as $project) { |
56
|
|
|
// Get total edit count from DB otherwise. |
57
|
|
|
$revisionTableName = $this->labsHelper->getTable('revision', $project['dbName']); |
58
|
|
|
$sql = "SELECT COUNT(rev_id) FROM $revisionTableName WHERE rev_user_text=:username"; |
59
|
|
|
$stmt = $this->replicas->prepare($sql); |
60
|
|
|
$stmt->bindParam("username", $username); |
61
|
|
|
$stmt->execute(); |
62
|
|
|
$total = (int)$stmt->fetchColumn(); |
63
|
|
|
$topEditCounts[$project['dbName']] = array_merge($project, ['total' => $total]); |
64
|
|
|
} |
65
|
|
|
uasort($topEditCounts, function ($a, $b) { |
66
|
|
|
return $b['total'] - $a['total']; |
67
|
|
|
}); |
68
|
|
|
return array_slice($topEditCounts, 0, $numProjects); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Get revision counts for the given user. |
73
|
|
|
* @param integer $userId The user's ID. |
74
|
|
|
* @returns string[] With keys: 'archived', 'total', 'first', 'last', '24h', '7d', '30d', and |
75
|
|
|
* '365d'. |
76
|
|
|
* @throws Exception |
77
|
|
|
*/ |
78
|
|
|
public function getRevisionCounts($userId) |
79
|
|
|
{ |
80
|
|
|
// Set up cache. |
81
|
|
|
$cacheKey = 'revisioncounts.'.$userId; |
82
|
|
|
if ($this->cacheHas($cacheKey)) { |
83
|
|
|
return $this->cacheGet($cacheKey); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
// Prepare the query and execute |
87
|
|
|
$archiveTable = $this->labsHelper->getTable('archive'); |
88
|
|
|
$revisionTable = $this->labsHelper->getTable('revision'); |
89
|
|
|
$resultQuery = $this->replicas->prepare(" |
90
|
|
|
(SELECT 'deleted' as source, COUNT(ar_id) AS value FROM $archiveTable |
91
|
|
|
WHERE ar_user = :userId) |
92
|
|
|
UNION |
93
|
|
|
(SELECT 'live' as source, COUNT(rev_id) AS value FROM $revisionTable |
94
|
|
|
WHERE rev_user = :userId) |
95
|
|
|
UNION |
96
|
|
|
(SELECT 'first' as source, rev_timestamp FROM $revisionTable |
97
|
|
|
WHERE rev_user = :userId ORDER BY rev_timestamp ASC LIMIT 1) |
98
|
|
|
UNION |
99
|
|
|
(SELECT 'last' as source, rev_timestamp FROM $revisionTable |
100
|
|
|
WHERE rev_user = :userId ORDER BY rev_timestamp DESC LIMIT 1) |
101
|
|
|
UNION |
102
|
|
|
(SELECT '24h' as source, COUNT(rev_id) as value FROM $revisionTable |
103
|
|
|
WHERE rev_user = :userId AND rev_timestamp >= DATE_SUB(NOW(),INTERVAL 24 HOUR)) |
104
|
|
|
UNION |
105
|
|
|
(SELECT '7d' as source, COUNT(rev_id) as value FROM $revisionTable |
106
|
|
|
WHERE rev_user = :userId AND rev_timestamp >= DATE_SUB(NOW(),INTERVAL 7 DAY)) |
107
|
|
|
UNION |
108
|
|
|
(SELECT '30d' as source, COUNT(rev_id) as value FROM $revisionTable |
109
|
|
|
WHERE rev_user = :userId AND rev_timestamp >= DATE_SUB(NOW(),INTERVAL 30 DAY)) |
110
|
|
|
UNION |
111
|
|
|
(SELECT '365d' as source, COUNT(rev_id) as value FROM $revisionTable |
112
|
|
|
WHERE rev_user = :userId AND rev_timestamp >= DATE_SUB(NOW(),INTERVAL 365 DAY)) |
113
|
|
|
UNION |
114
|
|
|
(SELECT 'small' AS source, COUNT(rev_id) AS value FROM $revisionTable |
115
|
|
|
WHERE rev_user = :userId AND rev_len < 20) |
116
|
|
|
UNION |
117
|
|
|
(SELECT 'large' AS source, COUNT(rev_id) AS value FROM $revisionTable |
118
|
|
|
WHERE rev_user = :userId AND rev_len > 1000) |
119
|
|
|
UNION |
120
|
|
|
(SELECT 'with_comments' AS source, COUNT(rev_id) AS value FROM $revisionTable |
121
|
|
|
WHERE rev_user = :userId AND rev_comment = '') |
122
|
|
|
UNION |
123
|
|
|
(SELECT 'minor_edits' AS source, COUNT(rev_id) AS value FROM $revisionTable |
124
|
|
|
WHERE rev_user = :userId AND rev_minor_edit = 1) |
125
|
|
|
"); |
126
|
|
|
$resultQuery->bindParam("userId", $userId); |
127
|
|
|
$resultQuery->execute(); |
128
|
|
|
$results = $resultQuery->fetchAll(); |
129
|
|
|
|
130
|
|
|
// Unknown user - This is a dirty hack that should be fixed. |
131
|
|
|
if (count($results) < 8) { |
132
|
|
|
throw new Exception("Unable to get all revision counts for user $userId"); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
$revisionCounts = array_combine( |
136
|
|
|
array_map(function ($e) { |
137
|
|
|
return $e['source']; |
138
|
|
|
}, $results), |
139
|
|
|
array_map(function ($e) { |
140
|
|
|
return $e['value']; |
141
|
|
|
}, $results) |
142
|
|
|
); |
143
|
|
|
|
144
|
|
|
// Count the number of days, accounting for when there's zero or one edit. |
145
|
|
|
$revisionCounts['days'] = 0; |
146
|
|
|
if (isset($revisionCounts['first']) && isset($revisionCounts['last'])) { |
147
|
|
|
$editingTimeInSeconds = ceil($revisionCounts['last'] - $revisionCounts['first']); |
148
|
|
|
$revisionCounts['days'] = $editingTimeInSeconds ? $editingTimeInSeconds/(60*60*24) : 1; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
// Format the first and last dates. |
152
|
|
|
$revisionCounts['first'] = isset($revisionCounts['first']) |
153
|
|
|
? date('Y-m-d H:i', strtotime($revisionCounts['first'])) |
154
|
|
|
: 0; |
155
|
|
|
$revisionCounts['last'] = isset($revisionCounts['last']) |
156
|
|
|
? date('Y-m-d H:i', strtotime($revisionCounts['last'])) |
157
|
|
|
: 0; |
158
|
|
|
|
159
|
|
|
// Sum deleted and live to make the total. |
160
|
|
|
$revisionCounts['total'] = $revisionCounts['deleted'] + $revisionCounts['live']; |
161
|
|
|
|
162
|
|
|
// Calculate the average number of live edits per day. |
163
|
|
|
$revisionCounts['avg_per_day'] = round( |
164
|
|
|
$revisionCounts['live'] / $revisionCounts['days'], |
165
|
|
|
3 |
166
|
|
|
); |
167
|
|
|
|
168
|
|
|
// Cache for 10 minutes, and return. |
|
|
|
|
169
|
|
|
$this->cacheSave($cacheKey, $revisionCounts, 'PT10M'); |
|
|
|
|
170
|
|
|
return $revisionCounts; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* |
175
|
|
|
* @param $username |
176
|
|
|
* @return integer |
177
|
|
|
*/ |
178
|
|
|
public function getPageCounts($username, $totalRevisions) |
179
|
|
|
{ |
180
|
|
|
$resultQuery = $this->replicas->prepare(" |
181
|
|
|
SELECT 'unique' as source, COUNT(distinct rev_page) as value |
182
|
|
|
FROM ".$this->labsHelper->getTable('revision')." where rev_user_text=:username |
183
|
|
|
UNION |
184
|
|
|
SELECT 'created-live' as source, COUNT(*) as value from ".$this->labsHelper->getTable('revision')." |
185
|
|
|
WHERE rev_user_text=:username and rev_parent_id=0 |
186
|
|
|
UNION |
187
|
|
|
SELECT 'created-deleted' as source, COUNT(*) as value from " |
188
|
|
|
.$this->labsHelper->getTable('archive')." |
189
|
|
|
WHERE ar_user_text=:username and ar_parent_id=0 |
190
|
|
|
UNION |
191
|
|
|
SELECT 'moved' as source, count(*) as value from ".$this->labsHelper->getTable('logging')." |
192
|
|
|
WHERE log_type='move' and log_action='move' and log_user_text=:username |
193
|
|
|
"); |
194
|
|
|
$resultQuery->bindParam("username", $username); |
195
|
|
|
$resultQuery->execute(); |
196
|
|
|
$results = $resultQuery->fetchAll(); |
197
|
|
|
|
198
|
|
|
$pageCounts = array_combine( |
199
|
|
|
array_map(function ($e) { |
200
|
|
|
return $e['source']; |
201
|
|
|
}, $results), |
202
|
|
|
array_map(function ($e) { |
203
|
|
|
return $e['value']; |
204
|
|
|
}, $results) |
205
|
|
|
); |
206
|
|
|
|
207
|
|
|
// Total created. |
208
|
|
|
$pageCounts['created'] = $pageCounts['created-live'] + $pageCounts['created-deleted']; |
209
|
|
|
|
210
|
|
|
// Calculate the average number of edits per page. |
211
|
|
|
$pageCounts['edits_per_page'] = 0; |
212
|
|
|
if ($pageCounts['unique'] && $totalRevisions) { |
213
|
|
|
$pageCounts['edits_per_page'] = round($totalRevisions / $pageCounts['unique'], 3); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
return $pageCounts; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* Get log totals for a user. |
221
|
|
|
* @param integer $userId The user ID. |
222
|
|
|
* @return integer[] Keys are log-action string, values are counts. |
223
|
|
|
*/ |
224
|
|
|
public function getLogCounts($userId) |
225
|
|
|
{ |
226
|
|
|
$sql = "SELECT CONCAT(log_type, '-', log_action) AS source, COUNT(log_id) AS value |
227
|
|
|
FROM ".$this->labsHelper->getTable('logging')." |
228
|
|
|
WHERE log_user = :userId |
229
|
|
|
GROUP BY log_type, log_action"; |
230
|
|
|
$resultQuery = $this->replicas->prepare($sql); |
231
|
|
|
$resultQuery->bindParam('userId', $userId); |
232
|
|
|
$resultQuery->execute(); |
233
|
|
|
$results = $resultQuery->fetchAll(); |
234
|
|
|
$logCounts = array_combine( |
235
|
|
|
array_map(function ($e) { |
236
|
|
|
return $e['source']; |
237
|
|
|
}, $results), |
238
|
|
|
array_map(function ($e) { |
239
|
|
|
return $e['value']; |
240
|
|
|
}, $results) |
241
|
|
|
); |
242
|
|
|
|
243
|
|
|
// Make sure there is some value for each of the wanted counts. |
244
|
|
|
$requiredCounts = [ |
245
|
|
|
'thanks-thank', 'review-approve', 'patrol-patrol','block-block', 'block-unblock', |
246
|
|
|
'protect-protect', 'protect-unprotect', 'delete-delete', 'delete-revision', |
247
|
|
|
'delete-restore', 'import-import', 'upload-upload', 'upload-overwrite', |
248
|
|
|
]; |
249
|
|
|
foreach ($requiredCounts as $req) { |
250
|
|
|
if (!isset($logCounts[$req])) { |
251
|
|
|
$logCounts[$req] = 0; |
252
|
|
|
} |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
// Merge approvals together. |
256
|
|
|
$logCounts['review-approve'] = $logCounts['review-approve'] + |
257
|
|
|
(!empty($logCounts['review-approve-a']) ? $logCounts['review-approve-a'] : 0) + |
258
|
|
|
(!empty($logCounts['review-approve-i']) ? $logCounts['review-approve-i'] : 0) + |
259
|
|
|
(!empty($logCounts['review-approve-ia']) ? $logCounts['review-approve-ia'] : 0); |
260
|
|
|
|
261
|
|
|
// Add Commons upload count, if applicable. |
262
|
|
|
$logCounts['files_uploaded_commons'] = 0; |
263
|
|
|
if ($this->labsHelper->isLabs()) { |
264
|
|
|
$sql = "SELECT COUNT(log_id) FROM commonswiki_p.logging_userindex |
265
|
|
|
WHERE log_type = 'upload' AND log_action = 'upload' AND log_user = :userId"; |
266
|
|
|
$resultQuery = $this->replicas->prepare($sql); |
267
|
|
|
$resultQuery->bindParam('userId', $userId); |
268
|
|
|
$resultQuery->execute(); |
269
|
|
|
$logCounts['files_uploaded_commons'] = $resultQuery->fetchColumn(); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
return $logCounts; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* Get the given user's total edit counts per namespace. |
277
|
|
|
* @param integer $userId The ID of the user. |
278
|
|
|
* @return integer[] Array keys are namespace IDs, values are the edit counts. |
279
|
|
|
*/ |
280
|
|
|
public function getNamespaceTotals($userId) |
281
|
|
|
{ |
282
|
|
|
$sql = "SELECT page_namespace, count(rev_id) AS total |
283
|
|
|
FROM ".$this->labsHelper->getTable('revision') ." r |
284
|
|
|
JOIN ".$this->labsHelper->getTable('page')." p on r.rev_page = p.page_id |
285
|
|
|
WHERE r.rev_user = :id GROUP BY page_namespace"; |
286
|
|
|
$resultQuery = $this->replicas->prepare($sql); |
287
|
|
|
$resultQuery->bindParam(":id", $userId); |
288
|
|
|
$resultQuery->execute(); |
289
|
|
|
$results = $resultQuery->fetchAll(); |
290
|
|
|
$namespaceTotals = array_combine( |
291
|
|
|
array_map(function ($e) { |
292
|
|
|
return $e['page_namespace']; |
293
|
|
|
}, $results), |
294
|
|
|
array_map(function ($e) { |
295
|
|
|
return $e['total']; |
296
|
|
|
}, $results) |
297
|
|
|
); |
298
|
|
|
return $namespaceTotals; |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* Get this user's most recent 10 edits across all projects. |
303
|
|
|
* @param string $username The username. |
304
|
|
|
* @param integer $contribCount The number of items to return. |
305
|
|
|
* @param integer $days The number of days to search from each wiki. |
306
|
|
|
* @return string[] |
307
|
|
|
*/ |
308
|
|
|
public function getRecentGlobalContribs($username, $contribCount = 10, $days = 30) |
309
|
|
|
{ |
310
|
|
|
$allRevisions = []; |
311
|
|
|
foreach ($this->labsHelper->allProjects() as $project) { |
312
|
|
|
$cacheKey = "globalcontribs.{$project['dbName']}.$username"; |
313
|
|
|
if ($this->cacheHas($cacheKey)) { |
314
|
|
|
$revisions = $this->cacheGet($cacheKey); |
315
|
|
|
} else { |
316
|
|
|
$sql = |
317
|
|
|
"SELECT rev_id, rev_comment, rev_timestamp, rev_minor_edit, rev_deleted, " |
318
|
|
|
. " rev_len, rev_parent_id, page_title " |
319
|
|
|
. " FROM " . $this->labsHelper->getTable('revision', $project['dbName']) |
320
|
|
|
. " JOIN " . $this->labsHelper->getTable('page', $project['dbName']) |
321
|
|
|
. " ON (rev_page = page_id)" |
322
|
|
|
. " WHERE rev_timestamp > NOW() - INTERVAL $days DAY AND rev_user_text LIKE :username" |
323
|
|
|
. " ORDER BY rev_timestamp DESC" |
324
|
|
|
. " LIMIT 10"; |
325
|
|
|
$resultQuery = $this->replicas->prepare($sql); |
326
|
|
|
$resultQuery->bindParam(":username", $username); |
327
|
|
|
$resultQuery->execute(); |
328
|
|
|
$revisions = $resultQuery->fetchAll(); |
329
|
|
|
$this->cacheSave($cacheKey, $revisions, 'PT15M'); |
|
|
|
|
330
|
|
|
} |
331
|
|
|
if (count($revisions) === 0) { |
332
|
|
|
continue; |
333
|
|
|
} |
334
|
|
|
$revsWithProject = array_map( |
335
|
|
|
function (&$item) use ($project) { |
336
|
|
|
$item['project_name'] = $project['name']; |
337
|
|
|
$item['project_url'] = $project['url']; |
338
|
|
|
$item['project_db_name'] = $project['dbName']; |
339
|
|
|
return $item; |
340
|
|
|
}, |
341
|
|
|
$revisions |
342
|
|
|
); |
343
|
|
|
$allRevisions = array_merge($allRevisions, $revsWithProject); |
344
|
|
|
} |
345
|
|
|
usort($allRevisions, function ($a, $b) { |
346
|
|
|
return $b['rev_timestamp'] - $a['rev_timestamp']; |
347
|
|
|
}); |
348
|
|
|
return array_slice($allRevisions, 0, $contribCount); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* Get data for a bar chart of monthly edit totals per namespace. |
353
|
|
|
* @param string $username The username. |
354
|
|
|
* @return string[] |
355
|
|
|
*/ |
356
|
|
|
public function getMonthCounts($username) |
357
|
|
|
{ |
358
|
|
|
$cacheKey = "monthcounts.$username"; |
359
|
|
|
if ($this->cacheHas($cacheKey)) { |
360
|
|
|
return $this->cacheGet($cacheKey); |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
$sql = "SELECT " |
364
|
|
|
. " YEAR(rev_timestamp) AS `year`," |
365
|
|
|
. " MONTH(rev_timestamp) AS `month`," |
366
|
|
|
. " page_namespace," |
367
|
|
|
. " COUNT(rev_id) AS `count` " |
368
|
|
|
. " FROM " . $this->labsHelper->getTable('revision') |
369
|
|
|
. " JOIN " . $this->labsHelper->getTable('page') . " ON (rev_page = page_id)" |
370
|
|
|
. " WHERE rev_user_text = :username" |
371
|
|
|
. " GROUP BY YEAR(rev_timestamp), MONTH(rev_timestamp), page_namespace " |
372
|
|
|
. " ORDER BY rev_timestamp DESC"; |
373
|
|
|
$resultQuery = $this->replicas->prepare($sql); |
374
|
|
|
$resultQuery->bindParam(":username", $username); |
375
|
|
|
$resultQuery->execute(); |
376
|
|
|
$totals = $resultQuery->fetchAll(); |
377
|
|
|
$out = [ |
378
|
|
|
'years' => [], |
379
|
|
|
'namespaces' => [], |
380
|
|
|
'totals' => [], |
381
|
|
|
]; |
382
|
|
|
$out['max_year'] = 0; |
383
|
|
|
$out['min_year'] = date('Y'); |
384
|
|
|
foreach ($totals as $total) { |
385
|
|
|
// Collect all applicable years and namespaces. |
386
|
|
|
$out['max_year'] = max($out['max_year'], $total['year']); |
387
|
|
|
$out['min_year'] = min($out['min_year'], $total['year']); |
388
|
|
|
// Collate the counts by namespace, and then year and month. |
389
|
|
|
$ns = $total['page_namespace']; |
390
|
|
|
if (!isset($out['totals'][$ns])) { |
391
|
|
|
$out['totals'][$ns] = []; |
392
|
|
|
} |
393
|
|
|
$out['totals'][$ns][$total['year'].$total['month']] = $total['count']; |
394
|
|
|
} |
395
|
|
|
// Fill in the blanks (where no edits were made in a given month for a namespace). |
396
|
|
|
for ($y = $out['min_year']; $y <= $out['max_year']; $y++) { |
397
|
|
|
for ($m = 1; $m <= 12; $m++) { |
398
|
|
|
foreach ($out['totals'] as $nsId => &$total) { |
399
|
|
|
if (!isset($total[$y.$m])) { |
400
|
|
|
$total[$y.$m] = 0; |
401
|
|
|
} |
402
|
|
|
} |
403
|
|
|
} |
404
|
|
|
} |
405
|
|
|
$this->cacheSave($cacheKey, $out, 'PT10M'); |
|
|
|
|
406
|
|
|
return $out; |
|
|
|
|
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* Get yearly edit totals for this user, grouped by namespace. |
411
|
|
|
* @param string $username |
412
|
|
|
* @return string[] ['<namespace>' => ['<year>' => 'total', ... ], ... ] |
413
|
|
|
*/ |
414
|
|
|
public function getYearlyTotalsByNamespace($username) |
415
|
|
|
{ |
416
|
|
|
$cacheKey = "yearcounts.$username"; |
417
|
|
|
if ($this->cacheHas($cacheKey)) { |
418
|
|
|
return $this->cacheGet($cacheKey); |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
$sql = "SELECT " |
422
|
|
|
. " SUBSTR(CAST(rev_timestamp AS CHAR(4)), 1, 4) AS `year`," |
423
|
|
|
. " page_namespace," |
424
|
|
|
. " COUNT(rev_id) AS `count` " |
425
|
|
|
. " FROM " . $this->labsHelper->getTable('revision') |
426
|
|
|
. " JOIN " . $this->labsHelper->getTable('page') . " ON (rev_page = page_id)" . |
427
|
|
|
" WHERE rev_user_text = :username" . |
428
|
|
|
" GROUP BY SUBSTR(CAST(rev_timestamp AS CHAR(4)), 1, 4), page_namespace " . |
429
|
|
|
" ORDER BY rev_timestamp DESC "; |
430
|
|
|
$resultQuery = $this->replicas->prepare($sql); |
431
|
|
|
$resultQuery->bindParam(":username", $username); |
432
|
|
|
$resultQuery->execute(); |
433
|
|
|
$totals = $resultQuery->fetchAll(); |
434
|
|
|
$out = [ |
435
|
|
|
'years' => [], |
436
|
|
|
'namespaces' => [], |
437
|
|
|
'totals' => [], |
438
|
|
|
]; |
439
|
|
|
foreach ($totals as $total) { |
440
|
|
|
$out['years'][$total['year']] = $total['year']; |
441
|
|
|
$out['namespaces'][$total['page_namespace']] = $total['page_namespace']; |
442
|
|
|
if (!isset($out['totals'][$total['page_namespace']])) { |
443
|
|
|
$out['totals'][$total['page_namespace']] = []; |
444
|
|
|
} |
445
|
|
|
$out['totals'][$total['page_namespace']][$total['year']] = $total['count']; |
446
|
|
|
} |
447
|
|
|
$this->cacheSave($cacheKey, $out, 'PT10M'); |
|
|
|
|
448
|
|
|
return $out; |
|
|
|
|
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
/** |
452
|
|
|
* Get data for the timecard chart, with totals grouped by day and to the nearest two-hours. |
453
|
|
|
* @param string $username The user's username. |
454
|
|
|
* @return string[] |
455
|
|
|
*/ |
456
|
|
|
public function getTimeCard($username) |
457
|
|
|
{ |
458
|
|
|
$cacheKey = "timecard.$username"; |
459
|
|
|
if ($this->cacheHas($cacheKey)) { |
460
|
|
|
return $this->cacheGet($cacheKey); |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
$hourInterval = 2; |
464
|
|
|
$xCalc = "ROUND(HOUR(rev_timestamp)/$hourInterval)*$hourInterval"; |
465
|
|
|
$sql = "SELECT " |
466
|
|
|
. " DAYOFWEEK(rev_timestamp) AS `y`, " |
467
|
|
|
. " $xCalc AS `x`, " |
468
|
|
|
. " COUNT(rev_id) AS `r` " |
469
|
|
|
. " FROM " . $this->labsHelper->getTable('revision') |
470
|
|
|
. " WHERE rev_user_text = :username" |
471
|
|
|
. " GROUP BY DAYOFWEEK(rev_timestamp), $xCalc " |
472
|
|
|
. " "; |
473
|
|
|
$resultQuery = $this->replicas->prepare($sql); |
474
|
|
|
$resultQuery->bindParam(":username", $username); |
475
|
|
|
$resultQuery->execute(); |
476
|
|
|
$totals = $resultQuery->fetchAll(); |
477
|
|
|
// Scale the radii: get the max, then scale each radius. |
478
|
|
|
// This looks inefficient, but there's a max of 72 elements in this array. |
479
|
|
|
$max = 0; |
480
|
|
|
foreach ($totals as $total) { |
481
|
|
|
$max = max($max, $total['r']); |
482
|
|
|
} |
483
|
|
|
foreach ($totals as &$total) { |
484
|
|
|
$total['r'] = round($total['r'] / $max * 100); |
485
|
|
|
} |
486
|
|
|
$this->cacheSave($cacheKey, $totals, 'PT10M'); |
|
|
|
|
487
|
|
|
return $totals; |
488
|
|
|
} |
489
|
|
|
} |
490
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.