Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.24% covered (success)
93.24%
69 / 74
54.55% covered (warning)
54.55%
6 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
DatabasePageQualityLevelLookup
93.24% covered (success)
93.24%
69 / 74
54.55% covered (warning)
54.55%
6 / 11
24.18
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isPageTitleInCache
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 flushCacheForPage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getQualityLevelForPageTitle
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 prefetchQualityLevelForTitles
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 fetchQualityLevelForPageTitles
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 fetchQualityLevelForPageTitlesFromPageProperties
94.44% covered (success)
94.44%
17 / 18
0.00% covered (danger)
0.00%
0 / 1
3.00
 fetchQualityLevelForPageTitlesFromPageCategories
95.00% covered (success)
95.00%
19 / 20
0.00% covered (danger)
0.00%
0 / 1
4
 filterPagesWithoutKnownQuality
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getCategoryForQualityLevels
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 computeCategoryForQualityLevels
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace ProofreadPage\Page;
4
5use InvalidArgumentException;
6use MediaWiki\MediaWikiServices;
7use MediaWiki\Title\Title;
8
9/**
10 * @license GPL-2.0-or-later
11 *
12 * Allows to retrieve the quality level of a Page: page
13 *
14 * TODO: drop the category fallback when all Wikisource pages will have the relevant page property
15 */
16class DatabasePageQualityLevelLookup implements PageQualityLevelLookup {
17
18    /**
19     * @var int
20     */
21    private $pageNamespaceId;
22
23    /**
24     * @var Title[]
25     */
26    private $categoryForQualityLevel;
27
28    /** @var (int|null)[] */
29    private $cache = [];
30
31    /**
32     * @param int $pageNamespaceId
33     */
34    public function __construct( $pageNamespaceId ) {
35        $this->pageNamespaceId = $pageNamespaceId;
36    }
37
38    /**
39     * @inheritDoc
40     */
41    public function isPageTitleInCache( Title $pageTitle ): bool {
42        return array_key_exists( $pageTitle->getDBkey(), $this->cache );
43    }
44
45    /**
46     * @inheritDoc
47     */
48    public function flushCacheForPage( Title $pageTitle ) {
49        unset( $this->cache[ $pageTitle->getDBkey() ] );
50    }
51
52    /**
53     * @inheritDoc
54     */
55    public function getQualityLevelForPageTitle( Title $pageTitle ) {
56        if ( !$pageTitle->inNamespace( $this->pageNamespaceId ) ) {
57            throw new InvalidArgumentException( $pageTitle . ' is not in Page: namespace' );
58        }
59        $cacheKey = $pageTitle->getDBkey();
60        if ( !array_key_exists( $cacheKey, $this->cache ) ) {
61            $this->fetchQualityLevelForPageTitles( [ $pageTitle ] );
62        }
63        return $this->cache[$cacheKey];
64    }
65
66    /**
67     * @inheritDoc
68     */
69    public function prefetchQualityLevelForTitles( array $pageTitles ) {
70        $pageTitles = array_filter( $pageTitles, function ( $pageTitle ) {
71            return $pageTitle instanceof Title &&
72                $pageTitle->inNamespace( $this->pageNamespaceId ) &&
73                !array_key_exists( $pageTitle->getDBkey(), $this->cache );
74        } );
75
76        $this->fetchQualityLevelForPageTitles( $pageTitles );
77    }
78
79    /**
80     * @param Title[] $pageTitles
81     */
82    private function fetchQualityLevelForPageTitles( array $pageTitles ) {
83        // We set to unknown all qualities
84        foreach ( $pageTitles as $pageTitle ) {
85            $this->cache[$pageTitle->getDBkey()] = null;
86        }
87
88        $this->fetchQualityLevelForPageTitlesFromPageProperties( $pageTitles );
89        $this->fetchQualityLevelForPageTitlesFromPageCategories(
90            $this->filterPagesWithoutKnownQuality( $pageTitles )
91        );
92    }
93
94    /**
95     * @param Title[] $pageTitles
96     */
97    private function fetchQualityLevelForPageTitlesFromPageProperties( array $pageTitles ) {
98        if ( !$pageTitles ) {
99            return;
100        }
101
102        $dbr = MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->getReplicaDatabase();
103        $results = $dbr->select(
104            [ 'page_props', 'page' ],
105            [ 'page_title', 'pp_value' ],
106            [
107                'pp_propname' => 'proofread_page_quality_level',
108                'page_id = pp_page',
109                'page_namespace' => $this->pageNamespaceId,
110                'page_title' => array_map( static function ( Title $pageTitle ) {
111                    return $pageTitle->getDBkey();
112                }, $pageTitles )
113            ],
114            __METHOD__
115        );
116        foreach ( $results as $result ) {
117            $this->cache[$result->page_title] = intval( $result->pp_value );
118        }
119    }
120
121    /**
122     * @param Title[] $pageTitles
123     */
124    private function fetchQualityLevelForPageTitlesFromPageCategories( array $pageTitles ) {
125        if ( !$pageTitles ) {
126            return;
127        }
128
129        $pageDbKeys = array_map( static function ( Title $title ){
130            return $title->getDBkey();
131        }, $pageTitles );
132
133        $dbr = MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->getReplicaDatabase();
134        foreach ( $this->getCategoryForQualityLevels() as $qualityLevel => $qualityCategory ) {
135            $results = $dbr->select(
136                [ 'categorylinks', 'page' ],
137                [ 'page_title' ],
138                [
139                    'page_id = cl_from',
140                    'page_title' => $pageDbKeys,
141                    'cl_to' => $qualityCategory->getDBkey(),
142                    'page_namespace' => $this->pageNamespaceId
143                ],
144                __METHOD__
145            );
146
147            foreach ( $results as $result ) {
148                $this->cache[$result->page_title] = $qualityLevel;
149            }
150        }
151    }
152
153    /**
154     * @param array $pageTitles
155     * @return array
156     */
157    private function filterPagesWithoutKnownQuality( array $pageTitles ) {
158        return array_filter( $pageTitles, function ( Title $pageTitle ) {
159            return $this->cache[$pageTitle->getDBkey()] === null;
160        } );
161    }
162
163    /**
164     * @return array categoryForQualityLevel
165     */
166    private function getCategoryForQualityLevels() {
167        if ( $this->categoryForQualityLevel === null ) {
168            $this->categoryForQualityLevel = $this->computeCategoryForQualityLevels();
169        }
170        return $this->categoryForQualityLevel;
171    }
172
173    private function computeCategoryForQualityLevels() {
174        $qualityCategories = [];
175        for ( $qualityLevel = 0; $qualityLevel <= 4; $qualityLevel++ ) {
176            $categoryTitle = Title::makeTitleSafe(
177                NS_CATEGORY,
178                wfMessage( "proofreadpage_quality{$qualityLevel}_category" )->inContentLanguage()->text()
179            );
180            if ( $categoryTitle !== null ) {
181                $qualityCategories[$qualityLevel] = $categoryTitle;
182            }
183        }
184        return $qualityCategories;
185    }
186}