jenkins-bot has submitted this change and it was merged.

Change subject: Introduce PageImagesDataUpdate for PageImages extension
......................................................................


Introduce PageImagesDataUpdate for PageImages extension

Bug: T112865
Change-Id: I5fba0e4a87d3034ca7d63ae9332eeb5f9b3bf449
---
M docs/options.wiki
M repo/config/Wikibase.default.php
A repo/includes/DataUpdates/PageImagesDataUpdate.php
M repo/includes/EntityParserOutputGenerator.php
A repo/tests/phpunit/includes/DataUpdates/PageImagesDataUpdateTest.php
5 files changed, 352 insertions(+), 0 deletions(-)

Approvals:
  Daniel Kinzler: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/docs/options.wiki b/docs/options.wiki
index 0af7d25..83e81da 100644
--- a/docs/options.wiki
+++ b/docs/options.wiki
@@ -46,6 +46,7 @@
 ;dataRightsUrl: Url to link to license for data contents. Defaults to 
$wgRightsUrl setting.
 ;dataRightsText: Text for data license link. Defaults to $wgRightsText setting.
 ;badgeItems: Items allowed to be used as badges. This setting expects an array 
of serialized item ids pointing to their CSS class names, like <code>array( 
'Q101' => 'wb-badge-goodarticle' )</code>. With this class name it is possible 
to change the icon of a specific badge.
+;preferredPageImagesProperties: List of image property id strings, in order of 
preference, that should be considered for the <code>page_image</code> page 
property. Defaults to an empty array.
 ;conceptBaseUri: Base URI for building concept URIs (for example used in Rdf 
output). This has to include the protocol and domain, only an entity identifier 
will be appended.
 
 === Expert Settings ===
diff --git a/repo/config/Wikibase.default.php b/repo/config/Wikibase.default.php
index dd84ee2..863bb9c 100644
--- a/repo/config/Wikibase.default.php
+++ b/repo/config/Wikibase.default.php
@@ -38,6 +38,10 @@
                // Items allowed to be used as badges pointing to their CSS 
class names
                'badgeItems' => array(),
 
+               // List of image property id strings, in order of preference, 
that should be considered for
+               // the "page_image" page property.
+               'preferredPageImagesProperties' => array(),
+
                // Number of seconds for which data output shall be cached.
                // Note: keep that low, because such caches cannot always be 
purged easily.
                'dataSquidMaxage' => $wgSquidMaxage,
