-
Notifications
You must be signed in to change notification settings - Fork 6
/
WikibaseAPIClient.php
216 lines (197 loc) · 7.48 KB
/
WikibaseAPIClient.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\Response;
use App\Exceptions\WikibaseValueParserException;
use Kevinrob\GuzzleCache\CacheMiddleware;
use App\Exceptions\WikibaseAPIClientException;
class WikibaseAPIClient
{
/**
* @var string wikibase api url
*/
private $baseUrl;
/**
* @var CacheMiddleware Guzzlehttp response caching middleware
*/
private $cache;
public function __construct(string $baseUrl, CacheMiddleware $cache)
{
$this->baseUrl = $baseUrl;
$this->cache = $cache;
}
private function list(array $values): string
{
if (str_contains(implode($values), '|')) {
return "\x1f" . implode("\x1f", $values);
} else {
return implode('|', $values);
}
}
private function get(string $action, array $params): Response
{
$response = Http::withMiddleware($this->cache)
->get($this->baseUrl, array_merge([
'action' => $action,
'format' => 'json',
'maxage' => config('wikidata.response_cache.ttl')
], $params));
// Checking for an errors field in the response, since Wikibase api
// responds with 200 even for erroneous requests
if (isset($response['error'])) {
throw new WikibaseAPIClientException($response['error']['info']);
}
return $response;
}
/**
* Make a direct wbparsevalue API request.
* You should probably use {@link parseValues} instead.
*
* @param string $property property ID
* @param string[] $values up to 50
* @return Response
* @throws WikibaseValueParserException If one of the values cannot be parsed.
*/
public function parseValuesForProperty(string $property, array $values): Response
{
try {
return $this->get('wbparsevalue', [
'values' => $this->list($values),
'property' => $property,
'validate' => true
]);
} catch (WikibaseAPIClientException $e) {
throw new WikibaseValueParserException($e->getMessage());
}
}
/**
* Parse the given values into Wikibase data values.
*
* @param string[][] $valuesByPropertyId Two-dimensional array.
* The outer level is indexed by property ID;
* the inner levels are lists (keys ignored) of plain values to be parsed.
* @return array[][] Two-dimensional array of parsed values.
* The outer level is indexed by property ID;
* the inner level maps the unparsed value (as in $valuesByPropertyId)
* to the parsed value (an array with at least 'type' and 'value' keys).
* @throws WikibaseValueParserException If one of the values cannot be parsed.
*/
public function parseValues(array $valuesByPropertyId): array
{
$parsed = [];
foreach ($valuesByPropertyId as $propertyId => $values) {
$parsed[$propertyId] = collect($values)
->unique()
->chunk(50) // wbparsevalues allows up to 50 values per request
->map(function ($chunk) use ($propertyId) {
$response = $this->parseValuesForProperty($propertyId, $chunk->toArray());
$results = [];
foreach ($response['results'] as $result) {
$results[$result['raw']] = $result;
}
return $results;
})
->collapse()
->toArray();
}
return $parsed;
}
/**
* Make a direct wbformatvalue API request.
* You should probably use {@link formatValues} instead.
*
* @param string $property property ID
* @param array $value Wikibase data value
* @param string $lang language code
* @return Response
*/
public function formatValueForProperty(string $property, array $value, string $lang): Response
{
return $this->get('wbformatvalue', [
'generate' => 'text/plain',
'datavalue' => json_encode($value),
'property' => $property,
'uselang' => $lang,
'options' => json_encode([ 'showcalendar' => 'auto' ]),
]);
}
/**
* Format the given data values into plain text.
*
* @param array[][] $valuesByPropertyId Two-dimensional array of data values.
* The outer level is indexed by property ID;
* the inner level contains the parsed data values.
* The return value of {@link parseValues} can be used here.
* @param string $lang language code
* @return string[][] Two-dimensional array.
* The outer level is indexed by property ID;
* the inner level maps the original keys to formatted values (plain text strings).
* If $valuesByPropertyId was the result of {@link parseValues},
* then the inner keys will be the original unparsed values passed into {@link parseValues}.
*/
public function formatValues(array $valuesByPropertyId, string $lang): array
{
$formatted = [];
foreach ($valuesByPropertyId as $propertyId => $values) {
$formatted[$propertyId] = collect($values)
->unique()
// no chunk, wbformatvalue only supports 1 value per request :(
->map(function (array $value) use ($lang, $propertyId) {
$response = $this->formatValueForProperty($propertyId, $value, $lang);
return $response['result'];
})
->toArray();
}
return $formatted;
}
public function formatEntities(array $ids, string $lang): Response
{
$response = $this->get('wbformatentities', [
'ids' => $this->list($ids),
'uselang' => $lang
]);
return $response;
}
public function getLabels(array $ids, string $lang): array
{
// wbformatentities only allows for up to 50 entities to be formatted at a time
return collect($ids)->chunk(50)
->map(function ($chunk) use ($lang) {
$response = $this->formatEntities($chunk->toArray(), $lang);
return $response['wbformatentities'];
})
->collapse()
// The code below was added due to the fact that wbformatentities only
// returns labels formatted as html links, however we only require the
// label text. Therefore, we extract the text from the links.
->map('strip_tags')
->toArray();
}
public function getEntities(array $ids, array $props): Response
{
return $this->get('wbgetentities', [
'ids' => $this->list($ids),
'props' => $this->list($props),
]);
}
/**
* Get the datatypes of the given property IDs.
*
* @param string[] $ids
* @return (string|null)[] Mapping the property ID to the datatype,
* or null if the property does not exist.
*/
public function getPropertyDatatypes(array $ids): array
{
return collect($ids)
->chunk(50) // wbgetentities allows up to 50 IDs per request
->map(function ($chunk) {
$chunkIds = $chunk->toArray();
$response = $this->getEntities($chunkIds, ['datatype']);
return array_column($response['entities'], 'datatype', 'id') +
array_fill_keys($chunkIds, null);
})
->collapse()
->toArray();
}
}