-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathWatchAnalyticsParserFunctions.php
241 lines (204 loc) · 6.9 KB
/
WatchAnalyticsParserFunctions.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
<?php
class WatchAnalyticsParserFunctions {
public static function setup( &$parser ) {
$parser->setFunctionHook(
'underwatched_categories',
[
'WatchAnalyticsParserFunctions', // class to call function from
'renderUnderwatchedCategories' // function to call within that class
],
SFH_OBJECT_ARGS
);
// pages needing watchers
$parser->setFunctionHook(
'watchers_needed',
[
'WatchAnalyticsParserFunctions',
'renderWatchersNeeded'
],
SFH_OBJECT_ARGS
);
return true;
}
public static function processArgs( $frame, $args, $defaults ) {
$new_args = [];
$num_args = count( $args );
$num_defaults = count( $defaults );
$count = ( $num_args > $num_defaults ) ? $num_args : $num_defaults;
for ( $i = 0; $i < $count; $i++ ) {
if ( isset( $args[$i] ) ) {
$new_args[$i] = trim( $frame->expand( $args[$i] ) );
} else {
$new_args[$i] = $defaults[$i];
}
}
return $new_args;
}
public static function renderUnderwatchedCategories( &$parser, $frame, $args ) {
// @TODO: currently these do nothing. The namespace arg needs to be text
// provided by the user, so this method needs to convert "Main" to zero, etc
// $args = self::processArgs( $frame, $args, array(0) );
// $namespace = $args[0];
$dbr = wfGetDB( DB_MASTER );
$query = "
SELECT * FROM (
SELECT
p.page_namespace,
p.page_title,
SUM(IF(w.wl_title IS NOT NULL, 1, 0)) AS num_watches,
SUM(IF(w.wl_title IS NOT NULL AND w.wl_notificationtimestamp IS NULL, 1, 0)) AS num_reviewed,
SUM(IF(w.wl_title IS NOT NULL AND w.wl_notificationtimestamp IS NULL, 0, 1)) * 100 / COUNT(*) AS percent_pending,
MAX(TIMESTAMPDIFF(MINUTE, w.wl_notificationtimestamp, UTC_TIMESTAMP())) AS max_pending_minutes,
AVG(TIMESTAMPDIFF(MINUTE, w.wl_notificationtimestamp, UTC_TIMESTAMP())) AS avg_pending_minutes,
(SELECT group_concat(cl_to SEPARATOR ';') as subq_categories FROM categorylinks WHERE cl_from = p.page_id) AS categories
FROM `watchlist` `w`
RIGHT JOIN `page` `p` ON ((p.page_namespace=w.wl_namespace AND p.page_title=w.wl_title))
WHERE
p.page_namespace = 0
AND p.page_is_redirect = 0
GROUP BY p.page_title, p.page_namespace
ORDER BY num_watches, num_reviewed
) tmp
WHERE num_watches < 2";
$result = $dbr->query( $query );
$output = "{| class=\"wikitable sortable\"\n";
$output .= "! Category !! Number of Under-watched pages\n";
$categories = [];
while ( $row = $dbr->fetchObject( $result ) ) {
$pageCategories = explode( ';', $row->categories );
foreach ( $pageCategories as $cat ) {
if ( isset( $categories[ $cat ] ) ) {
$categories[ $cat ]++;
} else {
$categories[ $cat ] = 1;
}
}
}
arsort( $categories );
foreach ( $categories as $cat => $numUnderwatchedPages ) {
if ( $cat === '' ) {
$catLink = "''Uncategorized''";
} else {
$catTitle = Category::newFromName( $cat )->getTitle();
$catLink = "[[:$catTitle|" . $catTitle->getText() . "]]";
}
$output .= "|-\n";
$output .= "| $catLink || $numUnderwatchedPages\n";
}
$output .= '|}[[Category:Pages using beta WatchAnalytics features]]';
return $output;
}
public static function renderWatchersNeeded( &$parser, $frame, $args ) {
global $wgUser;
$wgUserId = $wgUser->getId();
$args = self::processArgs( $frame, $args, [ 0, 60, 3, 10 ] );
$namespace = intval( $args[0] );
$numDays = intval( $args[1] );
$maxWatchers = intval( $args[2] );
$limit = intval( $args[3] );
$rangeTimestamp = date( 'YmdHis', time() - ( $numDays * 24 * 60 * 60 ) );
$dbr = wfGetDB( DB_MASTER );
if ( class_exists( 'Wiretap' ) && false ) {
$query =
"SELECT
p.page_id AS page_id,
p.page_title AS page_title,
p.page_namespace AS page_namespace,
COUNT(DISTINCT(user_name)) AS unique_hits,
red.rd_namespace AS redir_to_ns,
red.rd_title AS redir_to_title,
redir_page.page_id AS redir_id,
(
SELECT COUNT(*)
FROM watchlist AS watch
WHERE
watch.wl_namespace = p.page_namespace
AND watch.wl_title = p.page_title
) AS watches
FROM wiretap AS w
INNER JOIN page AS p ON
p.page_id = w.page_id
LEFT JOIN redirect AS red ON
red.rd_from = p.page_id
LEFT JOIN page AS redir_page ON
red.rd_namespace = redir_page.page_namespace
AND red.rd_title = redir_page.page_title
WHERE
hit_timestamp > $rangeTimestamp
GROUP BY
p.page_namespace, p.page_title
ORDER BY
unique_hits DESC
LIMIT 20";
} else {
$query =
"SELECT page_title, page_namespace FROM (
SELECT
p.page_title AS page_title,
p.page_namespace AS page_namespace,
SUM( IF(w.wl_title IS NOT NULL, 1, 0) ) AS num_watches,
SUM( IF(w.wl_user = $wgUserId, 1, 0) ) AS wg_user_watches,
p.page_counter / SUM( IF(w.wl_title IS NOT NULL, 1, 0) ) AS view_watch_ratio
FROM
watchlist AS w
LEFT JOIN page AS p ON
w.wl_title = p.page_title
AND w.wl_namespace = p.page_namespace
LEFT JOIN revision AS r ON
r.rev_id = p.page_latest
WHERE
p.page_namespace = $namespace
AND p.page_is_redirect = 0
AND r.rev_timestamp > $rangeTimestamp
GROUP BY
p.page_title, p.page_namespace
ORDER BY
view_watch_ratio DESC
) AS tmp
WHERE
num_watches <= $maxWatchers
AND wg_user_watches = 0
LIMIT $limit;";
}
$result = $dbr->query( $query );
$output = '';
while ( $row = $dbr->fetchObject( $result ) ) {
// $title = Title::makeTitle( $row->page_namespace, $row->page_title );
// $watchURL = $title->getFullURL( array( 'action' => 'watch' ) );
// $output .= "* [[$title]] - '''[$watchURL watch]'''\n";
$output .= Xml::tags(
'li',
[],
self::makeWatchLink( $row->page_namespace, $row->page_title )
);
}
$output = Xml::tags( 'ul', [], $output );
return [
0 => $output,
'isHTML' => true,
];
}
protected static function makeWatchLink( $ns, $titleText ) {
global $wgContLang, $wgUser;
$context = RequestContext::getMain();
$nt = Title::makeTitle( $ns, $titleText ); // was: makeTitleSafe
// FIXME: this is from Special:Unwatchedpages. It may not be valid in
// for a parser function intended to get people to watch more pages.
// Perhaps the parser function should offer an option as to whether or
// not to display invalid pages?
if ( !$nt ) {
return Html::element( 'span', [ 'class' => 'mw-invalidtitle' ],
Linker::getInvalidTitleDescription( $context, $ns, $titleText ) );
}
$text = $wgContLang->convert( $nt->getPrefixedText() );
$plink = Linker::linkKnown( $nt, htmlspecialchars( $text ) );
$token = WatchAction::getWatchToken( $nt, $wgUser );
$wlink = Linker::linkKnown(
$nt,
$context->msg( 'watch' )->escaped(),
[ 'class' => 'mw-watch-link watch-analytics-watchers-needed-link' ],
[ 'action' => 'watch', 'token' => $token ]
);
return $context->getLanguage()->specialList( $plink, $wlink );
}
}