Daniel Kinzler has uploaded a new change for review. https://gerrit.wikimedia.org/r/216745
Change subject: Track quantity units as referenced entities. ...................................................................... Track quantity units as referenced entities. NOTE: requires a new release of data-value/number, see https://github.com/DataValues/Number/pull/31 Change-Id: Id86dba7dfa2a46c9c7b4a94457b4f8a04f58984e --- M client/includes/WikibaseClient.php M lib/includes/ReferencedEntitiesFinder.php A lib/includes/parsers/ExtractingEntityIdParser.php M lib/tests/phpunit/ReferencedEntitiesFinderTest.php A lib/tests/phpunit/parsers/ExtractingEntityIdParserTest.php M repo/includes/EntityParserOutputGenerator.php M repo/includes/EntityParserOutputGeneratorFactory.php M repo/includes/WikibaseRepo.php M repo/tests/phpunit/includes/EntityParserOutputGeneratorTest.php 9 files changed, 236 insertions(+), 10 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase refs/changes/45/216745/1 diff --git a/client/includes/WikibaseClient.php b/client/includes/WikibaseClient.php index d9609d1..7ddbf8e 100644 --- a/client/includes/WikibaseClient.php +++ b/client/includes/WikibaseClient.php @@ -46,6 +46,7 @@ use Wikibase\Lib\LanguageNameLookup; use Wikibase\Lib\OutputFormatSnakFormatterFactory; use Wikibase\Lib\OutputFormatValueFormatterFactory; +use Wikibase\Lib\Parsers\ExtractingEntityIdParser; use Wikibase\Lib\PropertyInfoDataTypeLookup; use Wikibase\Lib\Serializers\ForbiddenSerializer; use Wikibase\Lib\Store\EntityContentDataCodec; @@ -519,7 +520,18 @@ return new WikibaseValueFormatterBuilders( $this->contentLanguage, new FormatterLabelDescriptionLookupFactory( $this->getTermLookup() ), - new LanguageNameLookup() + new LanguageNameLookup(), + $this->getRepoEntityUriParser() + ); + } + + /** + * @return ExtractingEntityIdParser + */ + private function getRepoEntityUriParser() { + return ExtractingEntityIdParser::newFrombaseUri( + $this->getSettings()->getSetting( 'conceptBaseUri' ), + $this->getEntityIdParser() ); } diff --git a/lib/includes/ReferencedEntitiesFinder.php b/lib/includes/ReferencedEntitiesFinder.php index b748abd..8a9f89c 100644 --- a/lib/includes/ReferencedEntitiesFinder.php +++ b/lib/includes/ReferencedEntitiesFinder.php @@ -2,7 +2,11 @@ namespace Wikibase; +use DataValues\DataValue; +use DataValues\QuantityValue; use Wikibase\DataModel\Entity\EntityId; +use Wikibase\DataModel\Entity\EntityIdParser; +use Wikibase\DataModel\Entity\EntityIdParsingException; use Wikibase\DataModel\Entity\EntityIdValue; use Wikibase\DataModel\Snak\PropertyValueSnak; use Wikibase\DataModel\Snak\Snak; @@ -22,6 +26,18 @@ class ReferencedEntitiesFinder { /** + * @var EntityIdParser + */ + private $entityUriParser; + + /** + * @param EntityIdParser $entityUriParser + */ + public function __construct( EntityIdParser $entityUriParser ) { + $this->entityUriParser = $entityUriParser; + } + + /** * Finds linked entities within a set of snaks. * * @since 0.4 @@ -39,15 +55,42 @@ if ( $snak instanceof PropertyValueSnak ) { $dataValue = $snak->getDataValue(); - - if ( $dataValue instanceof EntityIdValue ) { - $entityId = $dataValue->getEntityId(); - $entityIds[$entityId->getSerialization()] = $entityId; - } + $this->addEntityIdsFromValue( $dataValue, $entityIds ); } } return $entityIds; } + /** + * @param DataValue $dataValue + * @param EntityId[] $entityIds + */ + private function addEntityIdsFromValue( DataValue $dataValue, array &$entityIds ) { + if ( $dataValue instanceof EntityIdValue ) { + $entityId = $dataValue->getEntityId(); + $entityIds[$entityId->getSerialization()] = $entityId; + } elseif ( $dataValue instanceof QuantityValue ) { + $unitUri = $dataValue->getUnit(); + $this->addEntityIdsFromURI( $unitUri, $entityIds ); + } + + //TODO: EntityIds from GlobeCoordinateValue's globe URI (Wikidata, not local item URI!) + //TODO: EntityIds from TimeValue's calendar URI (Wikidata, not local item URI!) + } + + /** + * @param string $uri + * @param EntityId[] $entityIds + */ + private function addEntityIdsFromURI( $uri, array &$entityIds ) { + try { + $entityId = $this->entityUriParser->parse( $uri ); + $entityIds[$entityId->getSerialization()] = $entityId; + } catch ( EntityIdParsingException $ex ) { + // noop + } + } + + } diff --git a/lib/includes/parsers/ExtractingEntityIdParser.php b/lib/includes/parsers/ExtractingEntityIdParser.php new file mode 100644 index 0000000..80f09c8 --- /dev/null +++ b/lib/includes/parsers/ExtractingEntityIdParser.php @@ -0,0 +1,65 @@ +<?php + +namespace Wikibase\Lib\Parsers; + +use Wikibase\DataModel\Entity\EntityId; +use Wikibase\DataModel\Entity\EntityIdParser; +use Wikibase\DataModel\Entity\EntityIdParsingException; + +/** + * EntityIdParser that parses full entity URIs into EntityIds. + * + * @since 0.5 + * + * @license GPL 2+ + * @author Daniel Kinzler + */ +class ExtractingEntityIdParser implements EntityIdParser { + + public static function newFromBaseUri( $baseUri, EntityIdParser $idParser ) { + $escapedUri = preg_quote( $baseUri, '!' ); + $uriRegexp = '!^' . $escapedUri . '(.+)$!'; + + return new ExtractingEntityIdParser( $uriRegexp, $idParser ); + } + + /** + * @var string + */ + private $uriRegexp; + + /** + * @var EntityIdParser + */ + private $idParser; + + /** + * @param string $uriRegexp Regular expression matching the entity ID as the first capture group. + * @param EntityIdParser $idParser + */ + public function __construct( $uriRegexp, EntityIdParser $idParser ) { + $this->idParser = $idParser; + $this->uriRegexp = $uriRegexp; + } + + /** + * @since 0.5 + * + * @param string $entityUri + * + * @return EntityId + * @throws EntityIdParsingException + */ + public function parse( $entityUri ) { + if ( preg_match ( $this->uriRegexp, $entityUri, $m ) ) { + if ( !isset( $m[1] ) ) { + throw new EntityIdParsingException( 'Bad entity URI (incomplete match): ' . $entityUri ); + } + + return $this->idParser->parse( $m[1] ); + } + + throw new EntityIdParsingException( 'Bad entity URI: ' . $entityUri ); + } + +} diff --git a/lib/tests/phpunit/ReferencedEntitiesFinderTest.php b/lib/tests/phpunit/ReferencedEntitiesFinderTest.php index 7313185..844df26 100644 --- a/lib/tests/phpunit/ReferencedEntitiesFinderTest.php +++ b/lib/tests/phpunit/ReferencedEntitiesFinderTest.php @@ -2,7 +2,9 @@ namespace Wikibase\Lib\Test; +use DataValues\QuantityValue; use DataValues\StringValue; +use Wikibase\DataModel\Entity\BasicEntityIdParser; use Wikibase\DataModel\Entity\EntityId; use Wikibase\DataModel\Entity\EntityIdValue; use Wikibase\DataModel\Entity\ItemId; @@ -11,6 +13,7 @@ use Wikibase\DataModel\Snak\PropertySomeValueSnak; use Wikibase\DataModel\Snak\PropertyValueSnak; use Wikibase\DataModel\Snak\Snak; +use Wikibase\Lib\Parsers\ExtractingEntityIdParser; use Wikibase\ReferencedEntitiesFinder; /** @@ -32,9 +35,11 @@ $p11 = new PropertyId( 'p11' ); $p27 = new PropertyId( 'p27' ); $p44 = new PropertyId( 'p44' ); + $q800 = new ItemId( 'Q800' ); $q23Value = new EntityIdValue( new ItemId( 'q23' ) ); $q24Value = new EntityIdValue( new ItemId( 'q24' ) ); + $quantityValueUnitQ800 = QuantityValue::newFromNumber( 3, 'http://acme.test/entity/Q800' ); $argLists[] = array( array(), @@ -63,7 +68,13 @@ $argLists[] = array( array( new PropertyValueSnak( $p27, $q23Value ) ), array( $p27, $q23Value->getEntityId() ), - "PropertyValueSnak with EntityId" + "PropertyValueSnak with EntityIdValue" + ); + + $argLists[] = array( + array( new PropertyValueSnak( $p27, $quantityValueUnitQ800 ) ), + array( $p27, $q800 ), + "PropertyValueSnak with unit URI in a QuantityValue" ); $argLists[] = array( @@ -89,7 +100,11 @@ * @param string $message */ public function testFindSnakLinks( array $snaks, array $expected, $message ) { - $linkFinder = new ReferencedEntitiesFinder(); + $linkFinder = new ReferencedEntitiesFinder( + ExtractingEntityIdParser::newFromBaseUri( + 'http://acme.test/entity/', + new BasicEntityIdParser() + ) ); $actual = $linkFinder->findSnakLinks( $snaks ); diff --git a/lib/tests/phpunit/parsers/ExtractingEntityIdParserTest.php b/lib/tests/phpunit/parsers/ExtractingEntityIdParserTest.php new file mode 100644 index 0000000..1408af4 --- /dev/null +++ b/lib/tests/phpunit/parsers/ExtractingEntityIdParserTest.php @@ -0,0 +1,66 @@ +<?php + +namespace Wikibase\Lib\Test; + +use Wikibase\DataModel\Entity\BasicEntityIdParser; +use Wikibase\DataModel\Entity\ItemId; +use Wikibase\DataModel\Entity\PropertyId; +use Wikibase\Lib\Parsers\ExtractingEntityIdParser; + +/** + * @covers Wikibase\Lib\Parsers\ExtractingEntityIdParser + * + * @group ValueParsers + * @group WikibaseLib + * @group Wikibase + * + * @licence GNU GPL v2+ + * @author Daniel Kinzler + */ +class ExtractingEntityIdParserTest extends \PHPUnit_Framework_TestCase { + + public function provideParse() { + return array( + array( '!^https?://acme.test/entity/(.*)$!', 'http://acme.test/entity/Q14', new ItemId( 'Q14' ) ), + array( '!^(?:https?)://acme.test/entity/(.*?)(#.*)?$!', 'http://acme.test/entity/P14', new PropertyId( 'P14' ) ), + ); + } + + /** + * @dataProvider provideParse + */ + public function testParse( $regexp, $input, $expected ) { + $parser = new ExtractingEntityIdParser( $regexp, new BasicEntityIdParser() ); + $this->assertEquals( new ItemId( 'Q14' ), $parser->parse( 'http://acme.test/entity/Q14' ) ); + } + + public function provideParse_invalid() { + return array( + array( '!^https?://acme.test/entity/(.*)$!', 'http://acme.test/Q14' ), + array( '!^https?://acme.test/entity/(.*)$!', 'http://acme.test/entity/XYYZ' ), + array( '!^https?://acme.test/entity/(.*)$!', 'http://acme.test/Q14#x' ), + array( '!^https?://acme.test/entity/.*$!', 'http://acme.test/entity/Q14' ), + ); + } + + /** + * @dataProvider provideParse_invalid + */ + public function testParse_invalid( $regexp, $input ) { + $parser = new ExtractingEntityIdParser( $regexp, new BasicEntityIdParser() ); + + $this->setExpectedException( 'Wikibase\DataModel\Entity\EntityIdParsingException' ); + $parser->parse( $input ); + } + + public function testNewFromBaseUri() { + $parser = ExtractingEntityIdParser::newFromBaseUri( 'http://acme.test/entity/', new BasicEntityIdParser() ); + + $this->assertEquals( new ItemId( 'Q14' ), $parser->parse( 'http://acme.test/entity/Q14' ) ); + $this->assertEquals( new ItemId( 'Q14' ), $parser->parse( 'http://acme.test/entity/Q14' ), 'HTTPS' ); + + $this->setExpectedException( 'Wikibase\DataModel\Entity\EntityIdParsingException' ); + $parser->parse( 'http://acme.test/Q14' ); + } + +} diff --git a/repo/includes/EntityParserOutputGenerator.php b/repo/includes/EntityParserOutputGenerator.php index 5b5c378..23b0371 100644 --- a/repo/includes/EntityParserOutputGenerator.php +++ b/repo/includes/EntityParserOutputGenerator.php @@ -93,6 +93,7 @@ EntityInfoBuilderFactory $entityInfoBuilderFactory, LanguageFallbackChain $languageFallbackChain, $languageCode, + ReferencedEntitiesFinder $referencedEntitiesFinder, TemplateFactory $templateFactory ) { $this->entityViewFactory = $entityViewFactory; @@ -102,8 +103,7 @@ $this->entityInfoBuilderFactory = $entityInfoBuilderFactory; $this->languageFallbackChain = $languageFallbackChain; $this->languageCode = $languageCode; - - $this->referencedEntitiesFinder = new ReferencedEntitiesFinder(); + $this->referencedEntitiesFinder = $referencedEntitiesFinder; $this->templateFactory = $templateFactory; } diff --git a/repo/includes/EntityParserOutputGeneratorFactory.php b/repo/includes/EntityParserOutputGeneratorFactory.php index adc1ab2..fe3b5e9 100644 --- a/repo/includes/EntityParserOutputGeneratorFactory.php +++ b/repo/includes/EntityParserOutputGeneratorFactory.php @@ -44,6 +44,11 @@ private $valuesFinder; /** + * @var ReferencedEntitiesFinder + */ + private $referencedEntitiesFinder; + + /** * @var LanguageFallbackChainFactory */ private $languageFallbackChainFactory; @@ -54,6 +59,7 @@ EntityTitleLookup $entityTitleLookup, ValuesFinder $valuesFinder, LanguageFallbackChainFactory $languageFallbackChainFactory, + ReferencedEntitiesFinder $referencedEntitiesFinder, TemplateFactory $templateFactory ) { $this->entityViewFactory = $entityViewFactory; @@ -61,6 +67,7 @@ $this->entityTitleLookup = $entityTitleLookup; $this->valuesFinder = $valuesFinder; $this->languageFallbackChainFactory = $languageFallbackChainFactory; + $this->referencedEntitiesFinder = $referencedEntitiesFinder; $this->templateFactory = $templateFactory; } @@ -82,6 +89,7 @@ $this->entityInfoBuilderFactory, $this->getLanguageFallbackChain( $languageCode ), $languageCode, + $this->referencedEntitiesFinder, $this->templateFactory ); } diff --git a/repo/includes/WikibaseRepo.php b/repo/includes/WikibaseRepo.php index f71e551..47e955f 100644 --- a/repo/includes/WikibaseRepo.php +++ b/repo/includes/WikibaseRepo.php @@ -50,6 +50,7 @@ use Wikibase\Lib\Localizer\ParseExceptionLocalizer; use Wikibase\Lib\OutputFormatSnakFormatterFactory; use Wikibase\Lib\OutputFormatValueFormatterFactory; +use Wikibase\Lib\Parsers\ExtractingEntityIdParser; use Wikibase\Lib\PropertyInfoDataTypeLookup; use Wikibase\Lib\SnakConstructionService; use Wikibase\Lib\SnakFormatter; @@ -64,6 +65,7 @@ use Wikibase\Lib\WikibaseDataTypeBuilders; use Wikibase\Lib\WikibaseSnakFormatterBuilders; use Wikibase\Lib\WikibaseValueFormatterBuilders; +use Wikibase\ReferencedEntitiesFinder; use Wikibase\Repo\Content\EntityContentFactory; use Wikibase\Repo\Content\ItemHandler; use Wikibase\Repo\Content\PropertyHandler; @@ -574,6 +576,16 @@ } /** + * @return EntityIdParser + */ + private function getLocalEntityUriParser() { + return ExtractingEntityIdParser::newFrombaseUri( + $this->getSettings()->getSetting( 'conceptBaseUri' ), + $this->getEntityIdParser() + ); + } + + /** * @return OutputFormatSnakFormatterFactory */ protected function newSnakFormatterFactory() { @@ -1064,6 +1076,7 @@ $this->getEntityContentFactory(), new ValuesFinder( $this->getPropertyDataTypeLookup() ), $this->getLanguageFallbackChainFactory(), + new ReferencedEntitiesFinder( $this->getLocalEntityUriParser() ), $templateFactory ); } diff --git a/repo/tests/phpunit/includes/EntityParserOutputGeneratorTest.php b/repo/tests/phpunit/includes/EntityParserOutputGeneratorTest.php index b87b1e8..38624e1 100644 --- a/repo/tests/phpunit/includes/EntityParserOutputGeneratorTest.php +++ b/repo/tests/phpunit/includes/EntityParserOutputGeneratorTest.php @@ -7,6 +7,7 @@ use MediaWikiTestCase; use ParserOptions; use Title; +use Wikibase\DataModel\Entity\BasicEntityIdParser; use Wikibase\DataModel\Entity\EntityId; use Wikibase\DataModel\Entity\InMemoryDataTypeLookup; use Wikibase\DataModel\Entity\Item; @@ -16,6 +17,7 @@ use Wikibase\EntityParserOutputGenerator; use Wikibase\EntityRevision; use Wikibase\Lib\Store\Sql\SqlEntityInfoBuilderFactory; +use Wikibase\ReferencedEntitiesFinder; use Wikibase\ValuesFinder; use Wikibase\View\Template\TemplateFactory; @@ -109,6 +111,7 @@ private function newEntityParserOutputGenerator() { $templateFactory = TemplateFactory::getDefaultInstance(); + $referencedEntitiesFinder = new ReferencedEntitiesFinder( new BasicEntityIdParser() ); return new EntityParserOutputGenerator( $this->getEntityViewFactory(), @@ -118,6 +121,7 @@ new SqlEntityInfoBuilderFactory(), $this->newLanguageFallbackChain(), 'en', + $referencedEntitiesFinder, $templateFactory ); } -- To view, visit https://gerrit.wikimedia.org/r/216745 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Id86dba7dfa2a46c9c7b4a94457b4f8a04f58984e Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Wikibase Gerrit-Branch: master Gerrit-Owner: Daniel Kinzler <[email protected]> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
