Completed
Push — master ( 1e59d7...89bb83 )
by
unknown
02:51
created

importEntityFromWikidata()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace WikibaseQuality\ConstraintReport\Maintenance;
4
5
use Config;
6
use Deserializers\Deserializer;
7
use Maintenance;
8
use Serializers\Serializer;
9
use User;
10
use Wikibase\DataModel\Entity\Item;
11
use Wikibase\DataModel\SiteLinkList;
12
use Wikibase\DataModel\Statement\StatementListProvider;
13
use Wikibase\Lib\Store\EntityStore;
14
use Wikibase\Lib\Store\StorageException;
15
use Wikibase\Repo\WikibaseRepo;
16
17
// @codeCoverageIgnoreStart
18
$basePath = getenv( "MW_INSTALL_PATH" ) !== false
19
	? getenv( "MW_INSTALL_PATH" ) : __DIR__ . "/../../..";
20
21
require_once $basePath . "/maintenance/Maintenance.php";
22
// @codeCoverageIgnoreEnd
23
24
/**
25
 * Imports entities needed for constraint checks from Wikidata into the local repository.
26
 *
27
 * @license GPL-2.0-or-later
28
 */
29
class ImportConstraintEntities extends Maintenance {
30
31
	/**
32
	 * @var Serializer
33
	 */
34
	private $entitySerializer;
35
36
	/**
37
	 * @var Deserializer
38
	 */
39
	private $entityDeserializer;
40
41
	/**
42
	 * @var EntityStore
43
	 */
44
	private $entityStore;
45
46
	/**
47
	 * @var User
48
	 */
49
	private $user;
50
51
	public function __construct() {
52
		parent::__construct();
53
54
		$this->addDescription(
55
			'Import entities needed for constraint checks ' .
56
			'from Wikidata into the local repository.'
57
		);
58
		$this->addOption(
59
			'config-format',
60
			'The format in which the resulting configuration will be omitted: ' .
61
			'"globals" for directly settings global variables, suitable for inclusion in LocalSettings.php (default), ' .
62
			'or "wgConf" for printing parts of arrays suitable for inclusion in $wgConf->settings.'
63
		);
64
		$this->addOption(
65
			'dry-run',
66
			'Don’t actually import entities, just print which ones would be imported.'
67
		);
68
	}
69
70
	/**
71
	 * (This cannot happen in the constructor because the autoloader is not yet initialized there.)
72
	 */
73
	private function setupServices() {
74
		$repo = WikibaseRepo::getDefaultInstance();
75
		$this->entitySerializer = $repo->getAllTypesEntitySerializer();
76
		$this->entityDeserializer = $repo->getInternalFormatEntityDeserializer();
77
		$this->entityStore = $repo->getEntityStore();
78
		$this->user = User::newSystemUser( 'WikibaseQualityConstraints importer' );
79
	}
80
81
	public function execute() {
82
		$this->setupServices();
83
84
		$configUpdates = [];
85
86
		$extensionJsonFile = __DIR__ . '/../extension.json';
87
		$extensionJsonText = file_get_contents( $extensionJsonFile );
88
		$extensionJson = json_decode( $extensionJsonText, /* assoc = */ true );
89
		$wikidataEntityIds = $this->getEntitiesToImport( $extensionJson['config'], $this->getConfig() );
90
91
		foreach ( $wikidataEntityIds as $key => $wikidataEntityId ) {
92
			$localEntityId = $this->importEntityFromWikidata( $wikidataEntityId );
93
			$configUpdates[$key] = [
94
				'wikidata' => $wikidataEntityId,
95
				'local' => $localEntityId,
96
			];
97
		}
98
99
		$this->outputConfigUpdates( $configUpdates );
100
	}
101
102
	/**
103
	 * @param array $extensionJsonConfig
104
	 * @param Config $wikiConfig
105
	 * @return string[]
106
	 */
107
	private function getEntitiesToImport( array $extensionJsonConfig, Config $wikiConfig ) {
108
		$wikidataEntityIds = [];
109
110
		foreach ( $extensionJsonConfig as $key => $value ) {
111
			if ( !preg_match( '/Id$/', $key ) ) {
112
				continue;
113
			}
114
115
			$wikidataEntityId = $value['value'];
116
			$localEntityId = $wikiConfig->get( $key );
117
118
			if ( $localEntityId === $wikidataEntityId ) {
119
				$wikidataEntityIds[$key] = $wikidataEntityId;
120
			}
121
		}
122
123
		return $wikidataEntityIds;
124
	}
125
126
	/**
127
	 * @param string $wikidataEntityId
128
	 * @return string local entity ID
129
	 */
130
	private function importEntityFromWikidata( $wikidataEntityId ) {
131
		$wikidataEntityUrl = "https://www.wikidata.org/wiki/Special:EntityData/$wikidataEntityId.json";
132
		$wikidataEntitiesJson = file_get_contents( $wikidataEntityUrl );
133
		return $this->importEntityFromJson( $wikidataEntityId, $wikidataEntitiesJson );
134
	}
135
136
	/**
137
	 * @param string $wikidataEntityId
138
	 * @param string $wikidataEntitiesJson
139
	 * @return string local entity ID
140
	 */
141
	private function importEntityFromJson( $wikidataEntityId, $wikidataEntitiesJson ) {
142
		$wikidataEntityArray = json_decode( $wikidataEntitiesJson, true )['entities'][$wikidataEntityId];
143
		$wikidataEntity = $this->entityDeserializer->deserialize( $wikidataEntityArray );
144
145
		$wikidataEntity->setId( null );
146
147
		if ( $wikidataEntity instanceof StatementListProvider ) {
148
			$wikidataEntity->getStatements()->clear();
149
		}
150
151
		if ( $wikidataEntity instanceof Item ) {
152
			$wikidataEntity->setSiteLinkList( new SiteLinkList() );
153
		}
154
155
		if ( $this->getOption( 'dry-run', false ) ) {
156
			$wikidataEntityJson = json_encode( $this->entitySerializer->serialize( $wikidataEntity ) );
157
			$this->output( $wikidataEntityJson . "\n" );
158
			return "-$wikidataEntityId";
159
		}
160
161
		try {
162
			$localEntity = $this->entityStore->saveEntity(
163
				$wikidataEntity,
164
				"imported from [[wikidata:$wikidataEntityId]]",
165
				$this->user,
166
				EDIT_NEW | EDIT_FORCE_BOT
167
			)->getEntity();
168
169
			return $localEntity->getId()->getSerialization();
170
		} catch ( StorageException $storageException ) {
0 ignored issues
show
Bug introduced by
The class Wikibase\Lib\Store\StorageException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
171
			$message = $storageException->getMessage();
172
			// example message:
173
			// * Item [[Item:Q475|Q475]] already has label "as references" associated with language code en, using the same description text.
174
			// note that the label and language code may vary (conflicts in any language),
175
			// and that the item link may or may not be in the main namespace
176
			$pattern = '/[[|]([^]|]*)]] already has label .* using the same description text/';
177
			if ( preg_match( $pattern, $message, $matches ) ) {
178
				return $matches[1];
179
			} else {
180
				throw $storageException;
181
			}
182
		}
183
	}
184
185
	private function outputConfigUpdates( array $configUpdates ) {
186
		$configFormat = $this->getOption( 'config-format', 'globals' );
187
		switch ( $configFormat ) {
188
			case 'globals':
189
				$this->outputConfigUpdatesGlobals( $configUpdates );
190
				break;
191
			case 'wgConf':
192
				$this->outputConfigUpdatesWgConf( $configUpdates );
193
				break;
194
			default:
195
				$this->error( "Invalid config format \"$configFormat\", using \"globals\"" );
196
				$this->outputConfigUpdatesGlobals( $configUpdates );
197
				break;
198
		}
199
	}
200
201
	private function outputConfigUpdatesGlobals( array $configUpdates ) {
202
		foreach ( $configUpdates as $key => $value ) {
203
			$localValueCode = var_export( $value['local'], true );
204
			$this->output( "\$wg$key = $localValueCode;\n" );
205
		}
206
	}
207
208
	private function outputConfigUpdatesWgConf( array $configUpdates ) {
209
		foreach ( $configUpdates as $key => $value ) {
210
			$keyCode = var_export( $key, true );
211
			$wikidataValueCode = var_export( $value['wikidata'], true );
212
			$localValueCode = var_export( $value['local'], true );
213
			$wikiIdCode = var_export( wfWikiID(), true );
214
			$block = <<< EOF
215
$keyCode => [
216
	'default' => $wikidataValueCode,
217
	$wikiIdCode => $localValueCode,
218
],
219
220
221
EOF;
222
			$this->output( $block );
223
		}
224
	}
225
226
}
227
228
// @codeCoverageIgnoreStart
229
$maintClass = ImportConstraintEntities::class;
230
require_once RUN_MAINTENANCE_IF_MAIN;
231
// @codeCoverageIgnoreEnd
232