diff --git a/repo/includes/DataUpdates/PageImagesDataUpdate.php 
b/repo/includes/DataUpdates/PageImagesDataUpdate.php
new file mode 100644
index 0000000..43f6eb9
--- /dev/null
+++ b/repo/includes/DataUpdates/PageImagesDataUpdate.php
@@ -0,0 +1,195 @@
+<?php
+
+namespace Wikibase\Repo\DataUpdates;
+
+use DataValues\StringValue;
+use Wikibase\DataModel\Entity\PropertyId;
+use Wikibase\DataModel\Snak\PropertyValueSnak;
+use Wikibase\DataModel\Snak\Snak;
+use Wikibase\DataModel\Statement\Statement;
+use Wikibase\DataModel\Statement\StatementList;
+
+/**
+ * Code to make the PageImages extension aware of pages in the Wikibase 
namespaces.
+ *
+ * @since 0.5
+ *
+ * @licence GNU GPL v2+
+ * @author Thiemo Mättig
+ */
+class PageImagesDataUpdate {
+
+       /**
+        * @var int[] Hash table of image property id strings pointing to 
priorities (smaller numbers
+        * are better).
+        */
+       private $propertyPriorities = array();
+
+       /**
+        * @var string
+        */
+       private $bestProperty;
+
+       /**
+        * @var int
+        */
+       private $bestRank;
+
+       /**
+        * @var string
+        */
+       private $bestFileName;
+
+       /**
+        * @param string[] $imagePropertyIds List of image property id strings, 
in order of preference.
+        */
+       public function __construct( array $imagePropertyIds ) {
+               $this->setPropertyIds( $imagePropertyIds );
+       }
+
+       /**
+        * @param string[] $ids
+        */
+       private function setPropertyIds( array $ids ) {
+               $this->propertyPriorities = array_flip( array_unique( 
array_values( $ids ) ) );
+       }
+
+       /**
+        * @param StatementList $statements
+        *
+        * @return string|null The file's page name without the NS_FILE 
namespace, or null if not found.
+        */
+       public function getBestImageFileName( StatementList $statements ) {
+               $this->bestProperty = null;
+               $this->bestRank = null;
+               $this->bestFileName = null;
+
+               foreach ( $statements->toArray() as $statement ) {
+                       $this->processStatement( $statement );
+               }
+
+               return $this->bestFileName;
+       }
+
+       /**
+        * @param Statement $statement
+        */
+       public function processStatement( Statement $statement ) {
+               $this->processSnak(
+                       $statement->getMainSnak(),
+                       $statement->getPropertyId(),
+                       $statement->getRank()
+               );
+       }
+
+       /**
+        * @param Snak $snak
+        * @param PropertyId $propertyId
+        * @param int $rank
+        */
+       private function processSnak(
+               Snak $snak,
+               PropertyId $propertyId,
+               $rank = Statement::RANK_NORMAL
+       ) {
+               $id = $propertyId->getSerialization();
+               $fileName = str_replace( ' ', '_', $this->getString( $snak ) );
+
+               if ( $fileName === null || $fileName === '' ) {
+                       return;
+               }
+
+               if ( !$this->isAcceptableRank( $rank ) ) {
+                       return;
+               }
+
+               if ( !$this->isAcceptablePriority( $id ) ) {
+                       return;
+               }
+
+               if ( $this->isSamePriority( $id ) && !$this->isBetterRank( 
$rank ) ) {
+                       return;
+               }
+
+               $this->bestProperty = $id;
+               $this->bestRank = $rank;
+               $this->bestFileName = $fileName;
+       }
+
+       /**
+        * @param Snak $snak
+        *
+        * @return string|null
+        */
+       private function getString( Snak $snak ) {
+               if ( $snak instanceof PropertyValueSnak ) {
+                       $value = $snak->getDataValue();
+
+                       if ( $value instanceof StringValue ) {
+                               return $value->getValue();
+                       }
+               }
+
+               return null;
+       }
+
+       /**
+        * @param string $propertyId
+        *
+        * @return bool True if the property is configured as one of the image 
properties and it's
+        * priority is equal or better than the current best.
+        */
+       private function isAcceptablePriority( $propertyId ) {
+               if ( !array_key_exists( $propertyId, $this->propertyPriorities 
) ) {
+                       return false;
+               }
+
+               if ( $this->bestProperty === null ) {
+                       return true;
+               }
+
+               $priority = $this->propertyPriorities[$propertyId];
+               $bestPriority = $this->propertyPriorities[$this->bestProperty];
+               return $priority <= $bestPriority;
+       }
+
+       /**
+        * @param string $propertyId
+        *
+        * @return bool True if the property's priority is identical to the 
current best.
+        */
+       private function isSamePriority( $propertyId ) {
+               if ( $this->bestProperty === null ) {
+                       return false;
+               }
+
+               $priority = $this->propertyPriorities[$propertyId];
+               $bestPriority = $this->propertyPriorities[$this->bestProperty];
+               return $priority === $bestPriority;
+       }
+
+       /**
+        * @param int $rank
+        *
+        * @return bool True if the rank is not deprecated.
+        */
+       private function isAcceptableRank( $rank ) {
+               return $rank !== Statement::RANK_DEPRECATED;
+       }
+
+       /**
+        * @param int $rank
+        *
+        * @return bool
+        */
+       private function isBetterRank( $rank ) {
+               if ( $this->bestRank === null ) {
+                       // Everything is better than nothing.
+                       return true;
+               }
+
+               // Ranks are guaranteed to be in increasing, numerical order.
+               return $rank > $this->bestRank;
+       }
+
+}
diff --git a/repo/includes/EntityParserOutputGenerator.php 
b/repo/includes/EntityParserOutputGenerator.php
index 0402924..ddb3fc5 100644
--- a/repo/includes/EntityParserOutputGenerator.php
+++ b/repo/includes/EntityParserOutputGenerator.php
@@ -15,6 +15,7 @@
 use Wikibase\DataModel\SiteLink;
 use Wikibase\DataModel\SiteLinkList;
 use Wikibase\DataModel\Snak\Snak;
+use Wikibase\DataModel\Statement\StatementList;
 use Wikibase\DataModel\Statement\StatementListProvider;
 use Wikibase\DataModel\Term\FingerprintProvider;
 use Wikibase\Lib\Store\EntityInfo;
@@ -22,8 +23,10 @@
 use Wikibase\Lib\Store\EntityInfoTermLookup;
 use Wikibase\Lib\Store\EntityTitleLookup;
 use Wikibase\Lib\Store\LanguageFallbackLabelDescriptionLookup;
