Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
61.90% |
78 / 126 |
|
42.86% |
6 / 14 |
CRAP | |
0.00% |
0 / 1 |
ProofreadPageLuaLibrary | |
61.90% |
78 / 126 |
|
42.86% |
6 / 14 |
71.34 | |
0.00% |
0 / 1 |
register | |
100.00% |
28 / 28 |
|
100.00% |
1 / 1 |
2 | |||
addTemplateDependencyOnPage | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
addTemplateDependencyOnAllPagesInIndex | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
20 | |||
doGetIndexProgress | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
6 | |||
getIndexContent | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
doGetIndexFields | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
doGetIndexCategories | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
getPaginationForIndex | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
2.15 | |||
doGetNumberOfPages | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
doGetPageInIndex | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
doGetPageQuality | |
81.82% |
9 / 11 |
|
0.00% |
0 / 1 |
2.02 | |||
getIndexForPage | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
doGetIndexForPage | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
doGetPageNumbering | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace ProofreadPage; |
4 | |
5 | use MediaWiki\Content\WikitextContent; |
6 | use MediaWiki\Extension\Scribunto\Engines\LuaCommon\LibraryBase; |
7 | use MediaWiki\Extension\Scribunto\Engines\LuaCommon\LuaError; |
8 | use MediaWiki\Logger\LoggerFactory; |
9 | use MediaWiki\Parser\ParserOutput; |
10 | use MediaWiki\Title\Title; |
11 | use OutOfBoundsException; |
12 | use ProofreadPage\Index\IndexContent; |
13 | use ProofreadPage\Page\PageLevel; |
14 | use ProofreadPage\Pagination\Pagination; |
15 | use Psr\Log\LoggerInterface; |
16 | |
17 | class ProofreadPageLuaLibrary extends LibraryBase { |
18 | |
19 | /** |
20 | * @var Context |
21 | */ |
22 | private $context; |
23 | |
24 | /** |
25 | * @var LoggerInterface |
26 | */ |
27 | private $logger; |
28 | |
29 | /** |
30 | * @var ParserOutput|null |
31 | */ |
32 | private $parserOutput; |
33 | |
34 | /** @inheritDoc */ |
35 | public function register() { |
36 | $this->context = Context::getDefaultContext(); |
37 | |
38 | $extensionLuaPath = __DIR__ . '/lualib/ProofreadPage.lua'; |
39 | |
40 | // "export" PHP functions to the Lua library interface |
41 | $lib = [ |
42 | 'doGetIndexProgress' => [ $this, 'doGetIndexProgress' ], |
43 | 'doGetIndexFields' => [ $this, 'doGetIndexFields' ], |
44 | 'doGetIndexCategories' => [ $this, 'doGetIndexCategories' ], |
45 | 'doGetNumberOfPages' => [ $this, 'doGetNumberOfPages' ], |
46 | 'doGetPageInIndex' => [ $this, 'doGetPageInIndex' ], |
47 | 'doGetIndexPageNumbers' => [ $this, 'doGetIndexPageNumbers' ], |
48 | 'doGetPageQuality' => [ $this, 'doGetPageQuality' ], |
49 | 'doGetIndexForPage' => [ $this, 'doGetIndexForPage' ], |
50 | 'doGetPageNumbering' => [ $this, 'doGetPageNumbering' ] |
51 | ]; |
52 | $opts = [ |
53 | 'NS_INDEX' => $this->context->getIndexNamespaceId(), |
54 | 'NS_PAGE' => $this->context->getPageNamespaceId(), |
55 | 'qualityLevel' => [ |
56 | 'WITHOUT_TEXT' => PageLevel::WITHOUT_TEXT, |
57 | 'NOT_PROOFREAD' => PageLevel::NOT_PROOFREAD, |
58 | 'PROBLEMATIC' => PageLevel::PROBLEMATIC, |
59 | 'PROOFREAD' => PageLevel::PROOFREAD, |
60 | 'VALIDATED' => PageLevel::VALIDATED, |
61 | ] |
62 | ]; |
63 | |
64 | $this->logger = LoggerFactory::getInstance( 'ext.proofreadPage.lua' ); |
65 | |
66 | if ( $this->getParser() ) { |
67 | $this->parserOutput = $this->getParser()->getOutput(); |
68 | } |
69 | |
70 | return $this->getEngine()->registerInterface( $extensionLuaPath, $lib, $opts ); |
71 | } |
72 | |
73 | /** |
74 | * Add a parser dependency on the given page (index or otherwise) |
75 | * |
76 | * @param Title|null $pageTitle |
77 | */ |
78 | private function addTemplateDependencyOnPage( ?Title $pageTitle ) { |
79 | if ( $this->parserOutput && $pageTitle ) { |
80 | $this->parserOutput->addTemplate( |
81 | $pageTitle, |
82 | $pageTitle->getArticleID(), |
83 | $pageTitle->getLatestRevID() |
84 | ); |
85 | } |
86 | } |
87 | |
88 | /** |
89 | * Add a parser dependency on every page in the index (and the index itself) |
90 | * |
91 | * @param Title|null $indexTitle |
92 | */ |
93 | private function addTemplateDependencyOnAllPagesInIndex( ?Title $indexTitle ) { |
94 | if ( $this->parserOutput && $indexTitle ) { |
95 | // this depends on the index itself (for the content) |
96 | $pagination = $this->getPaginationForIndex( $indexTitle ); |
97 | $pagination->prefetchPageLinks(); |
98 | |
99 | foreach ( $pagination as $pageTitle ) { |
100 | $this->addTemplateDependencyOnPage( $pageTitle ); |
101 | } |
102 | } |
103 | } |
104 | |
105 | /** |
106 | * Return the index statistics for the given index name |
107 | * |
108 | * This function may be expensive if the index has not yet been cached. |
109 | * |
110 | * @param string $indexName The index title to get stats for |
111 | * @return array The result table, in an array |
112 | * @throws LuaError If the expensive function count has been exceeded |
113 | */ |
114 | public function doGetIndexProgress( string $indexName ): array { |
115 | $indexTitle = Title::makeTitleSafe( $this->context->getIndexNamespaceId(), $indexName ); |
116 | |
117 | $statsLookup = $this->context->getIndexQualityStatsLookup(); |
118 | |
119 | if ( !$statsLookup->isIndexTitleInCache( $indexTitle ) ) { |
120 | $this->logger->debug( "Index stats cache miss: " . $indexTitle->getFullText() ); |
121 | $this->incrementExpensiveFunctionCount(); |
122 | } |
123 | |
124 | // Progress depends on every page in the index |
125 | $this->addTemplateDependencyOnAllPagesInIndex( $indexTitle ); |
126 | |
127 | $indexStats = $statsLookup->getStatsForIndexTitle( $indexTitle ); |
128 | |
129 | // Map stats to the Lua table |
130 | return [ [ |
131 | 0 => $indexStats->getNumberOfPagesForQualityLevel( 0 ), |
132 | 1 => $indexStats->getNumberOfPagesForQualityLevel( 1 ), |
133 | 2 => $indexStats->getNumberOfPagesForQualityLevel( 2 ), |
134 | 3 => $indexStats->getNumberOfPagesForQualityLevel( 3 ), |
135 | 4 => $indexStats->getNumberOfPagesForQualityLevel( 4 ), |
136 | "total" => $indexStats->getNumberOfPages(), |
137 | "existing" => $indexStats->getNumberOfPagesWithAnyQualityLevel(), |
138 | "missing" => $indexStats->getNumberOfPagesWithoutQualityLevel(), |
139 | ] ]; |
140 | } |
141 | |
142 | /** |
143 | * Get the IndexContent for a give index |
144 | * @param string $indexName the name of the index |
145 | * @return IndexContent|null the index content (or null if the index is |
146 | * not found or the title construction fails) |
147 | */ |
148 | private function getIndexContent( string $indexName ): ?IndexContent { |
149 | $indexTitle = Title::makeTitleSafe( $this->context->getIndexNamespaceId(), $indexName ); |
150 | $contentLookup = $this->context->getIndexContentLookup(); |
151 | |
152 | if ( !$contentLookup->isIndexTitleInCache( $indexTitle ) ) { |
153 | $this->logger->debug( "Index content cache miss: " . $indexTitle->getFullText() ); |
154 | $this->incrementExpensiveFunctionCount(); |
155 | } |
156 | |
157 | // if the index content is needed, there's a dependency on the index |
158 | $this->addTemplateDependencyOnPage( $indexTitle ); |
159 | |
160 | return $contentLookup->getIndexContentForTitle( $indexTitle ); |
161 | } |
162 | |
163 | /** |
164 | * Return the index fields for the given index name |
165 | * |
166 | * This function may be expensive if the index content has not yet been cached. |
167 | * |
168 | * @param string $indexName the index title to get stats for |
169 | * @return array the result table, in an array |
170 | * @throws LuaError If the expensive function count has been exceeded |
171 | */ |
172 | public function doGetIndexFields( string $indexName ): array { |
173 | // this can be expensive |
174 | $indexContent = $this->getIndexContent( $indexName ); |
175 | |
176 | $textConverter = static function ( WikitextContent $field ): string { |
177 | return $field->getText(); |
178 | }; |
179 | |
180 | return [ array_map( $textConverter, $indexContent->getFields() ) ]; |
181 | } |
182 | |
183 | /** |
184 | * Return the index categories for the given index name |
185 | * |
186 | * Note this is only the categories entered on the index page, and doesn't |
187 | * include categories added by the Index page template or any other |
188 | * expansion of wikitext. |
189 | * |
190 | * This function may be expensive if the index content has not yet been cached. |
191 | * |
192 | * @param string $indexName The index title to get stats for |
193 | * @return array The result table, in an array |
194 | * @throws LuaError If the expensive function count has been exceeded |
195 | */ |
196 | public function doGetIndexCategories( string $indexName ): array { |
197 | // this can be expensive |
198 | $indexContent = $this->getIndexContent( $indexName ); |
199 | |
200 | $textConverter = static function ( Title $field ): string { |
201 | return $field->getText(); |
202 | }; |
203 | |
204 | // remap into a Lua-esque 1-indexed array of title strings |
205 | return [ |
206 | array_map( $textConverter, $indexContent->getCategories() ) |
207 | ]; |
208 | } |
209 | |
210 | /** |
211 | * Get the Pagination for a given index |
212 | * |
213 | * Increments the expensive function counter if needed |
214 | * |
215 | * @param Title $indexTitle |
216 | * @return Pagination |
217 | */ |
218 | private function getPaginationForIndex( Title $indexTitle ) { |
219 | $paginationFactory = $this->context->getPaginationFactory(); |
220 | |
221 | if ( !$paginationFactory->isIndexTitleInCache( $indexTitle ) ) { |
222 | $this->logger->debug( "Index pagination cache miss: " . $indexTitle->getFullText() ); |
223 | $this->incrementExpensiveFunctionCount(); |
224 | } |
225 | |
226 | // the pagination depends on the index content |
227 | $this->addTemplateDependencyOnPage( $indexTitle ); |
228 | |
229 | // maybe expensive, but cached |
230 | return $paginationFactory->getPaginationForIndexTitle( $indexTitle ); |
231 | } |
232 | |
233 | /** |
234 | * Get the total number of pages in the index |
235 | * |
236 | * @param string $indexName The index title |
237 | * @return array The number of pages in the index, 0 for an invalid index |
238 | */ |
239 | public function doGetNumberOfPages( string $indexName ): array { |
240 | $indexTitle = Title::makeTitleSafe( $this->context->getIndexNamespaceId(), $indexName ); |
241 | |
242 | // maybe expensive |
243 | $pagination = $this->getPaginationForIndex( $indexTitle ); |
244 | |
245 | return [ $pagination->getNumberOfPages() ]; |
246 | } |
247 | |
248 | /** |
249 | * The n'th page in the pagination for an index |
250 | * |
251 | * @param string $indexName The index title |
252 | * @param int $n The index of the pag in that index (1 is the first) |
253 | * @return array The page title, as an array for Lua |
254 | */ |
255 | public function doGetPageInIndex( string $indexName, int $n ): array { |
256 | $indexTitle = Title::makeTitleSafe( $this->context->getIndexNamespaceId(), $indexName ); |
257 | |
258 | // maybe expensive |
259 | $pagination = $this->getPaginationForIndex( $indexTitle ); |
260 | |
261 | try { |
262 | $pageTitle = $pagination->getPageTitle( $n ); |
263 | } catch ( OutOfBoundsException $e ) { |
264 | return [ null ]; |
265 | } |
266 | |
267 | return [ $pageTitle->getText() ]; |
268 | } |
269 | |
270 | /** |
271 | * Get the quality information for a given page |
272 | * |
273 | * @param string $pageName The title of the page to get the info for |
274 | * @return array The quality information as an array |
275 | */ |
276 | public function doGetPageQuality( string $pageName ): array { |
277 | $pageTitle = Title::makeTitleSafe( $this->context->getPageNamespaceId(), $pageName ); |
278 | $pqLookup = $this->context->getPageQualityLevelLookup(); |
279 | |
280 | if ( !$pqLookup->isPageTitleInCache( $pageTitle ) ) { |
281 | $this->logger->debug( "Page quality cache miss: " . $pageTitle->getFullText() ); |
282 | $this->incrementExpensiveFunctionCount(); |
283 | } |
284 | |
285 | // the page quality depends only on that page |
286 | $this->addTemplateDependencyOnPage( $pageTitle ); |
287 | |
288 | return [ [ |
289 | 'level' => $pqLookup->getQualityLevelForPageTitle( $pageTitle ), |
290 | // 'user' => $pageLevel->getUser(), // T289137 |
291 | // 'timestamp' maybe? |
292 | ] ]; |
293 | } |
294 | |
295 | /** |
296 | * Get the title of the index for a given page |
297 | * |
298 | * @param Title $pageTitle |
299 | * @return Title|null The title of the index |
300 | */ |
301 | private function getIndexForPage( Title $pageTitle ): ?Title { |
302 | $ifpLookup = $this->context->getIndexForPageLookup(); |
303 | |
304 | if ( !$ifpLookup->isPageTitleInCache( $pageTitle ) ) { |
305 | $this->logger->debug( "Index for page cache miss: " . $pageTitle->getFullText() ); |
306 | $this->incrementExpensiveFunctionCount(); |
307 | } |
308 | |
309 | return $ifpLookup->getIndexForPageTitle( $pageTitle ); |
310 | } |
311 | |
312 | /** |
313 | * Get the title of the index for a given page |
314 | * |
315 | * @param string $pageName The title of the page to get the index for |
316 | * @return array The name of the index |
317 | */ |
318 | public function doGetIndexForPage( string $pageName ): array { |
319 | $pageTitle = Title::makeTitleSafe( $this->context->getPageNamespaceId(), $pageName ); |
320 | $indexTitle = $this->getIndexForPage( $pageTitle ); |
321 | |
322 | // if the page is moved, this could change, and also if the index pagination changes |
323 | $this->addTemplateDependencyOnPage( $pageTitle ); |
324 | $this->addTemplateDependencyOnPage( $indexTitle ); |
325 | |
326 | if ( $indexTitle == null ) { |
327 | return [ null ]; |
328 | } |
329 | |
330 | return [ $indexTitle->getBaseText() ]; |
331 | } |
332 | |
333 | /** |
334 | * Get the page numbering for a given page |
335 | * |
336 | * @param string $pageName The title of the page to get the numbering for |
337 | * @return array The name of the index |
338 | */ |
339 | public function doGetPageNumbering( string $pageName ): array { |
340 | $pageTitle = Title::makeTitleSafe( $this->context->getPageNamespaceId(), $pageName ); |
341 | |
342 | // maybe expensive |
343 | $indexTitle = $this->getIndexForPage( $pageTitle ); |
344 | |
345 | if ( $indexTitle == null ) { |
346 | return [ null ]; |
347 | } |
348 | |
349 | // this is a cached lookup, so we'll only look this indexes pagination up once, |
350 | // but that first time is expensive |
351 | $pagination = $this->getPaginationForIndex( $indexTitle ); |
352 | $language = $indexTitle->getPageLanguage(); |
353 | |
354 | $pageInPagination = $pagination->getPageNumber( $pageTitle ); |
355 | $dispNum = $pagination->getDisplayedPageNumber( $pageInPagination ); |
356 | |
357 | return [ [ |
358 | 'position' => $pageInPagination, |
359 | 'display' => $dispNum->getFormattedPageNumber( $language ), |
360 | 'raw' => $dispNum->getRawPageNumber( $language ) |
361 | ] ]; |
362 | } |
363 | } |