|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* This file provides a class to manipulate the analysis response. |
|
4
|
|
|
* |
|
5
|
|
|
* @author David Riccitelli <[email protected]> |
|
6
|
|
|
* @since 3.25.0 |
|
7
|
|
|
* @package Wordlift\Analysis\Response |
|
8
|
|
|
*/ |
|
9
|
|
|
|
|
10
|
|
|
namespace Wordlift\Analysis\Response; |
|
11
|
|
|
|
|
12
|
|
|
use stdClass; |
|
13
|
|
|
use Wordlift\Entity\Entity_Helper; |
|
14
|
|
|
|
|
15
|
|
|
class Analysis_Response_Ops { |
|
16
|
|
|
|
|
17
|
|
|
/** |
|
18
|
|
|
* The analysis response json. |
|
19
|
|
|
* |
|
20
|
|
|
* @since 3.21.5 |
|
21
|
|
|
* @access private |
|
22
|
|
|
* @var mixed $json Holds the analysis response json. |
|
23
|
|
|
*/ |
|
24
|
|
|
private $json; |
|
25
|
|
|
|
|
26
|
|
|
/** |
|
27
|
|
|
* Holds the {@link Wordlift_Entity_Uri_Service}. |
|
28
|
|
|
* |
|
29
|
|
|
* @since 3.21.5 |
|
30
|
|
|
* @access private |
|
31
|
|
|
* @var \Wordlift_Entity_Uri_Service $entity_uri_service The {@link Wordlift_Entity_Uri_Service} instance. |
|
32
|
|
|
*/ |
|
33
|
|
|
private $entity_uri_service; |
|
34
|
|
|
|
|
35
|
|
|
private $entity_service; |
|
36
|
|
|
|
|
37
|
|
|
/** |
|
38
|
|
|
* @var \Wordlift_Entity_Type_Service |
|
39
|
|
|
*/ |
|
40
|
|
|
private $entity_type_service; |
|
41
|
|
|
/** |
|
42
|
|
|
* @var \Wordlift_Post_Image_Storage |
|
43
|
|
|
*/ |
|
44
|
|
|
private $post_image_storage; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* @var Entity_Helper |
|
48
|
|
|
*/ |
|
49
|
|
|
private $entity_helper; |
|
50
|
|
|
|
|
51
|
|
|
/** |
|
52
|
|
|
* Analysis_Response_Ops constructor. |
|
53
|
|
|
* |
|
54
|
|
|
* @param \Wordlift_Entity_Uri_Service $entity_uri_service The {@link Wordlift_Entity_Uri_Service}. |
|
55
|
|
|
* @param \Wordlift_Entity_Service $entity_service The {@link Wordlift_Entity_Service}. |
|
56
|
|
|
* @param \Wordlift_Entity_Type_Service $entity_type_service The {@link Wordlift_Entity_Type_Service}. |
|
57
|
|
|
* @param \Wordlift_Post_Image_Storage $post_image_storage A {@link Wordlift_Post_Image_Storage} instance. |
|
58
|
|
|
* @param Entity_Helper $entity_helper The {@link Entity_Helper}. |
|
59
|
|
|
* @param mixed $json The analysis response json. |
|
60
|
|
|
* |
|
61
|
|
|
* @since 3.21.5 |
|
62
|
|
|
*/ |
|
63
|
|
View Code Duplication |
public function __construct( $entity_uri_service, $entity_service, $entity_type_service, $post_image_storage, $entity_helper, $json ) { |
|
|
|
|
|
|
64
|
|
|
|
|
65
|
|
|
$this->json = $json; |
|
66
|
|
|
$this->entity_uri_service = $entity_uri_service; |
|
67
|
|
|
$this->entity_service = $entity_service; |
|
68
|
|
|
$this->entity_type_service = $entity_type_service; |
|
69
|
|
|
$this->post_image_storage = $post_image_storage; |
|
70
|
|
|
$this->entity_helper = $entity_helper; |
|
71
|
|
|
|
|
72
|
|
|
} |
|
73
|
|
|
|
|
74
|
|
|
/** |
|
75
|
|
|
* Switches remote entities, i.e. entities with id outside the local dataset, to local entities. |
|
76
|
|
|
* |
|
77
|
|
|
* The function takes all the entities that have an id which is not local. For each remote entity, a list of URIs |
|
78
|
|
|
* is built comprising the entity id and the sameAs. Then a query is issued in the local database to find potential |
|
79
|
|
|
* matches from the local vocabulary. |
|
80
|
|
|
* |
|
81
|
|
|
* If found, the entity id is swapped with the local id and the remote id is added to the sameAs. |
|
82
|
|
|
* |
|
83
|
|
|
* @return Analysis_Response_Ops The current Analysis_Response_Ops instance. |
|
84
|
|
|
*/ |
|
85
|
|
|
public function make_entities_local() { |
|
86
|
|
|
|
|
87
|
|
|
if ( ! isset( $this->json->entities ) ) { |
|
88
|
|
|
return $this; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
// Get the URIs. |
|
92
|
|
|
$uris = array_keys( get_object_vars( $this->json->entities ) ); |
|
93
|
|
|
$mappings = $this->entity_helper->map_many_to_local( $uris ); |
|
94
|
|
|
|
|
95
|
|
|
foreach ( $mappings as $external_uri => $internal_uri ) { |
|
96
|
|
|
|
|
97
|
|
|
// Move the data from the external URI to the internal URI. |
|
98
|
|
|
if ( ! isset( $this->json->entities->{$internal_uri} ) ) { |
|
99
|
|
|
$this->json->entities->{$internal_uri} = $this->json->entities->{$external_uri}; |
|
100
|
|
|
} |
|
101
|
|
|
|
|
102
|
|
|
// Ensure sameAs is an array. |
|
103
|
|
|
if ( ! isset( $this->json->entities->{$internal_uri}->sameAs ) |
|
104
|
|
|
|| ! is_array( $this->json->entities->{$internal_uri}->sameAs ) ) { |
|
105
|
|
|
$this->json->entities->{$internal_uri}->sameAs = array(); |
|
106
|
|
|
} |
|
107
|
|
|
|
|
108
|
|
|
// Add the external URI as sameAs. |
|
109
|
|
|
$this->json->entities->{$internal_uri}->sameAs[] = $external_uri; |
|
110
|
|
|
|
|
111
|
|
|
// Finally remove the external URI. |
|
112
|
|
|
unset( $this->json->entities->{$external_uri} ); |
|
113
|
|
|
} |
|
114
|
|
|
|
|
115
|
|
|
if ( isset( $this->json->annotations ) ) { |
|
116
|
|
|
foreach ( $this->json->annotations as $key => $annotation ) { |
|
117
|
|
|
if ( isset( $annotation->entityMatches ) ) { |
|
118
|
|
|
foreach ( $annotation->entityMatches as $match ) { |
|
119
|
|
|
if ( isset( $match->entityId ) && isset( $mappings[ $match->entityId ] ) ) { |
|
120
|
|
|
$match->entityId = $mappings[ $match->entityId ]; |
|
121
|
|
|
} |
|
122
|
|
|
} |
|
123
|
|
|
} |
|
124
|
|
|
} |
|
125
|
|
|
} |
|
126
|
|
|
|
|
127
|
|
|
return $this; |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
/** |
|
131
|
|
|
* Add occurrences by parsing the provided html content. |
|
132
|
|
|
* |
|
133
|
|
|
* @param string $content The html content with annotations. |
|
134
|
|
|
* |
|
135
|
|
|
* @return Analysis_Response_Ops The {@link Analysis_Response_Ops} instance. |
|
136
|
|
|
* |
|
137
|
|
|
* @since 3.23.7 refactor the regex pattern to take into account that there might be css classes between textannotation |
|
138
|
|
|
* and disambiguated. |
|
139
|
|
|
* |
|
140
|
|
|
* @link https://github.com/insideout10/wordlift-plugin/issues/1001 |
|
141
|
|
|
*/ |
|
142
|
|
|
public function add_occurrences( $content ) { |
|
143
|
|
|
|
|
144
|
|
|
// Try to get all the disambiguated annotations and bail out if an error occurs. |
|
145
|
|
|
if ( false === preg_match_all( |
|
146
|
|
|
'|<span\s+id="([^"]+)"\s+class="textannotation\s+(?:\S+\s+)?disambiguated(?=[\s"])[^"]*"\s+itemid="([^"]*)">(.*?)</span>|', |
|
147
|
|
|
$content, |
|
148
|
|
|
$matches, |
|
149
|
|
|
PREG_OFFSET_CAPTURE |
|
150
|
|
|
) ) { |
|
151
|
|
|
return $this; |
|
152
|
|
|
} |
|
153
|
|
|
|
|
154
|
|
|
if ( empty( $matches ) ) { |
|
155
|
|
|
return $this; |
|
156
|
|
|
} |
|
157
|
|
|
|
|
158
|
|
|
$parse_data = array_reduce( range( 0, count( $matches[1] ) - 1 ), function ( $carry, $i ) use ( $matches ) { |
|
159
|
|
|
if ( empty( $matches[0] ) ) { |
|
160
|
|
|
return $carry; |
|
161
|
|
|
} |
|
162
|
|
|
|
|
163
|
|
|
$start = $matches[0][ $i ][1]; |
|
164
|
|
|
$end = $start + strlen( $matches[0][ $i ][0] ); |
|
165
|
|
|
$annotation_id = $matches[1][ $i ][0]; |
|
166
|
|
|
$item_id = $matches[2][ $i ][0]; |
|
167
|
|
|
$text = $matches[3][ $i ][0]; |
|
168
|
|
|
|
|
169
|
|
|
$annotation = new StdClass; |
|
170
|
|
|
$annotation->annotationId = $annotation_id; |
|
171
|
|
|
$annotation->start = $start; |
|
172
|
|
|
$annotation->end = $end; |
|
173
|
|
|
$annotation->text = $text; |
|
174
|
|
|
|
|
175
|
|
|
$entity_match = new StdClass; |
|
176
|
|
|
$entity_match->confidence = 100; |
|
177
|
|
|
$entity_match->entityId = $item_id; |
|
178
|
|
|
$annotation->entityMatches[] = $entity_match; |
|
179
|
|
|
|
|
180
|
|
|
$carry['annotations'][ $annotation_id ] = $annotation; |
|
181
|
|
|
$carry['occurrences'][ $item_id ][] = $annotation_id; |
|
182
|
|
|
|
|
183
|
|
|
return $carry; |
|
184
|
|
|
}, array( 'annotations' => array(), 'occurrences' => array(), ) ); |
|
185
|
|
|
|
|
186
|
|
|
$annotations = $parse_data['annotations']; |
|
187
|
|
|
$occurrences = $parse_data['occurrences']; |
|
188
|
|
|
|
|
189
|
|
|
foreach ( array_keys( $occurrences ) as $item_id ) { |
|
190
|
|
|
|
|
191
|
|
|
// If the entity isn't there, add it. |
|
192
|
|
|
if ( ! isset( $this->json->entities->{$item_id} ) ) { |
|
193
|
|
|
$entity = $this->get_local_entity( $item_id ); |
|
194
|
|
|
|
|
195
|
|
|
// Entity not found in the local vocabulary, continue to the next one. |
|
196
|
|
|
if ( false === $entity ) { |
|
197
|
|
|
continue; |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
$this->json->entities->{$item_id} = $entity; |
|
201
|
|
|
} |
|
202
|
|
|
} |
|
203
|
|
|
|
|
204
|
|
|
// Here we're adding back some data structures required by the client-side code. |
|
205
|
|
|
// |
|
206
|
|
|
// We're adding: |
|
207
|
|
|
// 1. the .entities[entity_id].occurrences array with the annotations' ids. |
|
208
|
|
|
// 2. the .entities[entity_id].annotations[annotation_id] = { id: annotation_id } map. |
|
209
|
|
|
// |
|
210
|
|
|
// Before 3.23.0 this was done by the client-side code located in src/coffee/editpost-widget/app.services.AnalysisService.coffee |
|
211
|
|
|
// function `preselect`, which was called by src/coffee/editpost-widget/app.services.EditorService.coffee in |
|
212
|
|
|
// `embedAnalysis`. |
|
213
|
|
|
foreach ( $this->json->entities as $id => $entity ) { |
|
214
|
|
|
$this->json->entities->{$id}->occurrences = isset( $occurrences[ $id ] ) ? $occurrences[ $id ] : array();; |
|
215
|
|
|
|
|
216
|
|
|
foreach ( $this->json->entities->{$id}->occurrences as $annotation_id ) { |
|
217
|
|
|
$this->json->entities->{$id}->annotations[ $annotation_id ] = array( |
|
218
|
|
|
'id' => $annotation_id, |
|
219
|
|
|
); |
|
220
|
|
|
} |
|
221
|
|
|
} |
|
222
|
|
|
|
|
223
|
|
|
// Add the missing annotations. This allows the analysis response to work also if we didn't receive results |
|
224
|
|
|
// from the analysis API. |
|
225
|
|
|
foreach ( $annotations as $annotation_id => $annotation ) { |
|
226
|
|
|
|
|
227
|
|
|
if ( ! isset( $this->json->annotations->{$annotation_id} ) ) { |
|
228
|
|
|
$this->json->annotations->{$annotation_id} = $annotation; |
|
229
|
|
|
} |
|
230
|
|
|
|
|
231
|
|
|
} |
|
232
|
|
|
|
|
233
|
|
|
return $this; |
|
234
|
|
|
} |
|
235
|
|
|
|
|
236
|
|
|
private function get_local_entity( $uri ) { |
|
237
|
|
|
|
|
238
|
|
|
$entity = $this->entity_uri_service->get_entity( $uri ); |
|
239
|
|
|
|
|
240
|
|
|
if ( null === $entity ) { |
|
241
|
|
|
return false; |
|
242
|
|
|
} |
|
243
|
|
|
|
|
244
|
|
|
$type = $this->entity_type_service->get( $entity->ID ); |
|
245
|
|
|
$images = $this->post_image_storage->get( $entity->ID ); |
|
246
|
|
|
|
|
247
|
|
|
return (object) array( |
|
248
|
|
|
'id' => $uri, |
|
249
|
|
|
'label' => $entity->post_title, |
|
250
|
|
|
'description' => $entity->post_content, |
|
251
|
|
|
'sameAs' => wl_schema_get_value( $entity->ID, 'sameAs' ), |
|
252
|
|
|
'mainType' => str_replace( 'wl-', '', $type['css_class'] ), |
|
253
|
|
|
'types' => wl_get_entity_rdf_types( $entity->ID ), |
|
254
|
|
|
'images' => $images, |
|
255
|
|
|
); |
|
256
|
|
|
} |
|
257
|
|
|
|
|
258
|
|
|
/** |
|
259
|
|
|
* Return the JSON response. |
|
260
|
|
|
* |
|
261
|
|
|
* @return mixed The JSON response. |
|
262
|
|
|
* @since 3.24.2 |
|
263
|
|
|
*/ |
|
264
|
|
|
public function get_json() { |
|
265
|
|
|
|
|
266
|
|
|
return $this->json; |
|
267
|
|
|
} |
|
268
|
|
|
|
|
269
|
|
|
/** |
|
270
|
|
|
* Get the string representation of the JSON. |
|
271
|
|
|
* |
|
272
|
|
|
* @return false|string The string representation or false in case of error. |
|
273
|
|
|
*/ |
|
274
|
|
|
public function to_string() { |
|
275
|
|
|
|
|
276
|
|
|
// Add the `JSON_UNESCAPED_UNICODE` only for PHP 5.4+. |
|
277
|
|
|
$options = ( version_compare( PHP_VERSION, '5.4', '>=' ) |
|
278
|
|
|
? 256 : 0 ); |
|
279
|
|
|
|
|
280
|
|
|
return wp_json_encode( $this->json, $options ); |
|
281
|
|
|
} |
|
282
|
|
|
|
|
283
|
|
|
} |
|
284
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.