+use Wikibase\Repo\DataUpdates\PageImagesDataUpdate;
 use Wikibase\Repo\LinkedData\EntityDataFormatProvider;
 use Wikibase\Repo\View\RepoSpecialPageLinker;
+use Wikibase\Repo\WikibaseRepo;
 use Wikibase\View\EmptyEditSectionGenerator;
 use Wikibase\View\EntityViewFactory;
 use Wikibase\View\Template\TemplateFactory;
@@ -129,6 +132,7 @@
         * @param ParserOptions $options
         * @param bool $generateHtml
         *
+        * @throws InvalidArgumentException
         * @return ParserOutput
         */
        public function getParserOutput(
@@ -172,6 +176,7 @@
                $configVars = $this->configBuilder->build( $entity );
                $parserOutput->addJsConfigVars( $configVars );
 
+               $this->addBestImageToParserOutput( $parserOutput, 
$entity->getStatements() );
                $this->addLinksToParserOutput( $parserOutput, $usedEntityIds, 
$snaks );
 
                // FIXME: OCP violation - 
https://phabricator.wikimedia.org/T75495
@@ -210,6 +215,19 @@
                return $parserOutput;
        }
 
+       private function addBestImageToParserOutput( ParserOutput 
$parserOutput, StatementList $statements ) {
+               $repo = WikibaseRepo::getDefaultInstance();
+               // TODO: Inject this setting!
+               $propertyIds = $repo->getSettings()->getSetting( 
'preferredPageImagesProperties' );
+
+               if ( !empty( $propertyIds ) ) {
+                       $dataUpdate = new PageImagesDataUpdate( $propertyIds );
+                       $fileName = $dataUpdate->getBestImageFileName( 
$statements );
+                       // This property name is the only "soft dependency" on 
the PageImages extension.
+                       $parserOutput->setProperty( 'page_image', $fileName );
+               }
+       }
+
        /**
         * @param ParserOutput $parserOutput
         * @param EntityId[] $usedEntityIds
diff --git 
a/repo/tests/phpunit/includes/DataUpdates/PageImagesDataUpdateTest.php 
b/repo/tests/phpunit/includes/DataUpdates/PageImagesDataUpdateTest.php
new file mode 100644
index 0000000..6be288d
--- /dev/null
+++ b/repo/tests/phpunit/includes/DataUpdates/PageImagesDataUpdateTest.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace Wikibase\Repo\Tests\DataUpdates;
+
+use DataValues\BooleanValue;
+use DataValues\StringValue;
+use PHPUnit_Framework_TestCase;
+use Wikibase\DataModel\Snak\PropertyNoValueSnak;
+use Wikibase\DataModel\Snak\PropertySomeValueSnak;
+use Wikibase\DataModel\Snak\PropertyValueSnak;
+use Wikibase\DataModel\Statement\Statement;
+use Wikibase\DataModel\Statement\StatementList;
+use Wikibase\Repo\DataUpdates\PageImagesDataUpdate;
+
+/**
+ * @covers Wikibase\Repo\DataUpdates\PageImagesDataUpdate
+ *
+ * @since 0.5
+ *
+ * @group WikibaseRepo
+ * @group Wikibase
+ *
+ * @license GNU GPL v2+
+ * @author Thiemo Mättig
+ */
+class PageImagesDataUpdateTest extends PHPUnit_Framework_TestCase {
+
+       /**
+        * @param string[] $propertyIds
+        *
+        * @return PageImagesDataUpdate
+        */
+       private function newInstance( array $propertyIds ) {
+               return new PageImagesDataUpdate( $propertyIds );
+       }
+
+       /**
+        * @param StatementList $statements
+        * @param int $propertyId
+        * @param string $string
+        * @param int $rank
+        */
+       private function addStatement(
+               StatementList $statements,
+               $propertyId,
+               $string,
+               $rank = Statement::RANK_NORMAL
+       ) {
+               $statement = new Statement(
+                       new PropertyValueSnak( $propertyId, new StringValue( 
$string ) )
+               );
+               $statement->setRank( $rank );
+               $statements->addStatement( $statement );
+       }
+
+       /**
+        * @dataProvider constructorArgumentsProvider
+        */
+       public function testConstructor( $propertyIds ) {
+               $instance = $this->newInstance( $propertyIds );
+               $this->assertInstanceOf( 
'Wikibase\Repo\DataUpdates\PageImagesDataUpdate', $instance );
+       }
+
+       public function constructorArgumentsProvider() {
+               return array(
+                       'Empty' => array( array() ),
+                       'Property ids' => array( array( 'P1', 'P9999' ) ),
+                       'Non-property ids' => array( array( 'Q1' ) ),
+                       'Invalid ids' => array( array( 'invalid' ) ),
+               );
+       }
+
+       /**
+        * @dataProvider bestImageProvider
+        */
+       public function testGetBestImageFileName(
+               StatementList $statements,
+               array $propertyIds,
+               $expected
+       ) {
+               $instance = $this->newInstance( $propertyIds );
+               $this->assertSame( $expected, $instance->getBestImageFileName( 
$statements ) );
+       }
+
+       public function bestImageProvider() {
+               $statements = new StatementList();
+
+               $this->addStatement( $statements, 1, '1.jpg' );
+
+               $statements->addNewStatement( new PropertyNoValueSnak( 2 ) );
+               $statements->addNewStatement( new PropertySomeValueSnak( 2 ) );
+               $statements->addNewStatement( new PropertyValueSnak( 2, new 
BooleanValue( true ) ) );
+               $this->addStatement( $statements, 2, '' );
+               $this->addStatement( $statements, 2, '2.jpg', 
Statement::RANK_DEPRECATED );
+
+               $statements->addNewStatement( new PropertySomeValueSnak( 3 ) );
+               $this->addStatement( $statements, 3, '3a.jpg' );
+               $this->addStatement( $statements, 3, '3b.jpg' );
+
+               $this->addStatement( $statements, 4, 'Four 1.jpg', 
Statement::RANK_DEPRECATED );
+               $this->addStatement( $statements, 4, 'Four 2.jpg' );
+               $this->addStatement( $statements, 4, 'Four 3.jpg' );
+
+               $this->addStatement( $statements, 5, '5a.jpg' );
+               $this->addStatement( $statements, 4, '5b.jpg', 
Statement::RANK_DEPRECATED );
+               $this->addStatement( $statements, 5, '5c.jpg', 
Statement::RANK_PREFERRED );
+               $this->addStatement( $statements, 5, '5d.jpg' );
+               $this->addStatement( $statements, 5, '5e.jpg', 
Statement::RANK_PREFERRED );
+
+               return array(
+                       // Find nothing for various reasons.
+                       'Ignore non-strings' => array( $statements, array( 'P2' 
), null ),
+                       'Property not found' => array( $statements, array( 
'P9999' ), null ),
+                       'Not a property id' => array( $statements, array( 'Q1' 
), null ),
+                       'Invalid id' => array( $statements, array( 'invalid' ), 
null ),
+                       'Ignore misconfiguration' => array( $statements, array( 
'P1', 'P2', 'P1' ), '1.jpg' ),
+                       'Ignore keys' => array( $statements, array( 2 => 'P1', 
1 => 'P2' ), '1.jpg' ),
+
+                       // Simple searches.
+                       'Find 1' => array( $statements, array( 'P1' ), '1.jpg' 
),
+                       'Skip non-strings' => array( $statements, array( 'P3' 
), '3a.jpg' ),
+                       'Skip missing ids' => array( $statements, array( 
'P9999', 'P1' ), '1.jpg' ),
+                       'Skip item ids' => array( $statements, array( 'Q1', 
'P1' ), '1.jpg' ),
+                       'Skip invalid ids' => array( $statements, array( 
'invalid', 'P1' ), '1.jpg' ),
+
+                       'Increasing order' => array( $statements, array( 'P1', 
'P2', 'P3' ), '1.jpg' ),
+                       'Decreasing order' => array( $statements, array( 'P3', 
'P2', 'P1' ), '3a.jpg' ),
+
+                       'Skip deprecated' => array( $statements, array( 'P4' ), 
'Four_2.jpg' ),
+                       'Prefer preferred' => array( $statements, array( 'P5' 
), '5c.jpg' ),
+               );
+       }
+
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/243673
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I5fba0e4a87d3034ca7d63ae9332eeb5f9b3bf449
Gerrit-PatchSet: 7
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Thiemo Mättig (WMDE) <[email protected]>
Gerrit-Reviewer: Aude <[email protected]>
Gerrit-Reviewer: Daniel Kinzler <[email protected]>
Gerrit-Reviewer: Hoo man <[email protected]>
Gerrit-Reviewer: Jonas Kress (WMDE) <[email protected]>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to