Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
60.00% covered (warning)
60.00%
3 / 5
CRAP
91.30% covered (success)
91.30%
42 / 46
ItemNotabilityFilter
0.00% covered (danger)
0.00%
0 / 1
60.00% covered (warning)
60.00%
3 / 5
17.19
91.30% covered (success)
91.30%
42 / 46
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 getNotableEntityIds
0.00% covered (danger)
0.00%
0 / 1
8.15
86.67% covered (warning)
86.67%
13 / 15
 getPagePropsByItem
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
7 / 7
 selectPagePropsPage
0.00% covered (danger)
0.00%
0 / 1
3.02
86.67% covered (warning)
86.67%
13 / 15
 getItemsWithoutArticle
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
4 / 4
<?php
namespace ArticlePlaceholder;
use Wikibase\DataModel\Entity\ItemId;
use Wikibase\Lib\Store\EntityNamespaceLookup;
use Wikibase\Lib\Store\SiteLinkLookup;
use Wikimedia\Rdbms\FakeResultWrapper;
use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\IResultWrapper;
use Wikimedia\Rdbms\SessionConsistentConnectionManager;
/**
 * Filter a list of items by article placeholder notability.
 *
 * @license GPL-2.0-or-later
 * @author Lucie-Aimée Kaffee
 * @author Marius Hoch
 */
class ItemNotabilityFilter {
    /**
     * Minimum number of statements for an item to be notable
     */
    private const MIN_STATEMENTS = 3;
    /**
     * Minimum number of sitelinks for an item to be notable
     */
    private const MIN_SITELINKS = 2;
    /**
     * @var SessionConsistentConnectionManager
     */
    private $connectionManager;
    /**
     * @var EntityNamespaceLookup
     */
    private $entityNamespaceLookup;
    /**
     * @var SiteLinkLookup
     */
    private $siteLinkLookup;
    /**
     * @var string
     */
    private $siteGlobalId;
    /**
     * @param SessionConsistentConnectionManager $connectionManager
     * @param EntityNamespaceLookup $entityNamespaceLookup
     * @param SiteLinkLookup $siteLinkLookup
     * @param string $siteGlobalId
     */
    public function __construct(
        SessionConsistentConnectionManager $connectionManager,
        EntityNamespaceLookup $entityNamespaceLookup,
        SiteLinkLookup $siteLinkLookup,
        $siteGlobalId
    ) {
        $this->connectionManager = $connectionManager;
        $this->entityNamespaceLookup = $entityNamespaceLookup;
        $this->siteLinkLookup = $siteLinkLookup;
        $this->siteGlobalId = $siteGlobalId;
    }
    /**
     * @param ItemId[] $itemIds
     *
     * @return ItemId[]
     */
    public function getNotableEntityIds( array $itemIds ) {
        if ( $itemIds === [] ) {
            return [];
        }
        $byNumericId = [];
        $pagePropsByItem = $this->getPagePropsByItem( $itemIds );
        foreach ( $itemIds as $itemId ) {
            $itemIdSerialization = $itemId->getSerialization();
            if ( !isset( $pagePropsByItem[$itemIdSerialization] ) ) {
                continue;
            }
            $pageProps = $pagePropsByItem[$itemIdSerialization];
            if (
                isset( $pageProps['wb-claims'] ) &&
                isset( $pageProps['wb-sitelinks'] ) &&
                $pageProps['wb-claims'] >= self::MIN_STATEMENTS &&
                $pageProps['wb-sitelinks'] >= self::MIN_SITELINKS
            ) {
                $byNumericId[$itemId->getNumericId()] = $itemId;
            }
        }
        return $this->getItemsWithoutArticle( $byNumericId );
    }
    /**
     * @param ItemId[] $itemIds
     *
     * @return int[][] Map of page_title => propname => numeric value
     */
    private function getPagePropsByItem( array $itemIds ) {
        $values = [];
        $dbr = $this->connectionManager->getReadConnection();
        $res = $this->selectPagePropsPage( $dbr, $itemIds );
        $this->connectionManager->releaseConnection( $dbr );
        foreach ( $res as $row ) {
            $values[$row->page_title][$row->pp_propname] = intval( $row->pp_value ?: 0 );
        }
        return $values;
    }
    /**
     * @param IDatabase $dbr
     * @param ItemId[] $itemIds
     *
     * @return IResultWrapper
     */
    private function selectPagePropsPage( IDatabase $dbr, array $itemIds ) {
        $entityNamespace = $this->entityNamespaceLookup->getEntityNamespace( 'item' );
        if ( !is_int( $entityNamespace ) ) {
            wfLogWarning( 'The ArticlePlaceholder extension requires an "item" namespace' );
            return new FakeResultWrapper( [] );
        }
        $itemIdSerializations = [];
        foreach ( $itemIds as $itemId ) {
            $itemIdSerializations[] = $itemId->getSerialization();
        }
        return $dbr->select(
            [ 'page_props', 'page' ],
            [ 'page_title', 'pp_propname', 'pp_value' ],
            [
                'page_namespace' => $entityNamespace,
                'page_title' => $itemIdSerializations,
                'pp_propname' => [ 'wb-sitelinks', 'wb-claims' ]
            ],
            __METHOD__,
            [],
            [ 'page' => [ 'LEFT JOIN', 'page_id=pp_page' ] ]
        );
    }
    /**
     * @param ItemId[] $itemIds expected to be indexed by numeric item ID
     *
     * @return ItemId[]
     */
    private function getItemsWithoutArticle( array $itemIds ) {
        $links = $this->siteLinkLookup->getLinks( array_keys( $itemIds ), [ $this->siteGlobalId ] );
        foreach ( $links as [ , , $numericId ] ) {
            unset( $itemIds[$numericId] );
        }
        return array_values( $itemIds );
    }
}