Add license headers, general cleanups on Java files

This commit is contained in:
Antonin Delpeuch 2018-03-04 00:14:59 +00:00
parent d6b229f25e
commit 0b14a1a627
140 changed files with 5494 additions and 2592 deletions

View File

@ -1,10 +1,32 @@
#-------------------------------------------------------------------------------
# Copyright (C) 2018 Antonin Delpeuch
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#-------------------------------------------------------------------------------
Sources for the images:
- Information.svg by User:Bobarino: The following images were obtained from Wikimedia Commons:
- Information.svg by User:Bobarino: (CC BY-SA)
https://commons.wikimedia.org/wiki/File:Information.svg https://commons.wikimedia.org/wiki/File:Information.svg
- Warning.svg by User:Ezekiel63745 - Warning.svg by User:Ezekiel63745 (CC BY-SA)
https://commons.wikimedia.org/wiki/File:Information_orange.svg https://commons.wikimedia.org/wiki/File:Information_orange.svg
- Important.svg originally from David Vignoni - Important.svg originally from David Vignoni (GNU LGPL)
https://commons.wikimedia.org/wiki/File:Nuvola_apps_important.svg https://commons.wikimedia.org/wiki/File:Nuvola_apps_important.svg
- Critical.svg by User:Stannered - Critical.svg by User:Stannered (CC BY-SA)
https://commons.wikimedia.org/wiki/File:Stop_x_nuvola.svg https://commons.wikimedia.org/wiki/File:Stop_x_nuvola.svg

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.commands; package org.openrefine.wikidata.commands;
import java.io.IOException; import java.io.IOException;
@ -14,13 +37,13 @@ import org.openrefine.wikidata.editing.ConnectionManager;
import com.google.refine.commands.Command; import com.google.refine.commands.Command;
public class LoginCommand extends Command { public class LoginCommand extends Command {
@Override @Override
public void doPost(HttpServletRequest request, HttpServletResponse response) public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { throws ServletException, IOException {
String username = request.getParameter("wb-username"); String username = request.getParameter("wb-username");
String password = request.getParameter("wb-password"); String password = request.getParameter("wb-password");
String remember = request.getParameter("remember-credentials"); String remember = request.getParameter("remember-credentials");
System.out.println(remember);
ConnectionManager manager = ConnectionManager.getInstance(); ConnectionManager manager = ConnectionManager.getInstance();
if (username != null && password != null) { if (username != null && password != null) {
manager.login(username, password, "on".equals(remember)); manager.login(username, password, "on".equals(remember));
@ -29,10 +52,10 @@ public class LoginCommand extends Command {
} }
response.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "application/json"); response.setHeader("Content-Type", "application/json");
StringWriter sb = new StringWriter(2048); StringWriter sb = new StringWriter(2048);
JSONWriter writer = new JSONWriter(sb); JSONWriter writer = new JSONWriter(sb);
try { try {
writer.object(); writer.object();
writer.key("logged_in"); writer.key("logged_in");
@ -45,7 +68,7 @@ public class LoginCommand extends Command {
} }
respond(response, sb.toString()); respond(response, sb.toString());
} }
@Override @Override
public void doGet(HttpServletRequest request, HttpServletResponse response) public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { throws ServletException, IOException {

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.commands; package org.openrefine.wikidata.commands;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -15,8 +38,7 @@ public class PerformWikibaseEditsCommand extends EngineDependentCommand {
protected AbstractOperation createOperation(Project project, HttpServletRequest request, JSONObject engineConfig) protected AbstractOperation createOperation(Project project, HttpServletRequest request, JSONObject engineConfig)
throws Exception { throws Exception {
String summary = request.getParameter("summary"); String summary = request.getParameter("summary");
return new PerformWikibaseEditsOperation(engineConfig, return new PerformWikibaseEditsOperation(engineConfig, summary);
summary);
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
/* /*
Copyright 2010, Google Inc. Copyright 2010, Google Inc.
@ -34,8 +57,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package org.openrefine.wikidata.commands; package org.openrefine.wikidata.commands;
import java.io.IOException; import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
@ -63,25 +84,25 @@ import com.google.refine.model.Project;
import com.google.refine.util.ParsingUtilities; import com.google.refine.util.ParsingUtilities;
public class PreviewWikibaseSchemaCommand extends Command { public class PreviewWikibaseSchemaCommand extends Command {
@Override @Override
public void doPost(HttpServletRequest request, HttpServletResponse response) public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { throws ServletException, IOException {
try { try {
Project project = getProject(request); Project project = getProject(request);
response.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "application/json"); response.setHeader("Content-Type", "application/json");
String jsonString = request.getParameter("schema"); String jsonString = request.getParameter("schema");
WikibaseSchema schema = null; WikibaseSchema schema = null;
if (jsonString != null) { if (jsonString != null) {
try { try {
JSONObject json = ParsingUtilities.evaluateJsonStringToObject(jsonString); JSONObject json = ParsingUtilities.evaluateJsonStringToObject(jsonString);
schema = WikibaseSchema.reconstruct(json); schema = WikibaseSchema.reconstruct(json);
} catch(JSONException e) { } catch (JSONException e) {
respond(response, "error", "Wikibase schema could not be parsed."); respond(response, "error", "Wikibase schema could not be parsed.");
return; return;
} }
@ -92,20 +113,20 @@ public class PreviewWikibaseSchemaCommand extends Command {
respond(response, "error", "No Wikibase schema provided."); respond(response, "error", "No Wikibase schema provided.");
return; return;
} }
QAWarningStore warningStore = new QAWarningStore(); QAWarningStore warningStore = new QAWarningStore();
// Evaluate project // Evaluate project
Engine engine = getEngine(request, project); Engine engine = getEngine(request, project);
List<ItemUpdate> editBatch = schema.evaluate(project, engine, warningStore); List<ItemUpdate> editBatch = schema.evaluate(project, engine, warningStore);
StringWriter sb = new StringWriter(2048); StringWriter sb = new StringWriter(2048);
JSONWriter writer = new JSONWriter(sb); JSONWriter writer = new JSONWriter(sb);
writer.object(); writer.object();
{ {
StringWriter stringWriter = new StringWriter(); StringWriter stringWriter = new StringWriter();
// Inspect the edits and generate warnings // Inspect the edits and generate warnings
EditInspector inspector = new EditInspector(warningStore); EditInspector inspector = new EditInspector(warningStore);
inspector.inspect(editBatch); inspector.inspect(editBatch);
@ -115,22 +136,22 @@ public class PreviewWikibaseSchemaCommand extends Command {
warning.write(writer, new Properties()); warning.write(writer, new Properties());
} }
writer.endArray(); writer.endArray();
// this is not the length of the warnings array written before, // this is not the length of the warnings array written before,
// but the total number of issues raised (before deduplication) // but the total number of issues raised (before deduplication)
writer.key("nb_warnings"); writer.key("nb_warnings");
writer.value(warningStore.getNbWarnings()); writer.value(warningStore.getNbWarnings());
// Export to QuickStatements // Export to QuickStatements
QuickStatementsExporter exporter = new QuickStatementsExporter(); QuickStatementsExporter exporter = new QuickStatementsExporter();
exporter.translateItemList(editBatch, stringWriter); exporter.translateItemList(editBatch, stringWriter);
writer.key("quickstatements"); writer.key("quickstatements");
writer.value(FirstLinesExtractor.extractFirstLines(stringWriter.toString(), 50)); writer.value(FirstLinesExtractor.extractFirstLines(stringWriter.toString(), 50));
} }
writer.endObject(); writer.endObject();
respond(response, sb.toString()); respond(response, sb.toString());
} catch (Exception e) { } catch (Exception e) {
respondException(response, e); respondException(response, e);

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.commands; package org.openrefine.wikidata.commands;
import java.io.IOException; import java.io.IOException;
@ -23,24 +46,24 @@ public class SaveWikibaseSchemaCommand extends Command {
@Override @Override
public void doPost(HttpServletRequest request, HttpServletResponse response) public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { throws ServletException, IOException {
try { try {
Project project = getProject(request); Project project = getProject(request);
String jsonString = request.getParameter("schema"); String jsonString = request.getParameter("schema");
if (jsonString == null) { if (jsonString == null) {
respond(response, "error", "No Wikibase schema provided."); respond(response, "error", "No Wikibase schema provided.");
return; return;
} }
JSONObject json = ParsingUtilities.evaluateJsonStringToObject(jsonString); JSONObject json = ParsingUtilities.evaluateJsonStringToObject(jsonString);
WikibaseSchema schema = WikibaseSchema.reconstruct(json); WikibaseSchema schema = WikibaseSchema.reconstruct(json);
AbstractOperation op = new SaveWikibaseSchemaOperation(schema); AbstractOperation op = new SaveWikibaseSchemaOperation(schema);
Process process = op.createProcess(project, new Properties()); Process process = op.createProcess(project, new Properties());
performProcessAndRespond(request, response, project, process); performProcessAndRespond(request, response, project, process);
} catch (JSONException e) { } catch (JSONException e) {
respond(response, "error", "Wikibase schema could not be parsed."); respond(response, "error", "Wikibase schema could not be parsed.");
} catch (Exception e) { } catch (Exception e) {

View File

@ -1,54 +1,74 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.editing; package org.openrefine.wikidata.editing;
import java.io.IOException; import java.io.IOException;
import java.util.Properties;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONWriter;
import org.wikidata.wdtk.wikibaseapi.ApiConnection; import org.wikidata.wdtk.wikibaseapi.ApiConnection;
import org.wikidata.wdtk.wikibaseapi.LoginFailedException; import org.wikidata.wdtk.wikibaseapi.LoginFailedException;
import com.google.refine.ProjectManager; import com.google.refine.ProjectManager;
import com.google.refine.preference.PreferenceStore; import com.google.refine.preference.PreferenceStore;
/** /**
* Manages a connection to Wikidata, with login credentials stored * Manages a connection to Wikidata, with login credentials stored in the
* in the preferences. * preferences.
* *
* Ideally, we should store only the cookies and not the password. * Ideally, we should store only the cookies and not the password. But
* But Wikidata-Toolkit does not allow for that as cookies are kept * Wikidata-Toolkit does not allow for that as cookies are kept private.
* private.
* *
* This class is also hard-coded for Wikidata: generalization to other * This class is also hard-coded for Wikidata: generalization to other Wikibase
* Wikibase instances should be feasible though. * instances should be feasible though.
* *
* @author antonin * @author Antonin Delpeuch
*/ */
public class ConnectionManager { public class ConnectionManager {
public static final String PREFERENCE_STORE_KEY = "wikidata_credentials"; public static final String PREFERENCE_STORE_KEY = "wikidata_credentials";
private PreferenceStore prefStore; private PreferenceStore prefStore;
private ApiConnection connection; private ApiConnection connection;
private static class ConnectionManagerHolder { private static class ConnectionManagerHolder {
private static final ConnectionManager instance = new ConnectionManager(); private static final ConnectionManager instance = new ConnectionManager();
} }
public static ConnectionManager getInstance() { public static ConnectionManager getInstance() {
return ConnectionManagerHolder.instance; return ConnectionManagerHolder.instance;
} }
private ConnectionManager() { private ConnectionManager() {
prefStore = ProjectManager.singleton.getPreferenceStore(); prefStore = ProjectManager.singleton.getPreferenceStore();
connection = null; connection = null;
restoreSavedConnection(); restoreSavedConnection();
} }
public void login(String username, String password, boolean rememberCredentials) { public void login(String username, String password, boolean rememberCredentials) {
if (rememberCredentials) { if (rememberCredentials) {
try { try {
@ -63,7 +83,7 @@ public class ConnectionManager {
e.printStackTrace(); e.printStackTrace();
} }
} }
connection = ApiConnection.getWikidataApiConnection(); connection = ApiConnection.getWikidataApiConnection();
try { try {
connection.login(username, password); connection.login(username, password);
@ -71,14 +91,13 @@ public class ConnectionManager {
connection = null; connection = null;
} }
} }
public void restoreSavedConnection() { public void restoreSavedConnection() {
JSONObject savedCredentials = getStoredCredentials(); JSONObject savedCredentials = getStoredCredentials();
if (savedCredentials != null) { if (savedCredentials != null) {
connection = ApiConnection.getWikidataApiConnection(); connection = ApiConnection.getWikidataApiConnection();
try { try {
connection.login(savedCredentials.getString("username"), connection.login(savedCredentials.getString("username"), savedCredentials.getString("password"));
savedCredentials.getString("password"));
} catch (LoginFailedException e) { } catch (LoginFailedException e) {
connection = null; connection = null;
} catch (JSONException e) { } catch (JSONException e) {
@ -86,7 +105,7 @@ public class ConnectionManager {
} }
} }
} }
public JSONObject getStoredCredentials() { public JSONObject getStoredCredentials() {
JSONArray array = (JSONArray) prefStore.get(PREFERENCE_STORE_KEY); JSONArray array = (JSONArray) prefStore.get(PREFERENCE_STORE_KEY);
if (array.length() > 0) { if (array.length() > 0) {
@ -94,11 +113,11 @@ public class ConnectionManager {
return array.getJSONObject(0); return array.getJSONObject(0);
} catch (JSONException e) { } catch (JSONException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
return null; return null;
} }
public void logout() { public void logout() {
prefStore.put(PREFERENCE_STORE_KEY, new JSONArray()); prefStore.put(PREFERENCE_STORE_KEY, new JSONArray());
if (connection != null) { if (connection != null) {
@ -110,11 +129,11 @@ public class ConnectionManager {
} }
} }
} }
public ApiConnection getConnection() { public ApiConnection getConnection() {
return connection; return connection;
} }
public boolean isLoggedIn() { public boolean isLoggedIn() {
return connection != null; return connection != null;
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.editing; package org.openrefine.wikidata.editing;
import java.io.IOException; import java.io.IOException;
@ -27,184 +50,161 @@ import org.wikidata.wdtk.wikibaseapi.apierrors.MediaWikiApiErrorException;
* *
*/ */
public class EditBatchProcessor { public class EditBatchProcessor {
static final Logger logger = LoggerFactory
.getLogger(EditBatchProcessor.class);
private WikibaseDataFetcher fetcher; static final Logger logger = LoggerFactory.getLogger(EditBatchProcessor.class);
private WikibaseDataEditor editor;
private NewItemLibrary library;
private List<ItemUpdate> scheduled;
private String summary;
private List<ItemUpdate> remainingUpdates;
private List<ItemUpdate> currentBatch;
private int batchCursor;
private int globalCursor;
private Map<String, EntityDocument> currentDocs;
private int batchSize;
/**
* Initiates the process of pushing a batch of updates
* to Wikibase. This schedules the updates and is a
* prerequisite for calling {@link performOneEdit}.
*
* @param fetcher
* the fetcher to use to retrieve the current state of items
* @param editor
* the object to use to perform the edits
* @param updates
* the list of item updates to perform
* @param library
* the library to use to keep track of new item creation
* @param summary
* the summary to append to all edits
* @param batchSize
* the number of items that should be retrieved in one go from the API
*/
public EditBatchProcessor(WikibaseDataFetcher fetcher,
WikibaseDataEditor editor,
List<ItemUpdate> updates,
NewItemLibrary library,
String summary,
int batchSize) {
this.fetcher = fetcher;
this.editor = editor;
editor.setEditAsBot(true); // this will not do anything if the user does not
// have a bot flag, and this is generally wanted if they have one.
this.library = library;
this.summary = summary;
this.batchSize = batchSize;
// Schedule the edit batch
WikibaseAPIUpdateScheduler scheduler = new WikibaseAPIUpdateScheduler();
this.scheduled = scheduler.schedule(updates);
this.globalCursor = 0;
this.batchCursor = 0;
this.remainingUpdates = new ArrayList<>(scheduled);
this.currentBatch = Collections.emptyList();
this.currentDocs = Collections.emptyMap();
}
private WikibaseDataFetcher fetcher;
/** private WikibaseDataEditor editor;
* Performs the next edit in the batch. private NewItemLibrary library;
* private List<ItemUpdate> scheduled;
* @throws InterruptedException private String summary;
*/
public void performEdit() throws InterruptedException { private List<ItemUpdate> remainingUpdates;
if (remainingEdits() == 0) { private List<ItemUpdate> currentBatch;
return; private int batchCursor;
} private int globalCursor;
if (batchCursor == currentBatch.size()) { private Map<String, EntityDocument> currentDocs;
prepareNewBatch(); private int batchSize;
}
ItemUpdate update = currentBatch.get(batchCursor); /**
* Initiates the process of pushing a batch of updates to Wikibase. This
// Rewrite mentions to new items * schedules the updates and is a prerequisite for calling
ReconEntityRewriter rewriter = new ReconEntityRewriter(library, update.getItemId()); * {@link performOneEdit}.
update = rewriter.rewrite(update); *
* @param fetcher
try { * the fetcher to use to retrieve the current state of items
// New item * @param editor
if (update.isNew()) { * the object to use to perform the edits
ReconEntityIdValue newCell = (ReconEntityIdValue)update.getItemId(); * @param updates
update = update.normalizeLabelsAndAliases(); * the list of item updates to perform
* @param library
ItemDocument itemDocument = Datamodel.makeItemDocument(update.getItemId(), * the library to use to keep track of new item creation
update.getLabels().stream().collect(Collectors.toList()), * @param summary
update.getDescriptions().stream().collect(Collectors.toList()), * the summary to append to all edits
update.getAliases().stream().collect(Collectors.toList()), * @param batchSize
update.getAddedStatementGroups(), * the number of items that should be retrieved in one go from the
Collections.emptyMap()); * API
*/
ItemDocument createdDoc = editor.createItemDocument(itemDocument, summary); public EditBatchProcessor(WikibaseDataFetcher fetcher, WikibaseDataEditor editor, List<ItemUpdate> updates,
library.setQid(newCell.getReconInternalId(), createdDoc.getItemId().getId()); NewItemLibrary library, String summary, int batchSize) {
} else { this.fetcher = fetcher;
// Existing item this.editor = editor;
ItemDocument currentDocument = (ItemDocument)currentDocs.get(update.getItemId().getId()); editor.setEditAsBot(true); // this will not do anything if the user does not
/* // have a bot flag, and this is generally wanted if they have one.
TermStatementUpdate tsUpdate = new TermStatementUpdate( this.library = library;
currentDocument, this.summary = summary;
update.getAddedStatements().stream().collect(Collectors.toList()), this.batchSize = batchSize;
update.getDeletedStatements().stream().collect(Collectors.toList()),
update.getLabels().stream().collect(Collectors.toList()), // Schedule the edit batch
update.getDescriptions().stream().collect(Collectors.toList()), WikibaseAPIUpdateScheduler scheduler = new WikibaseAPIUpdateScheduler();
update.getAliases().stream().collect(Collectors.toList()), this.scheduled = scheduler.schedule(updates);
new ArrayList<MonolingualTextValue>() this.globalCursor = 0;
);
ObjectMapper mapper = new ObjectMapper(); this.batchCursor = 0;
logger.info(mapper.writeValueAsString(update)); this.remainingUpdates = new ArrayList<>(scheduled);
logger.info(update.toString()); this.currentBatch = Collections.emptyList();
logger.info(tsUpdate.getJsonUpdateString()); */ this.currentDocs = Collections.emptyMap();
editor.updateTermsStatements(currentDocument, }
update.getLabels().stream().collect(Collectors.toList()),
update.getDescriptions().stream().collect(Collectors.toList()), /**
update.getAliases().stream().collect(Collectors.toList()), * Performs the next edit in the batch.
new ArrayList<MonolingualTextValue>(), *
update.getAddedStatements().stream().collect(Collectors.toList()), * @throws InterruptedException
update.getDeletedStatements().stream().collect(Collectors.toList()), */
summary); public void performEdit()
} throws InterruptedException {
} catch (MediaWikiApiErrorException e) { if (remainingEdits() == 0) {
// TODO find a way to report these errors to the user in a nice way return;
e.printStackTrace(); }
} catch (IOException e) { if (batchCursor == currentBatch.size()) {
e.printStackTrace(); prepareNewBatch();
} }
ItemUpdate update = currentBatch.get(batchCursor);
batchCursor++;
} // Rewrite mentions to new items
ReconEntityRewriter rewriter = new ReconEntityRewriter(library, update.getItemId());
/** update = rewriter.rewrite(update);
* @return the number of edits that remain to be done in the current batch
*/ try {
public int remainingEdits() { // New item
return scheduled.size() - (globalCursor + batchCursor); if (update.isNew()) {
} ReconEntityIdValue newCell = (ReconEntityIdValue) update.getItemId();
update = update.normalizeLabelsAndAliases();
/**
* @return the progress, measured as a percentage ItemDocument itemDocument = Datamodel.makeItemDocument(update.getItemId(),
*/ update.getLabels().stream().collect(Collectors.toList()),
public int progress() { update.getDescriptions().stream().collect(Collectors.toList()),
return (100*(globalCursor + batchCursor)) / scheduled.size(); update.getAliases().stream().collect(Collectors.toList()), update.getAddedStatementGroups(),
} Collections.emptyMap());
protected void prepareNewBatch() throws InterruptedException { ItemDocument createdDoc = editor.createItemDocument(itemDocument, summary);
// remove the previous batch from the remainingUpdates library.setQid(newCell.getReconInternalId(), createdDoc.getItemId().getId());
globalCursor += currentBatch.size(); } else {
currentBatch.clear(); // Existing item
ItemDocument currentDocument = (ItemDocument) currentDocs.get(update.getItemId().getId());
if(remainingUpdates.size() < batchSize) { editor.updateTermsStatements(currentDocument, update.getLabels().stream().collect(Collectors.toList()),
update.getDescriptions().stream().collect(Collectors.toList()),
update.getAliases().stream().collect(Collectors.toList()),
new ArrayList<MonolingualTextValue>(),
update.getAddedStatements().stream().collect(Collectors.toList()),
update.getDeletedStatements().stream().collect(Collectors.toList()), summary);
}
} catch (MediaWikiApiErrorException e) {
// TODO find a way to report these errors to the user in a nice way
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
batchCursor++;
}
/**
* @return the number of edits that remain to be done in the current batch
*/
public int remainingEdits() {
return scheduled.size() - (globalCursor + batchCursor);
}
/**
* @return the progress, measured as a percentage
*/
public int progress() {
return (100 * (globalCursor + batchCursor)) / scheduled.size();
}
protected void prepareNewBatch()
throws InterruptedException {
// remove the previous batch from the remainingUpdates
globalCursor += currentBatch.size();
currentBatch.clear();
if (remainingUpdates.size() < batchSize) {
currentBatch = remainingUpdates; currentBatch = remainingUpdates;
remainingUpdates = Collections.emptyList(); remainingUpdates = Collections.emptyList();
} else { } else {
currentBatch = remainingUpdates.subList(0, batchSize); currentBatch = remainingUpdates.subList(0, batchSize);
} }
List<String> qidsToFetch = currentBatch.stream() List<String> qidsToFetch = currentBatch.stream().filter(u -> !u.isNew()).map(u -> u.getItemId().getId())
.filter(u -> !u.isNew()) .collect(Collectors.toList());
.map(u -> u.getItemId().getId())
.collect(Collectors.toList()); // Get the current documents for this batch of updates
logger.info("Requesting documents");
// Get the current documents for this batch of updates currentDocs = null;
logger.info("Requesting documents"); int retries = 3;
currentDocs = null; while (currentDocs == null && retries > 0) {
int retries = 3; try {
while (currentDocs == null && retries > 0) { currentDocs = fetcher.getEntityDocuments(qidsToFetch);
try { } catch (MediaWikiApiErrorException e) {
currentDocs = fetcher.getEntityDocuments(qidsToFetch); e.printStackTrace();
} catch (MediaWikiApiErrorException e) { Thread.sleep(5000);
e.printStackTrace(); }
Thread.sleep(5000); retries--;
} }
retries--; if (currentDocs == null) {
} throw new InterruptedException("Fetching current documents failed.");
if (currentDocs == null) { }
throw new InterruptedException("Fetching current documents failed."); batchCursor = 0;
} }
batchCursor = 0;
}
} }

View File

@ -1,98 +1,120 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.editing; package org.openrefine.wikidata.editing;
import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.Set;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import com.google.refine.model.Project; import java.util.Set;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.refine.model.Cell; import com.google.refine.model.Cell;
import com.google.refine.model.Column; import com.google.refine.model.Column;
import com.google.refine.model.Project;
import com.google.refine.model.Recon; import com.google.refine.model.Recon;
import com.google.refine.model.ReconCandidate; import com.google.refine.model.ReconCandidate;
import com.google.refine.model.ReconStats; import com.google.refine.model.ReconStats;
import com.google.refine.model.Row; import com.google.refine.model.Row;
/** /**
* This keeps track of the new items that we * This keeps track of the new items that we have created for each internal
* have created for each internal reconciliation id. * reconciliation id.
* *
* @author antonin * @author Antonin Delpeuch
* *
*/ */
public class NewItemLibrary { public class NewItemLibrary {
private Map<Long, String> map; private Map<Long, String> map;
public NewItemLibrary() { public NewItemLibrary() {
map = new HashMap<>(); map = new HashMap<>();
} }
@JsonCreator @JsonCreator
public NewItemLibrary(@JsonProperty("qidMap") Map<Long, String> map) { public NewItemLibrary(@JsonProperty("qidMap") Map<Long, String> map) {
this.map = map; this.map = map;
} }
/** /**
* Retrieves the Qid allocated to a given new cell * Retrieves the Qid allocated to a given new cell
* @param id: the fake ItemId generated by the cell *
* @param id:
* the fake ItemId generated by the cell
* @return the qid (or null if unallocated yet) * @return the qid (or null if unallocated yet)
*/ */
public String getQid(long id) { public String getQid(long id) {
return map.get(id); return map.get(id);
} }
/** /**
* Stores a Qid associated to a new cell * Stores a Qid associated to a new cell
* @param id : the internal reconciliation id of the new cell *
* @param qid : the associated Qid returned by Wikibase * @param id
* : the internal reconciliation id of the new cell
* @param qid
* : the associated Qid returned by Wikibase
*/ */
public void setQid(long id, String qid) { public void setQid(long id, String qid) {
map.put(id, qid); map.put(id, qid);
} }
/** /**
* Changes the "new" reconciled cells to their allocated * Changes the "new" reconciled cells to their allocated qids for later use.
* qids for later use.
* *
* @param reset: set to true to revert the operation (set cells to "new") * @param reset:
* set to true to revert the operation (set cells to "new")
*/ */
public void updateReconciledCells(Project project, boolean reset) { public void updateReconciledCells(Project project, boolean reset) {
Set<Integer> impactedColumns = new HashSet<>(); Set<Integer> impactedColumns = new HashSet<>();
/* /*
* Note that there is a slight violation of OpenRefine's model here: * Note that there is a slight violation of OpenRefine's model here: if we
* if we reconcile multiple cells to the same new item, and then * reconcile multiple cells to the same new item, and then perform this
* perform this operation on a subset of the corresponding rows, * operation on a subset of the corresponding rows, we are going to modify cells
* we are going to modify cells that are outside the facet (because * that are outside the facet (because they are reconciled to the same cell).
* they are reconciled to the same cell). But I think this is the * But I think this is the right thing to do.
* right thing to do.
*/ */
for(Row row : project.rows) { for (Row row : project.rows) {
for(int i = 0; i != row.cells.size(); i++) { for (int i = 0; i != row.cells.size(); i++) {
Cell cell = row.cells.get(i); Cell cell = row.cells.get(i);
if (cell == null || cell.recon == null) { if (cell == null || cell.recon == null) {
continue; continue;
} }
Recon recon = cell.recon; Recon recon = cell.recon;
if (Recon.Judgment.New.equals(recon.judgment) && !reset && if (Recon.Judgment.New.equals(recon.judgment) && !reset
map.containsKey(recon.judgmentHistoryEntry)) { && map.containsKey(recon.judgmentHistoryEntry)) {
recon.judgment = Recon.Judgment.Matched; recon.judgment = Recon.Judgment.Matched;
recon.match = new ReconCandidate( recon.match = new ReconCandidate(map.get(recon.judgmentHistoryEntry), cell.value.toString(),
map.get(recon.judgmentHistoryEntry), new String[0], 100);
cell.value.toString(),
new String[0],
100);
impactedColumns.add(i); impactedColumns.add(i);
} else if (Recon.Judgment.Matched.equals(recon.judgment) && reset && } else if (Recon.Judgment.Matched.equals(recon.judgment) && reset
map.containsKey(recon.judgmentHistoryEntry)) { && map.containsKey(recon.judgmentHistoryEntry)) {
recon.judgment = Recon.Judgment.New; recon.judgment = Recon.Judgment.New;
recon.match = null; recon.match = null;
impactedColumns.add(i); impactedColumns.add(i);
@ -100,35 +122,36 @@ public class NewItemLibrary {
} }
} }
// Update reconciliation statistics for impacted columns // Update reconciliation statistics for impacted columns
for(Integer colId : impactedColumns) { for (Integer colId : impactedColumns) {
Column column = project.columnModel.getColumnByCellIndex(colId); Column column = project.columnModel.getColumnByCellIndex(colId);
column.setReconStats(ReconStats.create(project, colId)); column.setReconStats(ReconStats.create(project, colId));
} }
} }
/** /**
* Getter, only meant to be used by Jackson * Getter, only meant to be used by Jackson
*
* @return the underlying map * @return the underlying map
*/ */
@JsonProperty("qidMap") @JsonProperty("qidMap")
public Map<Long, String> getQidMap() { public Map<Long, String> getQidMap() {
return map; return map;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !NewItemLibrary.class.isInstance(other)) { if (other == null || !NewItemLibrary.class.isInstance(other)) {
return false; return false;
} }
NewItemLibrary otherLibrary = (NewItemLibrary)other; NewItemLibrary otherLibrary = (NewItemLibrary) other;
return map.equals(otherLibrary.getQidMap()); return map.equals(otherLibrary.getQidMap());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return map.hashCode(); return map.hashCode();
} }
@Override @Override
public String toString() { public String toString() {
return map.toString(); return map.toString();

View File

@ -1,46 +1,64 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.editing; package org.openrefine.wikidata.editing;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.openrefine.wikidata.schema.entityvalues.ReconEntityIdValue;
import org.openrefine.wikidata.schema.entityvalues.ReconItemIdValue; import org.openrefine.wikidata.schema.entityvalues.ReconItemIdValue;
import org.openrefine.wikidata.updates.ItemUpdate; import org.openrefine.wikidata.updates.ItemUpdate;
import org.wikidata.wdtk.datamodel.helpers.Datamodel; import org.wikidata.wdtk.datamodel.helpers.Datamodel;
import org.wikidata.wdtk.datamodel.helpers.DatamodelConverter; import org.wikidata.wdtk.datamodel.helpers.DatamodelConverter;
import org.wikidata.wdtk.datamodel.implementation.DataObjectFactoryImpl; import org.wikidata.wdtk.datamodel.implementation.DataObjectFactoryImpl;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue; import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
import org.wikidata.wdtk.datamodel.interfaces.MonolingualTextValue; import org.wikidata.wdtk.datamodel.interfaces.MonolingualTextValue;
import org.wikidata.wdtk.datamodel.interfaces.Statement; import org.wikidata.wdtk.datamodel.interfaces.Statement;
import org.wikidata.wdtk.datamodel.interfaces.Value;
/** /**
* A class that rewrites an {@link ItemUpdate}, * A class that rewrites an {@link ItemUpdate}, replacing reconciled entity id
* replacing reconciled entity id values by their concrete * values by their concrete values after creation of all the new items involved.
* values after creation of all the new items involved.
* *
* If an item has not been created yet, an {@link IllegalArgumentException} * If an item has not been created yet, an {@link IllegalArgumentException} will
* will be raised. * be raised.
* *
* The subject is treated as a special case: it is returned unchanged. * The subject is treated as a special case: it is returned unchanged. This is
* This is because it is guaranteed not to appear in the update (but * because it is guaranteed not to appear in the update (but it does appear in
* it does appear in the datamodel representation as the subject is passed around * the datamodel representation as the subject is passed around to the Claim
* to the Claim objects its document contains). * objects its document contains).
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public class ReconEntityRewriter extends DatamodelConverter { public class ReconEntityRewriter extends DatamodelConverter {
private NewItemLibrary library; private NewItemLibrary library;
private ItemIdValue subject; private ItemIdValue subject;
/** /**
* Constructor. Sets up a rewriter which uses the provided library * Constructor. Sets up a rewriter which uses the provided library to look up
* to look up qids of new items, and the subject (which should not be * qids of new items, and the subject (which should not be rewritten).
* rewritten).
* *
* @param library * @param library
* @param subject * @param subject
@ -50,39 +68,35 @@ public class ReconEntityRewriter extends DatamodelConverter {
this.library = library; this.library = library;
this.subject = subject; this.subject = subject;
} }
@Override @Override
public ItemIdValue copy(ItemIdValue value) { public ItemIdValue copy(ItemIdValue value) {
if(subject.equals(value)) { if (subject.equals(value)) {
return value; return value;
} }
if(value instanceof ReconItemIdValue) { if (value instanceof ReconItemIdValue) {
ReconItemIdValue recon = (ReconItemIdValue)value; ReconItemIdValue recon = (ReconItemIdValue) value;
if(recon.isNew()) { if (recon.isNew()) {
String newId = library.getQid(recon.getReconInternalId()); String newId = library.getQid(recon.getReconInternalId());
if(newId == null) { if (newId == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Trying to rewrite an update where a new item was not created yet."); "Trying to rewrite an update where a new item was not created yet.");
} }
return Datamodel.makeItemIdValue(newId, return Datamodel.makeItemIdValue(newId, recon.getRecon().identifierSpace);
recon.getRecon().identifierSpace);
} }
} }
return super.copy(value); return super.copy(value);
} }
public ItemUpdate rewrite(ItemUpdate update) { public ItemUpdate rewrite(ItemUpdate update) {
Set<MonolingualTextValue> labels = update.getLabels().stream() Set<MonolingualTextValue> labels = update.getLabels().stream().map(l -> copy(l)).collect(Collectors.toSet());
.map(l -> copy(l)).collect(Collectors.toSet()); Set<MonolingualTextValue> descriptions = update.getDescriptions().stream().map(l -> copy(l))
Set<MonolingualTextValue> descriptions = update.getDescriptions().stream() .collect(Collectors.toSet());
.map(l -> copy(l)).collect(Collectors.toSet()); Set<MonolingualTextValue> aliases = update.getAliases().stream().map(l -> copy(l)).collect(Collectors.toSet());
Set<MonolingualTextValue> aliases = update.getAliases().stream() List<Statement> addedStatements = update.getAddedStatements().stream().map(l -> copy(l))
.map(l -> copy(l)).collect(Collectors.toSet()); .collect(Collectors.toList());
List<Statement> addedStatements = update.getAddedStatements().stream() Set<Statement> deletedStatements = update.getDeletedStatements().stream().map(l -> copy(l))
.map(l -> copy(l)).collect(Collectors.toList()); .collect(Collectors.toSet());
Set<Statement> deletedStatements = update.getDeletedStatements().stream() return new ItemUpdate(update.getItemId(), addedStatements, deletedStatements, labels, descriptions, aliases);
.map(l -> copy(l)).collect(Collectors.toSet());
return new ItemUpdate(update.getItemId(), addedStatements,
deletedStatements, labels, descriptions, aliases);
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.editing; package org.openrefine.wikidata.editing;
import java.util.Properties; import java.util.Properties;
@ -9,37 +32,37 @@ import org.json.JSONWriter;
import com.google.refine.Jsonizable; import com.google.refine.Jsonizable;
/** /**
* This is just the necessary bits to store Wikidata credentials * This is just the necessary bits to store Wikidata credentials in OpenRefine's
* in OpenRefine's preference store. * preference store.
* *
* @author antonin * @author Antonin Delpeuch
* *
*/ */
class WikibaseCredentials implements Jsonizable { class WikibaseCredentials implements Jsonizable {
private String username; private String username;
private String password; private String password;
public WikibaseCredentials() { public WikibaseCredentials() {
username = null; username = null;
password = null; password = null;
} }
public WikibaseCredentials(String username, String password) { public WikibaseCredentials(String username, String password) {
this.username = username; this.username = username;
this.password = password; this.password = password;
} }
public String getUsername() { public String getUsername() {
return username; return username;
} }
public String getPassword() { public String getPassword() {
return password; return password;
} }
public boolean isNonNull() { public boolean isNonNull() {
return username != null && password != null && ! "null".equals(username) && ! "null".equals(password); return username != null && password != null && !"null".equals(username) && !"null".equals(password);
} }
@Override @Override
@ -54,12 +77,10 @@ class WikibaseCredentials implements Jsonizable {
writer.value(password); writer.value(password);
writer.endObject(); writer.endObject();
} }
public static WikibaseCredentials load(JSONObject obj) throws JSONException {
return new WikibaseCredentials(
obj.getString("username"),
obj.getString("password"));
}
}
public static WikibaseCredentials load(JSONObject obj)
throws JSONException {
return new WikibaseCredentials(obj.getString("username"), obj.getString("password"));
}
}

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.exporters; package org.openrefine.wikidata.exporters;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -14,14 +37,12 @@ import org.wikidata.wdtk.datamodel.interfaces.TimeValue;
import org.wikidata.wdtk.datamodel.interfaces.ValueVisitor; import org.wikidata.wdtk.datamodel.interfaces.ValueVisitor;
/** /**
* Prints a Wikibase value as a string as required by QuickStatements. * Prints a Wikibase value as a string as required by QuickStatements. Format
* Format documentation: * documentation: https://www.wikidata.org/wiki/Help:QuickStatements
* https://www.wikidata.org/wiki/Help:QuickStatements
* *
* Any new entity id will be * Any new entity id will be assumed to be the last one created, represented
* assumed to be the last one created, represented with "LAST". It is * with "LAST". It is fine to do this assumption because we are working on edit
* fine to do this assumption because we are working on edit batches * batches previously scheduled by {@link QuickStatementsUpdateScheduler}.
* previously scheduled by {@link QuickStatementsUpdateScheduler}.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
@ -37,7 +58,7 @@ public class QSValuePrinter implements ValueVisitor<String> {
@Override @Override
public String visit(EntityIdValue value) { public String visit(EntityIdValue value) {
if (ReconEntityIdValue.class.isInstance(value) && ((ReconEntityIdValue)value).isNew()) { if (ReconEntityIdValue.class.isInstance(value) && ((ReconEntityIdValue) value).isNew()) {
return "LAST"; return "LAST";
} }
return value.getId(); return value.getId();
@ -45,19 +66,12 @@ public class QSValuePrinter implements ValueVisitor<String> {
@Override @Override
public String visit(GlobeCoordinatesValue value) { public String visit(GlobeCoordinatesValue value) {
return String.format( return String.format(Locale.US, "@%f/%f", value.getLatitude(), value.getLongitude());
Locale.US,
"@%f/%f",
value.getLatitude(),
value.getLongitude());
} }
@Override @Override
public String visit(MonolingualTextValue value) { public String visit(MonolingualTextValue value) {
return String.format( return String.format("%s:\"%s\"", value.getLanguageCode(), value.getText());
"%s:\"%s\"",
value.getLanguageCode(),
value.getText());
} }
@Override @Override
@ -66,22 +80,16 @@ public class QSValuePrinter implements ValueVisitor<String> {
String unitIri = value.getUnit(); String unitIri = value.getUnit();
String unitRepresentation = "", boundsRepresentation = ""; String unitRepresentation = "", boundsRepresentation = "";
if (!unitIri.isEmpty()) { if (!unitIri.isEmpty()) {
if (!unitIri.startsWith(unitPrefix)) if (!unitIri.startsWith(unitPrefix)) return null; // QuickStatements only accepts Qids as units
return null; // QuickStatements only accepts Qids as units unitRepresentation = "U" + unitIri.substring(unitPrefix.length());
unitRepresentation = "U"+unitIri.substring(unitPrefix.length());
} }
if (value.getLowerBound() != null) { if (value.getLowerBound() != null) {
// bounds are always null at the same time so we know they are both not null // bounds are always null at the same time so we know they are both not null
BigDecimal lowerBound = value.getLowerBound(); BigDecimal lowerBound = value.getLowerBound();
BigDecimal upperBound = value.getUpperBound(); BigDecimal upperBound = value.getUpperBound();
boundsRepresentation = String.format(Locale.US, "[%s,%s]", boundsRepresentation = String.format(Locale.US, "[%s,%s]", lowerBound.toString(), upperBound.toString());
lowerBound.toString(), upperBound.toString());
} }
return String.format( return String.format(Locale.US, "%s%s%s", value.getNumericValue().toString(), boundsRepresentation,
Locale.US,
"%s%s%s",
value.getNumericValue().toString(),
boundsRepresentation,
unitRepresentation); unitRepresentation);
} }
@ -92,14 +100,7 @@ public class QSValuePrinter implements ValueVisitor<String> {
@Override @Override
public String visit(TimeValue value) { public String visit(TimeValue value) {
return String.format( return String.format("+%04d-%02d-%02dT%02d:%02d:%02dZ/%d", value.getYear(), value.getMonth(), value.getDay(),
"+%04d-%02d-%02dT%02d:%02d:%02dZ/%d", value.getHour(), value.getMinute(), value.getSecond(), value.getPrecision());
value.getYear(),
value.getMonth(),
value.getDay(),
value.getHour(),
value.getMinute(),
value.getSecond(),
value.getPrecision());
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.exporters; package org.openrefine.wikidata.exporters;
import java.io.IOException; import java.io.IOException;
@ -29,15 +52,13 @@ import org.wikidata.wdtk.datamodel.interfaces.ValueVisitor;
public class QuickStatementsExporter implements WriterExporter { public class QuickStatementsExporter implements WriterExporter {
final static Logger logger = LoggerFactory.getLogger("QuickStatementsExporter"); final static Logger logger = LoggerFactory.getLogger("QuickStatementsExporter");
public static final String impossibleSchedulingErrorMessage =
"This edit batch cannot be performed with QuickStatements due to the structure of its new items.";
public static final String noSchemaErrorMessage =
"No schema was provided. You need to align your project with Wikidata first.";
public QuickStatementsExporter(){ public static final String impossibleSchedulingErrorMessage = "This edit batch cannot be performed with QuickStatements due to the structure of its new items.";
public static final String noSchemaErrorMessage = "No schema was provided. You need to align your project with Wikidata first.";
public QuickStatementsExporter() {
} }
@Override @Override
public String getContentType() { public String getContentType() {
return "text/plain"; return "text/plain";
@ -53,41 +74,45 @@ public class QuickStatementsExporter implements WriterExporter {
translateSchema(project, engine, schema, writer); translateSchema(project, engine, schema, writer);
} }
} }
/** /**
* Exports a project and a schema to a QuickStatements file * Exports a project and a schema to a QuickStatements file
* *
* @param project * @param project
* the project to translate * the project to translate
* @param engine * @param engine
* the engine used for evaluation of the edits * the engine used for evaluation of the edits
* @param schema * @param schema
* the WikibaseSchema used for translation of tabular data to edits * the WikibaseSchema used for translation of tabular data to edits
* @param writer * @param writer
* the writer to which the QS should be written * the writer to which the QS should be written
* @throws IOException * @throws IOException
*/ */
public void translateSchema(Project project, Engine engine, WikibaseSchema schema, Writer writer) throws IOException { public void translateSchema(Project project, Engine engine, WikibaseSchema schema, Writer writer)
throws IOException {
List<ItemUpdate> items = schema.evaluate(project, engine); List<ItemUpdate> items = schema.evaluate(project, engine);
translateItemList(items, writer); translateItemList(items, writer);
} }
public void translateItemList(List<ItemUpdate> updates, Writer writer) throws IOException { public void translateItemList(List<ItemUpdate> updates, Writer writer)
throws IOException {
QuickStatementsUpdateScheduler scheduler = new QuickStatementsUpdateScheduler(); QuickStatementsUpdateScheduler scheduler = new QuickStatementsUpdateScheduler();
try { try {
List<ItemUpdate> scheduled = scheduler.schedule(updates); List<ItemUpdate> scheduled = scheduler.schedule(updates);
for (ItemUpdate item : scheduled) { for (ItemUpdate item : scheduled) {
translateItem(item, writer); translateItem(item, writer);
} }
} catch(ImpossibleSchedulingException e) { } catch (ImpossibleSchedulingException e) {
writer.write(impossibleSchedulingErrorMessage); writer.write(impossibleSchedulingErrorMessage);
} }
} }
protected void translateNameDescr(String qid, Set<MonolingualTextValue> values, String prefix, ItemIdValue id, Writer writer) throws IOException { protected void translateNameDescr(String qid, Set<MonolingualTextValue> values, String prefix, ItemIdValue id,
Writer writer)
throws IOException {
for (MonolingualTextValue value : values) { for (MonolingualTextValue value : values) {
writer.write(qid+"\t"); writer.write(qid + "\t");
writer.write(prefix); writer.write(prefix);
writer.write(value.getLanguageCode()); writer.write(value.getLanguageCode());
writer.write("\t\""); writer.write("\t\"");
@ -95,19 +120,20 @@ public class QuickStatementsExporter implements WriterExporter {
writer.write("\"\n"); writer.write("\"\n");
} }
} }
protected void translateItem(ItemUpdate item, Writer writer) throws IOException { protected void translateItem(ItemUpdate item, Writer writer)
throws IOException {
String qid = item.getItemId().getId(); String qid = item.getItemId().getId();
if (item.isNew()) { if (item.isNew()) {
writer.write("CREATE\n"); writer.write("CREATE\n");
qid = "LAST"; qid = "LAST";
item = item.normalizeLabelsAndAliases(); item = item.normalizeLabelsAndAliases();
} }
translateNameDescr(qid, item.getLabels(), "L", item.getItemId(), writer); translateNameDescr(qid, item.getLabels(), "L", item.getItemId(), writer);
translateNameDescr(qid, item.getDescriptions(), "D", item.getItemId(), writer); translateNameDescr(qid, item.getDescriptions(), "D", item.getItemId(), writer);
translateNameDescr(qid, item.getAliases(), "A", item.getItemId(), writer); translateNameDescr(qid, item.getAliases(), "A", item.getItemId(), writer);
for (Statement s : item.getAddedStatements()) { for (Statement s : item.getAddedStatements()) {
translateStatement(qid, s, s.getClaim().getMainSnak().getPropertyId().getId(), true, writer); translateStatement(qid, s, s.getClaim().getMainSnak().getPropertyId().getId(), true, writer);
} }
@ -115,38 +141,41 @@ public class QuickStatementsExporter implements WriterExporter {
translateStatement(qid, s, s.getClaim().getMainSnak().getPropertyId().getId(), false, writer); translateStatement(qid, s, s.getClaim().getMainSnak().getPropertyId().getId(), false, writer);
} }
} }
protected void translateStatement(String qid, Statement statement, String pid, boolean add, Writer writer) throws IOException { protected void translateStatement(String qid, Statement statement, String pid, boolean add, Writer writer)
throws IOException {
Claim claim = statement.getClaim(); Claim claim = statement.getClaim();
Value val = claim.getValue(); Value val = claim.getValue();
ValueVisitor<String> vv = new QSValuePrinter(); ValueVisitor<String> vv = new QSValuePrinter();
String targetValue = val.accept(vv); String targetValue = val.accept(vv);
if (targetValue != null) { if (targetValue != null) {
if (! add) { if (!add) {
writer.write("- "); writer.write("- ");
} }
writer.write(qid + "\t" + pid + "\t" + targetValue); writer.write(qid + "\t" + pid + "\t" + targetValue);
for(SnakGroup q : claim.getQualifiers()) { for (SnakGroup q : claim.getQualifiers()) {
translateSnakGroup(q, false, writer); translateSnakGroup(q, false, writer);
} }
for(Reference r : statement.getReferences()) { for (Reference r : statement.getReferences()) {
for(SnakGroup g : r.getSnakGroups()) { for (SnakGroup g : r.getSnakGroups()) {
translateSnakGroup(g, true, writer); translateSnakGroup(g, true, writer);
} }
break; // QS only supports one reference break; // QS only supports one reference
} }
writer.write("\n"); writer.write("\n");
} }
} }
protected void translateSnakGroup(SnakGroup sg, boolean reference, Writer writer) throws IOException { protected void translateSnakGroup(SnakGroup sg, boolean reference, Writer writer)
for(Snak s : sg.getSnaks()) { throws IOException {
for (Snak s : sg.getSnaks()) {
translateSnak(s, reference, writer); translateSnak(s, reference, writer);
} }
} }
protected void translateSnak(Snak s, boolean reference, Writer writer) throws IOException { protected void translateSnak(Snak s, boolean reference, Writer writer)
throws IOException {
String pid = s.getPropertyId().getId(); String pid = s.getPropertyId().getId();
if (reference) { if (reference) {
pid = pid.replace('P', 'S'); pid = pid.replace('P', 'S');
@ -154,10 +183,9 @@ public class QuickStatementsExporter implements WriterExporter {
Value val = s.getValue(); Value val = s.getValue();
ValueVisitor<String> vv = new QSValuePrinter(); ValueVisitor<String> vv = new QSValuePrinter();
String valStr = val.accept(vv); String valStr = val.accept(vv);
if(valStr != null) { if (valStr != null) {
writer.write("\t" + pid + "\t" + valStr); writer.write("\t" + pid + "\t" + valStr);
} }
} }
} }

View File

@ -1,44 +1,48 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.operations; package org.openrefine.wikidata.operations;
import java.io.IOException; import java.io.IOException;
import java.io.LineNumberReader; import java.io.LineNumberReader;
import java.io.Writer; import java.io.Writer;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.stream.Collectors;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONWriter; import org.json.JSONWriter;
import org.openrefine.wikidata.editing.ConnectionManager; import org.openrefine.wikidata.editing.ConnectionManager;
import org.openrefine.wikidata.editing.EditBatchProcessor; import org.openrefine.wikidata.editing.EditBatchProcessor;
import org.openrefine.wikidata.editing.NewItemLibrary; import org.openrefine.wikidata.editing.NewItemLibrary;
import org.openrefine.wikidata.editing.ReconEntityRewriter;
import org.openrefine.wikidata.updates.ItemUpdate;
import org.openrefine.wikidata.updates.scheduler.ImpossibleSchedulingException;
import org.openrefine.wikidata.updates.scheduler.UpdateScheduler;
import org.openrefine.wikidata.updates.scheduler.WikibaseAPIUpdateScheduler;
import org.openrefine.wikidata.schema.WikibaseSchema; import org.openrefine.wikidata.schema.WikibaseSchema;
import org.openrefine.wikidata.schema.entityvalues.ReconEntityIdValue; import org.openrefine.wikidata.updates.ItemUpdate;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.wikidata.wdtk.datamodel.implementation.DataObjectFactoryImpl;
import org.wikidata.wdtk.datamodel.interfaces.DataObjectFactory;
import org.wikidata.wdtk.datamodel.interfaces.EntityDocument;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
import org.wikidata.wdtk.datamodel.interfaces.ItemDocument;
import org.wikidata.wdtk.datamodel.interfaces.MonolingualTextValue;
import org.wikidata.wdtk.util.WebResourceFetcherImpl; import org.wikidata.wdtk.util.WebResourceFetcherImpl;
import org.wikidata.wdtk.wikibaseapi.ApiConnection; import org.wikidata.wdtk.wikibaseapi.ApiConnection;
import org.wikidata.wdtk.wikibaseapi.TermStatementUpdate;
import org.wikidata.wdtk.wikibaseapi.WikibaseDataEditor; import org.wikidata.wdtk.wikibaseapi.WikibaseDataEditor;
import org.wikidata.wdtk.wikibaseapi.WikibaseDataFetcher; import org.wikidata.wdtk.wikibaseapi.WikibaseDataFetcher;
import org.wikidata.wdtk.wikibaseapi.apierrors.MediaWikiApiErrorException;
import org.wikidata.wdtk.datamodel.interfaces.SiteLink;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -53,22 +57,19 @@ import com.google.refine.process.LongRunningProcess;
import com.google.refine.process.Process; import com.google.refine.process.Process;
import com.google.refine.util.Pool; import com.google.refine.util.Pool;
public class PerformWikibaseEditsOperation extends EngineDependentOperation { public class PerformWikibaseEditsOperation extends EngineDependentOperation {
static final Logger logger = LoggerFactory
.getLogger(PerformWikibaseEditsOperation.class); static final Logger logger = LoggerFactory.getLogger(PerformWikibaseEditsOperation.class);
private String summary; private String summary;
public PerformWikibaseEditsOperation( public PerformWikibaseEditsOperation(JSONObject engineConfig, String summary) {
JSONObject engineConfig,
String summary) {
super(engineConfig); super(engineConfig);
this.summary = summary; this.summary = summary;
// getEngine(request, project); // getEngine(request, project);
} }
static public AbstractOperation reconstruct(Project project, JSONObject obj) static public AbstractOperation reconstruct(Project project, JSONObject obj)
throws Exception { throws Exception {
JSONObject engineConfig = obj.getJSONObject("engineConfig"); JSONObject engineConfig = obj.getJSONObject("engineConfig");
@ -76,12 +77,9 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation {
if (obj.has("summary")) { if (obj.has("summary")) {
summary = obj.getString("summary"); summary = obj.getString("summary");
} }
return new PerformWikibaseEditsOperation( return new PerformWikibaseEditsOperation(engineConfig, summary);
engineConfig,
summary);
} }
@Override @Override
public void write(JSONWriter writer, Properties options) public void write(JSONWriter writer, Properties options)
throws JSONException { throws JSONException {
@ -96,26 +94,22 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation {
writer.value(getEngineConfig()); writer.value(getEngineConfig());
writer.endObject(); writer.endObject();
} }
@Override @Override
protected String getBriefDescription(Project project) { protected String getBriefDescription(Project project) {
return "Peform edits on Wikidata"; return "Peform edits on Wikidata";
} }
@Override @Override
public Process createProcess(Project project, Properties options) throws Exception { public Process createProcess(Project project, Properties options)
return new PerformEditsProcess( throws Exception {
project, return new PerformEditsProcess(project, createEngine(project), getBriefDescription(project), summary);
createEngine(project),
getBriefDescription(project),
summary
);
} }
static public class PerformWikibaseEditsChange implements Change { static public class PerformWikibaseEditsChange implements Change {
private NewItemLibrary newItemLibrary; private NewItemLibrary newItemLibrary;
public PerformWikibaseEditsChange(NewItemLibrary library) { public PerformWikibaseEditsChange(NewItemLibrary library) {
newItemLibrary = library; newItemLibrary = library;
} }
@ -128,7 +122,7 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation {
@Override @Override
public void revert(Project project) { public void revert(Project project) {
// this does not do anything on Wikibase side - // this does not do anything on Wikibase side -
// (we don't revert changes on Wikidata either) // (we don't revert changes on Wikidata either)
newItemLibrary.updateReconciledCells(project, true); newItemLibrary.updateReconciledCells(project, true);
} }
@ -139,11 +133,11 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation {
if (newItemLibrary != null) { if (newItemLibrary != null) {
writer.write("newItems="); writer.write("newItems=");
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
writer.write(mapper.writeValueAsString(newItemLibrary)+"\n"); writer.write(mapper.writeValueAsString(newItemLibrary) + "\n");
} }
writer.write("/ec/\n"); // end of change writer.write("/ec/\n"); // end of change
} }
static public Change load(LineNumberReader reader, Pool pool) static public Change load(LineNumberReader reader, Pool pool)
throws Exception { throws Exception {
NewItemLibrary library = new NewItemLibrary(); NewItemLibrary library = new NewItemLibrary();
@ -152,7 +146,7 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation {
int equal = line.indexOf('='); int equal = line.indexOf('=');
CharSequence field = line.subSequence(0, equal); CharSequence field = line.subSequence(0, equal);
String value = line.substring(equal + 1); String value = line.substring(equal + 1);
if ("newItems".equals(field)) { if ("newItems".equals(field)) {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
library = mapper.readValue(value, NewItemLibrary.class); library = mapper.readValue(value, NewItemLibrary.class);
@ -160,19 +154,18 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation {
} }
return new PerformWikibaseEditsChange(library); return new PerformWikibaseEditsChange(library);
} }
} }
public class PerformEditsProcess extends LongRunningProcess implements Runnable { public class PerformEditsProcess extends LongRunningProcess implements Runnable {
protected Project _project; protected Project _project;
protected Engine _engine; protected Engine _engine;
protected WikibaseSchema _schema; protected WikibaseSchema _schema;
protected String _summary; protected String _summary;
protected final long _historyEntryID; protected final long _historyEntryID;
protected PerformEditsProcess(Project project, protected PerformEditsProcess(Project project, Engine engine, String description, String summary) {
Engine engine, String description, String summary) {
super(description); super(description);
this._project = project; this._project = project;
this._engine = engine; this._engine = engine;
@ -183,7 +176,7 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation {
@Override @Override
public void run() { public void run() {
WebResourceFetcherImpl.setUserAgent("OpenRefine Wikidata extension"); WebResourceFetcherImpl.setUserAgent("OpenRefine Wikidata extension");
ConnectionManager manager = ConnectionManager.getInstance(); ConnectionManager manager = ConnectionManager.getInstance();
if (!manager.isLoggedIn()) { if (!manager.isLoggedIn()) {
@ -193,21 +186,21 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation {
WikibaseDataFetcher wbdf = new WikibaseDataFetcher(connection, _schema.getBaseIri()); WikibaseDataFetcher wbdf = new WikibaseDataFetcher(connection, _schema.getBaseIri());
WikibaseDataEditor wbde = new WikibaseDataEditor(connection, _schema.getBaseIri()); WikibaseDataEditor wbde = new WikibaseDataEditor(connection, _schema.getBaseIri());
// Evaluate the schema // Evaluate the schema
List<ItemUpdate> itemDocuments = _schema.evaluate(_project, _engine); List<ItemUpdate> itemDocuments = _schema.evaluate(_project, _engine);
// Prepare the edits // Prepare the edits
NewItemLibrary newItemLibrary = new NewItemLibrary(); NewItemLibrary newItemLibrary = new NewItemLibrary();
EditBatchProcessor processor = new EditBatchProcessor(wbdf, EditBatchProcessor processor = new EditBatchProcessor(wbdf, wbde, itemDocuments, newItemLibrary, _summary,
wbde, itemDocuments, newItemLibrary, _summary, 50); 50);
// Perform edits // Perform edits
logger.info("Performing edits"); logger.info("Performing edits");
while(processor.remainingEdits() > 0) { while (processor.remainingEdits() > 0) {
try { try {
processor.performEdit(); processor.performEdit();
} catch(InterruptedException e) { } catch (InterruptedException e) {
_canceled = true; _canceled = true;
} }
_progress = processor.progress(); _progress = processor.progress();
@ -215,20 +208,15 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation {
break; break;
} }
} }
_progress = 100; _progress = 100;
if (!_canceled) { if (!_canceled) {
Change change = new PerformWikibaseEditsChange(newItemLibrary); Change change = new PerformWikibaseEditsChange(newItemLibrary);
HistoryEntry historyEntry = new HistoryEntry( HistoryEntry historyEntry = new HistoryEntry(_historyEntryID, _project, _description,
_historyEntryID, PerformWikibaseEditsOperation.this, change);
_project,
_description,
PerformWikibaseEditsOperation.this,
change
);
_project.history.addEntry(historyEntry); _project.history.addEntry(historyEntry);
_project.processManager.onDoneProcess(this); _project.processManager.onDoneProcess(this);
} }

View File

@ -1,24 +1,38 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.operations; package org.openrefine.wikidata.operations;
import java.io.IOException; import java.io.IOException;
import java.io.LineNumberReader; import java.io.LineNumberReader;
import java.io.Writer; import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONWriter; import org.json.JSONWriter;
import org.openrefine.wikidata.schema.WbItemConstant;
import org.openrefine.wikidata.schema.WbItemDocumentExpr;
import org.openrefine.wikidata.schema.WbNameDescExpr;
import org.openrefine.wikidata.schema.WbStatementGroupExpr;
import org.openrefine.wikidata.schema.WikibaseSchema; import org.openrefine.wikidata.schema.WikibaseSchema;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.refine.history.Change; import com.google.refine.history.Change;
import com.google.refine.history.HistoryEntry; import com.google.refine.history.HistoryEntry;
import com.google.refine.model.AbstractOperation; import com.google.refine.model.AbstractOperation;
@ -33,14 +47,12 @@ public class SaveWikibaseSchemaOperation extends AbstractOperation {
public SaveWikibaseSchemaOperation(WikibaseSchema schema) { public SaveWikibaseSchemaOperation(WikibaseSchema schema) {
this._schema = schema; this._schema = schema;
} }
static public AbstractOperation reconstruct(Project project, JSONObject obj) static public AbstractOperation reconstruct(Project project, JSONObject obj)
throws Exception { throws Exception {
return new SaveWikibaseSchemaOperation(WikibaseSchema.reconstruct(obj return new SaveWikibaseSchemaOperation(WikibaseSchema.reconstruct(obj.getJSONObject("schema")));
.getJSONObject("schema")));
} }
public void write(JSONWriter writer, Properties options) public void write(JSONWriter writer, Properties options)
@ -62,32 +74,32 @@ public class SaveWikibaseSchemaOperation extends AbstractOperation {
} }
@Override @Override
protected HistoryEntry createHistoryEntry(Project project, protected HistoryEntry createHistoryEntry(Project project, long historyEntryID)
long historyEntryID) throws Exception { throws Exception {
String description = "Save Wikibase schema skeleton"; String description = "Save Wikibase schema skeleton";
Change change = new WikibaseSchemaChange(_schema); Change change = new WikibaseSchemaChange(_schema);
return new HistoryEntry(historyEntryID, project, description, return new HistoryEntry(historyEntryID, project, description, SaveWikibaseSchemaOperation.this, change);
SaveWikibaseSchemaOperation.this, change);
} }
static public class WikibaseSchemaChange implements Change { static public class WikibaseSchemaChange implements Change {
final protected WikibaseSchema _newSchema; final protected WikibaseSchema _newSchema;
protected WikibaseSchema _oldSchema = null; protected WikibaseSchema _oldSchema = null;
public final static String overlayModelKey = "wikibaseSchema"; public final static String overlayModelKey = "wikibaseSchema";
public WikibaseSchemaChange(WikibaseSchema newSchema) { public WikibaseSchemaChange(WikibaseSchema newSchema) {
_newSchema = newSchema; _newSchema = newSchema;
} }
public void apply(Project project) { public void apply(Project project) {
synchronized (project) { synchronized (project) {
_oldSchema = (WikibaseSchema) project.overlayModels.get(overlayModelKey); _oldSchema = (WikibaseSchema) project.overlayModels.get(overlayModelKey);
project.overlayModels.put(overlayModelKey, _newSchema); project.overlayModels.put(overlayModelKey, _newSchema);
} }
} }
public void revert(Project project) { public void revert(Project project) {
synchronized (project) { synchronized (project) {
if (_oldSchema == null) { if (_oldSchema == null) {
@ -97,8 +109,9 @@ public class SaveWikibaseSchemaOperation extends AbstractOperation {
} }
} }
} }
public void save(Writer writer, Properties options) throws IOException { public void save(Writer writer, Properties options)
throws IOException {
writer.write("newSchema="); writer.write("newSchema=");
writeWikibaseSchema(_newSchema, writer); writeWikibaseSchema(_newSchema, writer);
writer.write('\n'); writer.write('\n');
@ -107,33 +120,31 @@ public class SaveWikibaseSchemaOperation extends AbstractOperation {
writer.write('\n'); writer.write('\n');
writer.write("/ec/\n"); // end of change marker writer.write("/ec/\n"); // end of change marker
} }
static public Change load(LineNumberReader reader, Pool pool) static public Change load(LineNumberReader reader, Pool pool)
throws Exception { throws Exception {
WikibaseSchema oldSchema = null; WikibaseSchema oldSchema = null;
WikibaseSchema newSchema = null; WikibaseSchema newSchema = null;
String line; String line;
while ((line = reader.readLine()) != null && !"/ec/".equals(line)) { while ((line = reader.readLine()) != null && !"/ec/".equals(line)) {
int equal = line.indexOf('='); int equal = line.indexOf('=');
CharSequence field = line.subSequence(0, equal); CharSequence field = line.subSequence(0, equal);
String value = line.substring(equal + 1); String value = line.substring(equal + 1);
if ("oldSchema".equals(field) && value.length() > 0) { if ("oldSchema".equals(field) && value.length() > 0) {
oldSchema = WikibaseSchema.reconstruct(ParsingUtilities oldSchema = WikibaseSchema.reconstruct(ParsingUtilities.evaluateJsonStringToObject(value));
.evaluateJsonStringToObject(value));
} else if ("newSchema".equals(field) && value.length() > 0) { } else if ("newSchema".equals(field) && value.length() > 0) {
newSchema = WikibaseSchema.reconstruct(ParsingUtilities newSchema = WikibaseSchema.reconstruct(ParsingUtilities.evaluateJsonStringToObject(value));
.evaluateJsonStringToObject(value));
} }
} }
WikibaseSchemaChange change = new WikibaseSchemaChange(newSchema); WikibaseSchemaChange change = new WikibaseSchemaChange(newSchema);
change._oldSchema = oldSchema; change._oldSchema = oldSchema;
return change; return change;
} }
static protected void writeWikibaseSchema(WikibaseSchema s, Writer writer) static protected void writeWikibaseSchema(WikibaseSchema s, Writer writer)
throws IOException { throws IOException {
if (s != null) { if (s != null) {

View File

@ -1,4 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa; package org.openrefine.wikidata.qa;
import java.util.Set; import java.util.Set;
@ -14,16 +36,20 @@ import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
public interface ConstraintFetcher { public interface ConstraintFetcher {
/** /**
* Retrieves the regular expression for formatting a property, or null if * Retrieves the regular expression for formatting a property, or null if there
* there is no such constraint * is no such constraint
*
* @param pid * @param pid
* @return the expression of a regular expression which should be compatible with java.util.regex * @return the expression of a regular expression which should be compatible
* with java.util.regex
*/ */
String getFormatRegex(PropertyIdValue pid); String getFormatRegex(PropertyIdValue pid);
/** /**
* Retrieves the property that is the inverse of a given property * Retrieves the property that is the inverse of a given property
* @param pid: the property to retrieve the inverse for *
* @param pid:
* the property to retrieve the inverse for
* @return the pid of the inverse property * @return the pid of the inverse property
*/ */
PropertyIdValue getInversePid(PropertyIdValue pid); PropertyIdValue getInversePid(PropertyIdValue pid);
@ -44,12 +70,14 @@ public interface ConstraintFetcher {
boolean isForReferencesOnly(PropertyIdValue pid); boolean isForReferencesOnly(PropertyIdValue pid);
/** /**
* Get the list of allowed qualifiers (as property ids) for this property (null if any) * Get the list of allowed qualifiers (as property ids) for this property (null
* if any)
*/ */
Set<PropertyIdValue> allowedQualifiers(PropertyIdValue pid); Set<PropertyIdValue> allowedQualifiers(PropertyIdValue pid);
/** /**
* Get the list of mandatory qualifiers (as property ids) for this property (null if any) * Get the list of mandatory qualifiers (as property ids) for this property
* (null if any)
*/ */
Set<PropertyIdValue> mandatoryQualifiers(PropertyIdValue pid); Set<PropertyIdValue> mandatoryQualifiers(PropertyIdValue pid);

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa; package org.openrefine.wikidata.qa;
import java.util.HashMap; import java.util.HashMap;
@ -18,8 +41,6 @@ import org.openrefine.wikidata.qa.scrutinizers.SingleValueScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.UnsourcedScrutinizer; import org.openrefine.wikidata.qa.scrutinizers.UnsourcedScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.WhitespaceScrutinizer; import org.openrefine.wikidata.qa.scrutinizers.WhitespaceScrutinizer;
import org.openrefine.wikidata.updates.ItemUpdate; import org.openrefine.wikidata.updates.ItemUpdate;
import org.openrefine.wikidata.updates.scheduler.ImpossibleSchedulingException;
import org.openrefine.wikidata.updates.scheduler.UpdateScheduler;
import org.openrefine.wikidata.updates.scheduler.WikibaseAPIUpdateScheduler; import org.openrefine.wikidata.updates.scheduler.WikibaseAPIUpdateScheduler;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
@ -30,15 +51,16 @@ import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
* *
*/ */
public class EditInspector { public class EditInspector {
private Map<String, EditScrutinizer> scrutinizers; private Map<String, EditScrutinizer> scrutinizers;
private QAWarningStore warningStore; private QAWarningStore warningStore;
private ConstraintFetcher fetcher; private ConstraintFetcher fetcher;
public EditInspector(QAWarningStore warningStore) { public EditInspector(QAWarningStore warningStore) {
this.scrutinizers = new HashMap<>(); this.scrutinizers = new HashMap<>();
this.fetcher = new WikidataConstraintFetcher(); this.fetcher = new WikidataConstraintFetcher();
this.warningStore = warningStore; this.warningStore = warningStore;
// Register all known scrutinizers here // Register all known scrutinizers here
register(new NewItemScrutinizer()); register(new NewItemScrutinizer());
register(new FormatScrutinizer()); register(new FormatScrutinizer());
@ -52,9 +74,10 @@ public class EditInspector {
register(new NoEditsMadeScrutinizer()); register(new NoEditsMadeScrutinizer());
register(new WhitespaceScrutinizer()); register(new WhitespaceScrutinizer());
} }
/** /**
* Adds a new scrutinizer to the inspector * Adds a new scrutinizer to the inspector
*
* @param scrutinizer * @param scrutinizer
*/ */
public void register(EditScrutinizer scrutinizer) { public void register(EditScrutinizer scrutinizer) {
@ -63,11 +86,11 @@ public class EditInspector {
scrutinizer.setStore(warningStore); scrutinizer.setStore(warningStore);
scrutinizer.setFetcher(fetcher); scrutinizer.setFetcher(fetcher);
} }
/** /**
* Inspect a batch of edits with the registered scrutinizers * Inspect a batch of edits with the registered scrutinizers
* @param editBatch *
* @param editBatch
*/ */
public void inspect(List<ItemUpdate> editBatch) { public void inspect(List<ItemUpdate> editBatch) {
// First, schedule them with some scheduler, // First, schedule them with some scheduler,
@ -75,16 +98,14 @@ public class EditInspector {
WikibaseAPIUpdateScheduler scheduler = new WikibaseAPIUpdateScheduler(); WikibaseAPIUpdateScheduler scheduler = new WikibaseAPIUpdateScheduler();
editBatch = scheduler.schedule(editBatch); editBatch = scheduler.schedule(editBatch);
Map<EntityIdValue, ItemUpdate> updates = ItemUpdate.groupBySubject(editBatch); Map<EntityIdValue, ItemUpdate> updates = ItemUpdate.groupBySubject(editBatch);
List<ItemUpdate> mergedUpdates = updates.values().stream().collect(Collectors.toList()); List<ItemUpdate> mergedUpdates = updates.values().stream().collect(Collectors.toList());
for(EditScrutinizer scrutinizer : scrutinizers.values()) { for (EditScrutinizer scrutinizer : scrutinizers.values()) {
scrutinizer.scrutinize(mergedUpdates); scrutinizer.scrutinize(mergedUpdates);
} }
if (warningStore.getNbWarnings() == 0) { if (warningStore.getNbWarnings() == 0) {
warningStore.addWarning(new QAWarning( warningStore.addWarning(new QAWarning("no-issue-detected", null, QAWarning.Severity.INFO, 0));
"no-issue-detected", null, QAWarning.Severity.INFO, 0));
} }
} }
} }

View File

@ -1,47 +1,69 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa; package org.openrefine.wikidata.qa;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Properties;
import org.jsoup.helper.Validate; import org.jsoup.helper.Validate;
import org.openrefine.wikidata.utils.JacksonJsonizable; import org.openrefine.wikidata.utils.JacksonJsonizable;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* A class to represent a QA warning emitted by the Wikidata schema * A class to represent a QA warning emitted by the Wikidata schema This could
* This could probably be reused at a broader scale, for instance for * probably be reused at a broader scale, for instance for Data Package
* Data Package validation. * validation.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public class QAWarning extends JacksonJsonizable implements Comparable<QAWarning> { public class QAWarning extends JacksonJsonizable implements Comparable<QAWarning> {
public enum Severity { public enum Severity {
INFO, // We just report something to the user but it is probably fine INFO, // We just report something to the user but it is probably fine
WARNING, // Edits that look wrong but in some cases they are actually fine WARNING, // Edits that look wrong but in some cases they are actually fine
IMPORTANT, // There is almost surely something wrong about the edit but in rare cases we might want to allow it IMPORTANT, // There is almost surely something wrong about the edit but in rare cases we
// might want to allow it
CRITICAL, // We should never edit if there is a critical issue CRITICAL, // We should never edit if there is a critical issue
} }
/// The type of QA warning emitted /// The type of QA warning emitted
private final String type; private final String type;
// The key for aggregation of other QA warnings together - this specializes the id // The key for aggregation of other QA warnings together - this specializes the
// id
private final String bucketId; private final String bucketId;
// The severity of the issue // The severity of the issue
private final Severity severity; private final Severity severity;
// The number of times this issue was found // The number of times this issue was found
private final int count; private final int count;
// Other details about the warning, that can be displayed to the user // Other details about the warning, that can be displayed to the user
private final Map<String,Object> properties; private final Map<String, Object> properties;
public QAWarning(String type, String bucketId, Severity severity, int count) { public QAWarning(String type, String bucketId, Severity severity, int count) {
Validate.notNull(type); Validate.notNull(type);
this.type = type; this.type = type;
@ -51,7 +73,7 @@ public class QAWarning extends JacksonJsonizable implements Comparable<QAWarning
this.count = count; this.count = count;
this.properties = new HashMap<>(); this.properties = new HashMap<>();
} }
/** /**
* @return the full key for aggregation of QA warnings * @return the full key for aggregation of QA warnings
*/ */
@ -63,62 +85,64 @@ public class QAWarning extends JacksonJsonizable implements Comparable<QAWarning
return this.type; return this.type;
} }
} }
/** /**
* Aggregates another QA warning of the same aggregation id. * Aggregates another QA warning of the same aggregation id.
*
* @param other * @param other
*/ */
public QAWarning aggregate(QAWarning other) { public QAWarning aggregate(QAWarning other) {
assert other.getAggregationId().equals(getAggregationId()); assert other.getAggregationId().equals(getAggregationId());
int newCount = count+other.getCount(); int newCount = count + other.getCount();
Severity newSeverity = severity; Severity newSeverity = severity;
if (other.getSeverity().compareTo(severity) > 0) { if (other.getSeverity().compareTo(severity) > 0) {
newSeverity = other.getSeverity(); newSeverity = other.getSeverity();
} }
QAWarning merged = new QAWarning(getType(), getBucketId(), newSeverity, QAWarning merged = new QAWarning(getType(), getBucketId(), newSeverity, newCount);
newCount); for (Entry<String, Object> entry : properties.entrySet()) {
for(Entry<String,Object> entry : properties.entrySet()) { merged.setProperty(entry.getKey(), entry.getValue());
merged.setProperty(entry.getKey(),entry.getValue());
} }
for(Entry<String,Object> entry : other.getProperties().entrySet()) { for (Entry<String, Object> entry : other.getProperties().entrySet()) {
merged.setProperty(entry.getKey(),entry.getValue()); merged.setProperty(entry.getKey(), entry.getValue());
} }
return merged; return merged;
} }
/** /**
* Sets a property of the QA warning, to be used by the front-end * Sets a property of the QA warning, to be used by the front-end for display.
* for display. *
* @param key: the name of the property * @param key:
* @param value should be Jackson-serializable * the name of the property
* @param value
* should be Jackson-serializable
*/ */
public void setProperty(String key, Object value) { public void setProperty(String key, Object value) {
this.properties.put(key, value); this.properties.put(key, value);
} }
@JsonProperty("type") @JsonProperty("type")
public String getType() { public String getType() {
return type; return type;
} }
@JsonProperty("bucketId") @JsonProperty("bucketId")
public String getBucketId() { public String getBucketId() {
return bucketId; return bucketId;
} }
@JsonProperty("severity") @JsonProperty("severity")
public Severity getSeverity() { public Severity getSeverity() {
return severity; return severity;
} }
@JsonProperty("count") @JsonProperty("count")
public int getCount() { public int getCount() {
return count; return count;
} }
@JsonProperty("properties") @JsonProperty("properties")
@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonInclude(JsonInclude.Include.NON_EMPTY)
public Map<String,Object> getProperties() { public Map<String, Object> getProperties() {
return properties; return properties;
} }
@ -127,19 +151,17 @@ public class QAWarning extends JacksonJsonizable implements Comparable<QAWarning
*/ */
@Override @Override
public int compareTo(QAWarning other) { public int compareTo(QAWarning other) {
return - severity.compareTo(other.getSeverity()); return -severity.compareTo(other.getSeverity());
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (other == null || !QAWarning.class.isInstance(other)) { if (other == null || !QAWarning.class.isInstance(other)) {
return false; return false;
} }
QAWarning otherWarning = (QAWarning)other; QAWarning otherWarning = (QAWarning) other;
return type.equals(otherWarning.getType()) && return type.equals(otherWarning.getType()) && bucketId.equals(otherWarning.getBucketId())
bucketId.equals(otherWarning.getBucketId()) && && severity.equals(otherWarning.getSeverity()) && count == otherWarning.getCount()
severity.equals(otherWarning.getSeverity()) && && properties.equals(otherWarning.getProperties());
count == otherWarning.getCount() &&
properties.equals(otherWarning.getProperties());
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa; package org.openrefine.wikidata.qa;
import java.util.ArrayList; import java.util.ArrayList;
@ -15,21 +38,22 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @author Antonin Delpeuch * @author Antonin Delpeuch
*/ */
public class QAWarningStore { public class QAWarningStore {
@JsonIgnore @JsonIgnore
private Map<String, QAWarning> map; private Map<String, QAWarning> map;
@JsonIgnore @JsonIgnore
private QAWarning.Severity maxSeverity; private QAWarning.Severity maxSeverity;
@JsonIgnore @JsonIgnore
private int totalWarnings; private int totalWarnings;
public QAWarningStore() { public QAWarningStore() {
this.map = new HashMap<>(); this.map = new HashMap<>();
this.maxSeverity = QAWarning.Severity.INFO; this.maxSeverity = QAWarning.Severity.INFO;
} }
/** /**
* Stores a warning, aggregating it with any existing * Stores a warning, aggregating it with any existing
*
* @param warning * @param warning
*/ */
public void addWarning(QAWarning warning) { public void addWarning(QAWarning warning) {
@ -46,7 +70,7 @@ public class QAWarningStore {
map.put(aggregationKey, warning); map.put(aggregationKey, warning);
} }
} }
/** /**
* Returns the list of aggregated warnings, ordered by decreasing severity * Returns the list of aggregated warnings, ordered by decreasing severity
*/ */
@ -56,7 +80,7 @@ public class QAWarningStore {
Collections.sort(result); Collections.sort(result);
return result; return result;
} }
/** /**
* Returns the maximum severity of the stored warnings (INFO if empty) * Returns the maximum severity of the stored warnings (INFO if empty)
*/ */
@ -64,7 +88,7 @@ public class QAWarningStore {
public QAWarning.Severity getMaxSeverity() { public QAWarning.Severity getMaxSeverity() {
return maxSeverity; return maxSeverity;
} }
/** /**
* Returns the total number of warnings * Returns the total number of warnings
*/ */

View File

@ -1,16 +1,36 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa; package org.openrefine.wikidata.qa;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.openrefine.wikidata.utils.EntityCache; import org.openrefine.wikidata.utils.EntityCache;
import org.wikidata.wdtk.datamodel.interfaces.EntityDocument;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
import org.wikidata.wdtk.datamodel.interfaces.PropertyDocument; import org.wikidata.wdtk.datamodel.interfaces.PropertyDocument;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue; import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Snak; import org.wikidata.wdtk.datamodel.interfaces.Snak;
@ -21,119 +41,123 @@ import org.wikidata.wdtk.datamodel.interfaces.StringValue;
import org.wikidata.wdtk.datamodel.interfaces.Value; import org.wikidata.wdtk.datamodel.interfaces.Value;
/** /**
* This class provides an abstraction over the way constraint * This class provides an abstraction over the way constraint definitions are
* definitions are stored in Wikidata. * stored in Wikidata.
* *
* @author antonin * @author Antonin Delpeuch
* *
*/ */
public class WikidataConstraintFetcher implements ConstraintFetcher { public class WikidataConstraintFetcher implements ConstraintFetcher {
public static String WIKIDATA_CONSTRAINT_PID = "P2302"; public static String WIKIDATA_CONSTRAINT_PID = "P2302";
public static String FORMAT_CONSTRAINT_QID = "Q21502404"; public static String FORMAT_CONSTRAINT_QID = "Q21502404";
public static String FORMAT_REGEX_PID = "P1793"; public static String FORMAT_REGEX_PID = "P1793";
public static String INVERSE_CONSTRAINT_QID = "Q21510855"; public static String INVERSE_CONSTRAINT_QID = "Q21510855";
public static String INVERSE_PROPERTY_PID = "P2306"; public static String INVERSE_PROPERTY_PID = "P2306";
public static String USED_ONLY_AS_VALUES_CONSTRAINT_QID = "Q21528958"; public static String USED_ONLY_AS_VALUES_CONSTRAINT_QID = "Q21528958";
public static String USED_ONLY_AS_QUALIFIER_CONSTRAINT_QID = "Q21510863"; public static String USED_ONLY_AS_QUALIFIER_CONSTRAINT_QID = "Q21510863";
public static String USED_ONLY_AS_REFERENCE_CONSTRAINT_QID = "Q21528959"; public static String USED_ONLY_AS_REFERENCE_CONSTRAINT_QID = "Q21528959";
public static String ALLOWED_QUALIFIERS_CONSTRAINT_QID = "Q21510851"; public static String ALLOWED_QUALIFIERS_CONSTRAINT_QID = "Q21510851";
public static String ALLOWED_QUALIFIERS_CONSTRAINT_PID = "P2306"; public static String ALLOWED_QUALIFIERS_CONSTRAINT_PID = "P2306";
public static String MANDATORY_QUALIFIERS_CONSTRAINT_QID = "Q21510856"; public static String MANDATORY_QUALIFIERS_CONSTRAINT_QID = "Q21510856";
public static String MANDATORY_QUALIFIERS_CONSTRAINT_PID = "P2306"; public static String MANDATORY_QUALIFIERS_CONSTRAINT_PID = "P2306";
public static String SINGLE_VALUE_CONSTRAINT_QID = "Q19474404"; public static String SINGLE_VALUE_CONSTRAINT_QID = "Q19474404";
public static String DISTINCT_VALUES_CONSTRAINT_QID = "Q21502410"; public static String DISTINCT_VALUES_CONSTRAINT_QID = "Q21502410";
// The following constraints still need to be implemented: // The following constraints still need to be implemented:
public static String TYPE_CONSTRAINT_QID = "Q21503250"; public static String TYPE_CONSTRAINT_QID = "Q21503250";
@Override @Override
public String getFormatRegex(PropertyIdValue pid) { public String getFormatRegex(PropertyIdValue pid) {
List<SnakGroup> specs = getSingleConstraint(pid, FORMAT_CONSTRAINT_QID); List<SnakGroup> specs = getSingleConstraint(pid, FORMAT_CONSTRAINT_QID);
if (specs != null) { if (specs != null) {
List<Value> regexes = findValues(specs, FORMAT_REGEX_PID); List<Value> regexes = findValues(specs, FORMAT_REGEX_PID);
if (! regexes.isEmpty()) { if (!regexes.isEmpty()) {
return ((StringValue)regexes.get(0)).getString(); return ((StringValue) regexes.get(0)).getString();
} }
} }
return null; return null;
} }
@Override @Override
public PropertyIdValue getInversePid(PropertyIdValue pid) { public PropertyIdValue getInversePid(PropertyIdValue pid) {
List<SnakGroup> specs = getSingleConstraint(pid, INVERSE_CONSTRAINT_QID); List<SnakGroup> specs = getSingleConstraint(pid, INVERSE_CONSTRAINT_QID);
if(specs != null) { if (specs != null) {
List<Value> inverses = findValues(specs, INVERSE_PROPERTY_PID); List<Value> inverses = findValues(specs, INVERSE_PROPERTY_PID);
if (! inverses.isEmpty()) { if (!inverses.isEmpty()) {
return (PropertyIdValue)inverses.get(0); return (PropertyIdValue) inverses.get(0);
} }
} }
return null; return null;
} }
@Override @Override
public boolean isForValuesOnly(PropertyIdValue pid) { public boolean isForValuesOnly(PropertyIdValue pid) {
return getSingleConstraint(pid, USED_ONLY_AS_VALUES_CONSTRAINT_QID) != null; return getSingleConstraint(pid, USED_ONLY_AS_VALUES_CONSTRAINT_QID) != null;
} }
@Override @Override
public boolean isForQualifiersOnly(PropertyIdValue pid) { public boolean isForQualifiersOnly(PropertyIdValue pid) {
return getSingleConstraint(pid, USED_ONLY_AS_QUALIFIER_CONSTRAINT_QID) != null; return getSingleConstraint(pid, USED_ONLY_AS_QUALIFIER_CONSTRAINT_QID) != null;
} }
@Override @Override
public boolean isForReferencesOnly(PropertyIdValue pid) { public boolean isForReferencesOnly(PropertyIdValue pid) {
return getSingleConstraint(pid, USED_ONLY_AS_REFERENCE_CONSTRAINT_QID) != null; return getSingleConstraint(pid, USED_ONLY_AS_REFERENCE_CONSTRAINT_QID) != null;
} }
@Override @Override
public Set<PropertyIdValue> allowedQualifiers(PropertyIdValue pid) { public Set<PropertyIdValue> allowedQualifiers(PropertyIdValue pid) {
List<SnakGroup> specs = getSingleConstraint(pid, ALLOWED_QUALIFIERS_CONSTRAINT_QID); List<SnakGroup> specs = getSingleConstraint(pid, ALLOWED_QUALIFIERS_CONSTRAINT_QID);
if (specs != null) { if (specs != null) {
List<Value> properties = findValues(specs, ALLOWED_QUALIFIERS_CONSTRAINT_PID); List<Value> properties = findValues(specs, ALLOWED_QUALIFIERS_CONSTRAINT_PID);
return properties.stream().map(e -> (PropertyIdValue) e).collect(Collectors.toSet()); return properties.stream().map(e -> (PropertyIdValue) e).collect(Collectors.toSet());
} }
return null; return null;
} }
@Override @Override
public Set<PropertyIdValue> mandatoryQualifiers(PropertyIdValue pid) { public Set<PropertyIdValue> mandatoryQualifiers(PropertyIdValue pid) {
List<SnakGroup> specs = getSingleConstraint(pid, MANDATORY_QUALIFIERS_CONSTRAINT_QID); List<SnakGroup> specs = getSingleConstraint(pid, MANDATORY_QUALIFIERS_CONSTRAINT_QID);
if (specs != null) { if (specs != null) {
List<Value> properties = findValues(specs, MANDATORY_QUALIFIERS_CONSTRAINT_PID); List<Value> properties = findValues(specs, MANDATORY_QUALIFIERS_CONSTRAINT_PID);
return properties.stream().map(e -> (PropertyIdValue) e).collect(Collectors.toSet()); return properties.stream().map(e -> (PropertyIdValue) e).collect(Collectors.toSet());
} }
return null; return null;
} }
@Override @Override
public boolean hasSingleValue(PropertyIdValue pid) { public boolean hasSingleValue(PropertyIdValue pid) {
return getSingleConstraint(pid, SINGLE_VALUE_CONSTRAINT_QID) != null; return getSingleConstraint(pid, SINGLE_VALUE_CONSTRAINT_QID) != null;
} }
@Override @Override
public boolean hasDistinctValues(PropertyIdValue pid) { public boolean hasDistinctValues(PropertyIdValue pid) {
return getSingleConstraint(pid, DISTINCT_VALUES_CONSTRAINT_QID) != null; return getSingleConstraint(pid, DISTINCT_VALUES_CONSTRAINT_QID) != null;
} }
/** /**
* Returns a single constraint for a particular type and a property, or null * Returns a single constraint for a particular type and a property, or null if
* if there is no such constraint * there is no such constraint
* @param pid: the property to retrieve the constraints for *
* @param qid: the type of the constraints * @param pid:
* @return the list of qualifiers for the constraint, or null if it does not exist * the property to retrieve the constraints for
* @param qid:
* the type of the constraints
* @return the list of qualifiers for the constraint, or null if it does not
* exist
*/ */
protected List<SnakGroup> getSingleConstraint(PropertyIdValue pid, String qid) { protected List<SnakGroup> getSingleConstraint(PropertyIdValue pid, String qid) {
Statement statement = getConstraintsByType(pid, qid).findFirst().orElse(null); Statement statement = getConstraintsByType(pid, qid).findFirst().orElse(null);
@ -142,23 +166,27 @@ public class WikidataConstraintFetcher implements ConstraintFetcher {
} }
return null; return null;
} }
/** /**
* Gets the list of constraints of a particular type for a property * Gets the list of constraints of a particular type for a property
* @param pid: the property to retrieve the constraints for *
* @param qid: the type of the constraints * @param pid:
* the property to retrieve the constraints for
* @param qid:
* the type of the constraints
* @return the stream of matching constraint statements * @return the stream of matching constraint statements
*/ */
protected Stream<Statement> getConstraintsByType(PropertyIdValue pid, String qid) { protected Stream<Statement> getConstraintsByType(PropertyIdValue pid, String qid) {
Stream<Statement> allConstraints = getConstraintStatements(pid) Stream<Statement> allConstraints = getConstraintStatements(pid).stream()
.stream()
.filter(s -> ((EntityIdValue) s.getValue()).getId().equals(qid)); .filter(s -> ((EntityIdValue) s.getValue()).getId().equals(qid));
return allConstraints; return allConstraints;
} }
/** /**
* Gets all the constraint statements for a given property * Gets all the constraint statements for a given property
* @param pid : the id of the property to retrieve the constraints for *
* @param pid
* : the id of the property to retrieve the constraints for
* @return the list of constraint statements * @return the list of constraint statements
*/ */
protected List<Statement> getConstraintStatements(PropertyIdValue pid) { protected List<Statement> getConstraintStatements(PropertyIdValue pid) {
@ -170,16 +198,19 @@ public class WikidataConstraintFetcher implements ConstraintFetcher {
return new ArrayList<Statement>(); return new ArrayList<Statement>();
} }
} }
/** /**
* Returns the values of a given property in qualifiers * Returns the values of a given property in qualifiers
* @param groups: the qualifiers *
* @param pid: the property to filter on * @param groups:
* the qualifiers
* @param pid:
* the property to filter on
* @return * @return
*/ */
protected List<Value> findValues(List<SnakGroup> groups, String pid) { protected List<Value> findValues(List<SnakGroup> groups, String pid) {
List<Value> results = new ArrayList<>(); List<Value> results = new ArrayList<>();
for(SnakGroup group : groups) { for (SnakGroup group : groups) {
if (group.getProperty().getId().equals(pid)) { if (group.getProperty().getId().equals(pid)) {
for (Snak snak : group.getSnaks()) for (Snak snak : group.getSnaks())
results.add(snak.getValue()); results.add(snak.getValue());

View File

@ -1,9 +1,30 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.openrefine.wikidata.qa.QAWarning; import org.openrefine.wikidata.qa.QAWarning;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
@ -12,18 +33,18 @@ import org.wikidata.wdtk.datamodel.interfaces.Statement;
import org.wikidata.wdtk.datamodel.interfaces.Value; import org.wikidata.wdtk.datamodel.interfaces.Value;
/** /**
* A scrutinizer that checks for properties using the same value * A scrutinizer that checks for properties using the same value on different
* on different items. * items.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public class DistinctValuesScrutinizer extends StatementScrutinizer { public class DistinctValuesScrutinizer extends StatementScrutinizer {
public final static String type = "identical-values-for-distinct-valued-property"; public final static String type = "identical-values-for-distinct-valued-property";
private Map<PropertyIdValue, Map<Value, EntityIdValue>> _seenValues; private Map<PropertyIdValue, Map<Value, EntityIdValue>> _seenValues;
public DistinctValuesScrutinizer() { public DistinctValuesScrutinizer() {
_seenValues = new HashMap<>(); _seenValues = new HashMap<>();
} }
@ -40,11 +61,7 @@ public class DistinctValuesScrutinizer extends StatementScrutinizer {
} }
if (seen.containsKey(mainSnakValue)) { if (seen.containsKey(mainSnakValue)) {
EntityIdValue otherId = seen.get(mainSnakValue); EntityIdValue otherId = seen.get(mainSnakValue);
QAWarning issue = new QAWarning( QAWarning issue = new QAWarning(type, pid.getId(), QAWarning.Severity.IMPORTANT, 1);
type,
pid.getId(),
QAWarning.Severity.IMPORTANT,
1);
issue.setProperty("property_entity", pid); issue.setProperty("property_entity", pid);
issue.setProperty("item1_entity", entityId); issue.setProperty("item1_entity", entityId);
issue.setProperty("item2_entity", otherId); issue.setProperty("item2_entity", otherId);

View File

@ -1,8 +1,30 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.List; import java.util.List;
import org.openrefine.wikidata.qa.WikidataConstraintFetcher;
import org.openrefine.wikidata.qa.ConstraintFetcher; import org.openrefine.wikidata.qa.ConstraintFetcher;
import org.openrefine.wikidata.qa.QAWarning; import org.openrefine.wikidata.qa.QAWarning;
import org.openrefine.wikidata.qa.QAWarning.Severity; import org.openrefine.wikidata.qa.QAWarning.Severity;
@ -15,64 +37,70 @@ import org.openrefine.wikidata.updates.ItemUpdate;
* @author Antonin Delpeuch * @author Antonin Delpeuch
*/ */
public abstract class EditScrutinizer { public abstract class EditScrutinizer {
protected QAWarningStore _store; protected QAWarningStore _store;
protected ConstraintFetcher _fetcher; protected ConstraintFetcher _fetcher;
public EditScrutinizer() { public EditScrutinizer() {
_fetcher = null; _fetcher = null;
_store = null; _store = null;
} }
public void setStore(QAWarningStore store) { public void setStore(QAWarningStore store) {
_store = store; _store = store;
} }
public void setFetcher(ConstraintFetcher fetcher) { public void setFetcher(ConstraintFetcher fetcher) {
_fetcher = fetcher; _fetcher = fetcher;
} }
/** /**
* Reads the candidate edits and emits warnings in the store * Reads the candidate edits and emits warnings in the store
* @param edit: the list of ItemUpdates to scrutinize *
* @param edit:
* the list of ItemUpdates to scrutinize
*/ */
public abstract void scrutinize(List<ItemUpdate> edit); public abstract void scrutinize(List<ItemUpdate> edit);
protected void addIssue(QAWarning warning) { protected void addIssue(QAWarning warning) {
_store.addWarning(warning); _store.addWarning(warning);
} }
protected void addIssue(String type, String aggregationId, Severity severity, int count) { protected void addIssue(String type, String aggregationId, Severity severity, int count) {
addIssue(new QAWarning(type, aggregationId, severity, count)); addIssue(new QAWarning(type, aggregationId, severity, count));
} }
/** /**
* Helper to be used by subclasses to emit simple INFO warnings * Helper to be used by subclasses to emit simple INFO warnings
*
* @param warning * @param warning
*/ */
protected void info(String type) { protected void info(String type) {
addIssue(type, null, QAWarning.Severity.INFO, 1); addIssue(type, null, QAWarning.Severity.INFO, 1);
} }
/** /**
* Helper to be used by subclasses to emit simple warnings * Helper to be used by subclasses to emit simple warnings
*
* @param warning * @param warning
*/ */
protected void warning(String type) { protected void warning(String type) {
addIssue(type, null, QAWarning.Severity.WARNING, 1); addIssue(type, null, QAWarning.Severity.WARNING, 1);
} }
/** /**
* Helper to be used by subclasses to emit simple important warnings * Helper to be used by subclasses to emit simple important warnings
*
* @param warning * @param warning
*/ */
protected void important(String type) { protected void important(String type) {
addIssue(type, null, QAWarning.Severity.IMPORTANT, 1); addIssue(type, null, QAWarning.Severity.IMPORTANT, 1);
} }
/** /**
* Helper to be used by subclasses to emit simple critical warnings * Helper to be used by subclasses to emit simple critical warnings
*
* @param warning * @param warning
*/ */
protected void critical(String type) { protected void critical(String type) {

View File

@ -1,8 +1,30 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.openrefine.wikidata.qa.QAWarning; import org.openrefine.wikidata.qa.QAWarning;
@ -12,31 +34,32 @@ import org.wikidata.wdtk.datamodel.interfaces.Snak;
import org.wikidata.wdtk.datamodel.interfaces.StringValue; import org.wikidata.wdtk.datamodel.interfaces.StringValue;
/** /**
* A scrutinizer that detects incorrect formats in text values * A scrutinizer that detects incorrect formats in text values (mostly
* (mostly identifiers). * identifiers).
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public class FormatScrutinizer extends SnakScrutinizer { public class FormatScrutinizer extends SnakScrutinizer {
public static final String type = "add-statements-with-invalid-format"; public static final String type = "add-statements-with-invalid-format";
private Map<PropertyIdValue, Pattern> _patterns; private Map<PropertyIdValue, Pattern> _patterns;
public FormatScrutinizer() { public FormatScrutinizer() {
_patterns = new HashMap<>(); _patterns = new HashMap<>();
} }
/** /**
* Loads the regex for a property and compiles it to a pattern * Loads the regex for a property and compiles it to a pattern (this is cached
* (this is cached upstream, plus we are doing it only once per * upstream, plus we are doing it only once per property and batch).
* property and batch). *
* @param pid the id of the property to fetch the constraints for * @param pid
* the id of the property to fetch the constraints for
* @return * @return
*/ */
protected Pattern getPattern(PropertyIdValue pid) { protected Pattern getPattern(PropertyIdValue pid) {
if(_patterns.containsKey(pid)) { if (_patterns.containsKey(pid)) {
return _patterns.get(pid); return _patterns.get(pid);
} else { } else {
String regex = _fetcher.getFormatRegex(pid); String regex = _fetcher.getFormatRegex(pid);
@ -51,7 +74,7 @@ public class FormatScrutinizer extends SnakScrutinizer {
@Override @Override
public void scrutinize(Snak snak, EntityIdValue entityId, boolean added) { public void scrutinize(Snak snak, EntityIdValue entityId, boolean added) {
if(StringValue.class.isInstance(snak.getValue())) { if (StringValue.class.isInstance(snak.getValue())) {
String value = ((StringValue) snak.getValue()).getString(); String value = ((StringValue) snak.getValue()).getString();
PropertyIdValue pid = snak.getPropertyId(); PropertyIdValue pid = snak.getPropertyId();
Pattern pattern = getPattern(pid); Pattern pattern = getPattern(pid);
@ -60,11 +83,7 @@ public class FormatScrutinizer extends SnakScrutinizer {
} }
if (!pattern.matcher(value).matches()) { if (!pattern.matcher(value).matches()) {
if (added) { if (added) {
QAWarning issue = new QAWarning( QAWarning issue = new QAWarning(type, pid.getId(), QAWarning.Severity.IMPORTANT, 1);
type,
pid.getId(),
QAWarning.Severity.IMPORTANT,
1);
issue.setProperty("property_entity", pid); issue.setProperty("property_entity", pid);
issue.setProperty("regex", pattern.toString()); issue.setProperty("regex", pattern.toString());
issue.setProperty("example_value", value); issue.setProperty("example_value", value);

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.HashMap; import java.util.HashMap;
@ -14,48 +37,47 @@ import org.wikidata.wdtk.datamodel.interfaces.Statement;
import org.wikidata.wdtk.datamodel.interfaces.Value; import org.wikidata.wdtk.datamodel.interfaces.Value;
/** /**
* A scrutinizer that checks for missing inverse statements in * A scrutinizer that checks for missing inverse statements in edit batches.
* edit batches.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public class InverseConstraintScrutinizer extends StatementScrutinizer { public class InverseConstraintScrutinizer extends StatementScrutinizer {
public static final String type = "missing-inverse-statements"; public static final String type = "missing-inverse-statements";
private Map<PropertyIdValue, PropertyIdValue> _inverse; private Map<PropertyIdValue, PropertyIdValue> _inverse;
private Map<PropertyIdValue, Map<EntityIdValue, Set<EntityIdValue> >> _statements; private Map<PropertyIdValue, Map<EntityIdValue, Set<EntityIdValue>>> _statements;
public InverseConstraintScrutinizer() { public InverseConstraintScrutinizer() {
_inverse = new HashMap<>(); _inverse = new HashMap<>();
_statements = new HashMap<>(); _statements = new HashMap<>();
} }
protected PropertyIdValue getInverseConstraint(PropertyIdValue pid) { protected PropertyIdValue getInverseConstraint(PropertyIdValue pid) {
if (_inverse.containsKey(pid)) { if (_inverse.containsKey(pid)) {
return _inverse.get(pid); return _inverse.get(pid);
} else { } else {
PropertyIdValue inversePid = _fetcher.getInversePid(pid); PropertyIdValue inversePid = _fetcher.getInversePid(pid);
_inverse.put(pid, inversePid); _inverse.put(pid, inversePid);
_statements.put(pid, new HashMap<EntityIdValue,Set<EntityIdValue>>()); _statements.put(pid, new HashMap<EntityIdValue, Set<EntityIdValue>>());
// We are doing this check because we do not have any guarantee that // We are doing this check because we do not have any guarantee that
// the inverse constraints are consistent on Wikidata. // the inverse constraints are consistent on Wikidata.
if (inversePid != null && !_inverse.containsKey(inversePid)) { if (inversePid != null && !_inverse.containsKey(inversePid)) {
_inverse.put(inversePid, pid); _inverse.put(inversePid, pid);
_statements.put(inversePid, new HashMap<EntityIdValue,Set<EntityIdValue>>()); _statements.put(inversePid, new HashMap<EntityIdValue, Set<EntityIdValue>>());
} }
return inversePid; return inversePid;
} }
} }
@Override @Override
public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) { public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) {
if (!added) { if (!added) {
return; // TODO support for deleted statements return; // TODO support for deleted statements
} }
Value mainSnakValue = statement.getClaim().getMainSnak().getValue(); Value mainSnakValue = statement.getClaim().getMainSnak().getValue();
if (ItemIdValue.class.isInstance(mainSnakValue)) { if (ItemIdValue.class.isInstance(mainSnakValue)) {
PropertyIdValue pid = statement.getClaim().getMainSnak().getPropertyId(); PropertyIdValue pid = statement.getClaim().getMainSnak().getPropertyId();
@ -71,24 +93,21 @@ public class InverseConstraintScrutinizer extends StatementScrutinizer {
} }
} }
} }
@Override @Override
public void batchIsFinished() { public void batchIsFinished() {
// For each pair of inverse properties (in each direction) // For each pair of inverse properties (in each direction)
for(Entry<PropertyIdValue,PropertyIdValue> propertyPair : _inverse.entrySet()) { for (Entry<PropertyIdValue, PropertyIdValue> propertyPair : _inverse.entrySet()) {
// Get the statements made for the first // Get the statements made for the first
PropertyIdValue ourProperty = propertyPair.getKey(); PropertyIdValue ourProperty = propertyPair.getKey();
for(Entry<EntityIdValue, Set<EntityIdValue>> itemLinks : _statements.get(ourProperty).entrySet()) { for (Entry<EntityIdValue, Set<EntityIdValue>> itemLinks : _statements.get(ourProperty).entrySet()) {
// For each outgoing link // For each outgoing link
for(EntityIdValue idValue : itemLinks.getValue()) { for (EntityIdValue idValue : itemLinks.getValue()) {
// Check that they are in the statements made for the second // Check that they are in the statements made for the second
PropertyIdValue missingProperty = propertyPair.getValue(); PropertyIdValue missingProperty = propertyPair.getValue();
Set<EntityIdValue> reciprocalLinks = _statements.get(missingProperty).get(idValue); Set<EntityIdValue> reciprocalLinks = _statements.get(missingProperty).get(idValue);
if (reciprocalLinks == null || !reciprocalLinks.contains(itemLinks.getKey())) { if (reciprocalLinks == null || !reciprocalLinks.contains(itemLinks.getKey())) {
QAWarning issue = new QAWarning(type, QAWarning issue = new QAWarning(type, ourProperty.getId(), QAWarning.Severity.IMPORTANT, 1);
ourProperty.getId(),
QAWarning.Severity.IMPORTANT,
1);
issue.setProperty("added_property_entity", ourProperty); issue.setProperty("added_property_entity", ourProperty);
issue.setProperty("inverse_property_entity", missingProperty); issue.setProperty("inverse_property_entity", missingProperty);
issue.setProperty("source_entity", itemLinks.getKey()); issue.setProperty("source_entity", itemLinks.getKey());

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.List; import java.util.List;
@ -8,8 +31,8 @@ public abstract class ItemUpdateScrutinizer extends EditScrutinizer {
@Override @Override
public void scrutinize(List<ItemUpdate> edit) { public void scrutinize(List<ItemUpdate> edit) {
for(ItemUpdate update : edit) { for (ItemUpdate update : edit) {
if(!update.isNull()) { if (!update.isNull()) {
scrutinize(update); scrutinize(update);
} }
} }
@ -17,15 +40,16 @@ public abstract class ItemUpdateScrutinizer extends EditScrutinizer {
} }
/** /**
* Method to be overridden by subclasses to scrutinize * Method to be overridden by subclasses to scrutinize an individual item
* an individual item update. * update.
*
* @param update * @param update
*/ */
public abstract void scrutinize(ItemUpdate update); public abstract void scrutinize(ItemUpdate update);
/** /**
* Method to be overridden by subclasses to emit warnings * Method to be overridden by subclasses to emit warnings once a batch has been
* once a batch has been completely analyzed. * completely analyzed.
*/ */
public void batchIsFinished() { public void batchIsFinished() {
; ;

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import org.openrefine.wikidata.qa.QAWarning; import org.openrefine.wikidata.qa.QAWarning;
@ -10,7 +33,7 @@ import org.wikidata.wdtk.datamodel.interfaces.StatementGroup;
* @author Antonin Delpeuch * @author Antonin Delpeuch
*/ */
public class NewItemScrutinizer extends ItemUpdateScrutinizer { public class NewItemScrutinizer extends ItemUpdateScrutinizer {
public static final String noLabelType = "new-item-without-labels-or-aliases"; public static final String noLabelType = "new-item-without-labels-or-aliases";
public static final String noDescType = "new-item-without-descriptions"; public static final String noDescType = "new-item-without-descriptions";
public static final String deletedStatementsType = "new-item-with-deleted-statements"; public static final String deletedStatementsType = "new-item-with-deleted-statements";
@ -21,40 +44,28 @@ public class NewItemScrutinizer extends ItemUpdateScrutinizer {
public void scrutinize(ItemUpdate update) { public void scrutinize(ItemUpdate update) {
if (update.isNew()) { if (update.isNew()) {
info(newItemType); info(newItemType);
if (update.getLabels().isEmpty() && update.getAliases().isEmpty()) { if (update.getLabels().isEmpty() && update.getAliases().isEmpty()) {
QAWarning issue = new QAWarning( QAWarning issue = new QAWarning(noLabelType, null, QAWarning.Severity.CRITICAL, 1);
noLabelType,
null,
QAWarning.Severity.CRITICAL,
1);
issue.setProperty("example_entity", update.getItemId()); issue.setProperty("example_entity", update.getItemId());
addIssue(issue); addIssue(issue);
} }
if (update.getDescriptions().isEmpty()) { if (update.getDescriptions().isEmpty()) {
QAWarning issue = new QAWarning( QAWarning issue = new QAWarning(noDescType, null, QAWarning.Severity.WARNING, 1);
noDescType,
null,
QAWarning.Severity.WARNING,
1);
issue.setProperty("example_entity", update.getItemId()); issue.setProperty("example_entity", update.getItemId());
addIssue(issue); addIssue(issue);
} }
if (!update.getDeletedStatements().isEmpty()) { if (!update.getDeletedStatements().isEmpty()) {
QAWarning issue = new QAWarning( QAWarning issue = new QAWarning(deletedStatementsType, null, QAWarning.Severity.WARNING, 1);
deletedStatementsType,
null,
QAWarning.Severity.WARNING,
1);
issue.setProperty("example_entity", update.getItemId()); issue.setProperty("example_entity", update.getItemId());
addIssue(issue); addIssue(issue);
} }
// Try to find a "instance of" or "subclass of" claim // Try to find a "instance of" or "subclass of" claim
boolean typeFound = false; boolean typeFound = false;
for(StatementGroup group : update.getAddedStatementGroups()) { for (StatementGroup group : update.getAddedStatementGroups()) {
String pid = group.getProperty().getId(); String pid = group.getProperty().getId();
if ("P31".equals(pid) || "P279".equals(pid)) { if ("P31".equals(pid) || "P279".equals(pid)) {
typeFound = true; typeFound = true;
@ -62,13 +73,9 @@ public class NewItemScrutinizer extends ItemUpdateScrutinizer {
} }
} }
if (!typeFound) { if (!typeFound) {
QAWarning issue = new QAWarning( QAWarning issue = new QAWarning(noTypeType, null, QAWarning.Severity.WARNING, 1);
noTypeType, issue.setProperty("example_entity", update.getItemId());
null, addIssue(issue);
QAWarning.Severity.WARNING,
1);
issue.setProperty("example_entity", update.getItemId());
addIssue(issue);
} }
} }
} }

View File

@ -1,17 +1,39 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.List; import java.util.List;
import org.openrefine.wikidata.updates.ItemUpdate; import org.openrefine.wikidata.updates.ItemUpdate;
public class NoEditsMadeScrutinizer extends EditScrutinizer { public class NoEditsMadeScrutinizer extends EditScrutinizer {
public static final String type = "no-edit-generated"; public static final String type = "no-edit-generated";
@Override @Override
public void scrutinize(List<ItemUpdate> edit) { public void scrutinize(List<ItemUpdate> edit) {
if(edit.stream().allMatch(e -> e.isNull())) { if (edit.stream().allMatch(e -> e.isNull())) {
info(type); info(type);
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.HashMap; import java.util.HashMap;
@ -12,24 +35,24 @@ import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Statement; import org.wikidata.wdtk.datamodel.interfaces.Statement;
/** /**
* A scrutinizer that checks the compatibility of the qualifiers * A scrutinizer that checks the compatibility of the qualifiers and the
* and the property of a statement, and looks for mandatory qualifiers. * property of a statement, and looks for mandatory qualifiers.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
*/ */
public class QualifierCompatibilityScrutinizer extends StatementScrutinizer { public class QualifierCompatibilityScrutinizer extends StatementScrutinizer {
public static final String missingMandatoryQualifiersType = "missing-mandatory-qualifiers"; public static final String missingMandatoryQualifiersType = "missing-mandatory-qualifiers";
public static final String disallowedQualifiersType = "disallowed-qualifiers"; public static final String disallowedQualifiersType = "disallowed-qualifiers";
private Map<PropertyIdValue, Set<PropertyIdValue>> _allowedQualifiers; private Map<PropertyIdValue, Set<PropertyIdValue>> _allowedQualifiers;
private Map<PropertyIdValue, Set<PropertyIdValue>> _mandatoryQualifiers; private Map<PropertyIdValue, Set<PropertyIdValue>> _mandatoryQualifiers;
public QualifierCompatibilityScrutinizer() { public QualifierCompatibilityScrutinizer() {
_allowedQualifiers = new HashMap<>(); _allowedQualifiers = new HashMap<>();
_mandatoryQualifiers = new HashMap<>(); _mandatoryQualifiers = new HashMap<>();
} }
protected boolean qualifierIsAllowed(PropertyIdValue statementProperty, PropertyIdValue qualifierProperty) { protected boolean qualifierIsAllowed(PropertyIdValue statementProperty, PropertyIdValue qualifierProperty) {
Set<PropertyIdValue> allowed = null; Set<PropertyIdValue> allowed = null;
if (_allowedQualifiers.containsKey(statementProperty)) { if (_allowedQualifiers.containsKey(statementProperty)) {
@ -40,7 +63,7 @@ public class QualifierCompatibilityScrutinizer extends StatementScrutinizer {
} }
return allowed == null || allowed.contains(qualifierProperty); return allowed == null || allowed.contains(qualifierProperty);
} }
protected Set<PropertyIdValue> mandatoryQualifiers(PropertyIdValue statementProperty) { protected Set<PropertyIdValue> mandatoryQualifiers(PropertyIdValue statementProperty) {
Set<PropertyIdValue> mandatory = null; Set<PropertyIdValue> mandatory = null;
if (_mandatoryQualifiers.containsKey(statementProperty)) { if (_mandatoryQualifiers.containsKey(statementProperty)) {
@ -58,31 +81,25 @@ public class QualifierCompatibilityScrutinizer extends StatementScrutinizer {
@Override @Override
public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) { public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) {
PropertyIdValue statementProperty = statement.getClaim().getMainSnak().getPropertyId(); PropertyIdValue statementProperty = statement.getClaim().getMainSnak().getPropertyId();
Set<PropertyIdValue> qualifiers = statement.getClaim().getQualifiers(). Set<PropertyIdValue> qualifiers = statement.getClaim().getQualifiers().stream().map(e -> e.getProperty())
stream().map(e -> e.getProperty()).collect(Collectors.toSet()); .collect(Collectors.toSet());
Set<PropertyIdValue> missingQualifiers = mandatoryQualifiers(statementProperty) Set<PropertyIdValue> missingQualifiers = mandatoryQualifiers(statementProperty).stream()
.stream().filter(p -> !qualifiers.contains(p)).collect(Collectors.toSet()); .filter(p -> !qualifiers.contains(p)).collect(Collectors.toSet());
Set<PropertyIdValue> disallowedQualifiers = qualifiers Set<PropertyIdValue> disallowedQualifiers = qualifiers.stream()
.stream().filter(p -> !qualifierIsAllowed(statementProperty, p)).collect(Collectors.toSet()); .filter(p -> !qualifierIsAllowed(statementProperty, p)).collect(Collectors.toSet());
for (PropertyIdValue missing : missingQualifiers) { for (PropertyIdValue missing : missingQualifiers) {
QAWarning issue = new QAWarning( QAWarning issue = new QAWarning(missingMandatoryQualifiersType,
missingMandatoryQualifiersType, statementProperty.getId() + "-" + missing.getId(), QAWarning.Severity.WARNING, 1);
statementProperty.getId()+"-"+missing.getId(),
QAWarning.Severity.WARNING,
1);
issue.setProperty("statement_property_entity", statementProperty); issue.setProperty("statement_property_entity", statementProperty);
issue.setProperty("missing_property_entity", missing); issue.setProperty("missing_property_entity", missing);
issue.setProperty("example_item_entity", entityId); issue.setProperty("example_item_entity", entityId);
addIssue(issue); addIssue(issue);
} }
for (PropertyIdValue disallowed : disallowedQualifiers) { for (PropertyIdValue disallowed : disallowedQualifiers) {
QAWarning issue = new QAWarning( QAWarning issue = new QAWarning(disallowedQualifiersType,
disallowedQualifiersType, statementProperty.getId() + "-" + disallowed.getId(), QAWarning.Severity.WARNING, 1);
statementProperty.getId()+"-"+disallowed.getId(),
QAWarning.Severity.WARNING,
1);
issue.setProperty("statement_property_entity", statementProperty); issue.setProperty("statement_property_entity", statementProperty);
issue.setProperty("disallowed_property_entity", disallowed); issue.setProperty("disallowed_property_entity", disallowed);
issue.setProperty("example_item_entity", entityId); issue.setProperty("example_item_entity", entityId);

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.HashMap; import java.util.HashMap;
@ -13,25 +36,22 @@ import org.wikidata.wdtk.datamodel.interfaces.Reference;
import org.wikidata.wdtk.datamodel.interfaces.Snak; import org.wikidata.wdtk.datamodel.interfaces.Snak;
import org.wikidata.wdtk.datamodel.interfaces.Statement; import org.wikidata.wdtk.datamodel.interfaces.Statement;
public class RestrictedPositionScrutinizer extends StatementScrutinizer { public class RestrictedPositionScrutinizer extends StatementScrutinizer {
protected enum SnakPosition { protected enum SnakPosition {
MAINSNAK, MAINSNAK, QUALIFIER, REFERENCE
QUALIFIER,
REFERENCE
} }
private Map<PropertyIdValue, SnakPosition> _restrictedPids; private Map<PropertyIdValue, SnakPosition> _restrictedPids;
private Set<PropertyIdValue> _unrestrictedPids; private Set<PropertyIdValue> _unrestrictedPids;
public RestrictedPositionScrutinizer() { public RestrictedPositionScrutinizer() {
_restrictedPids = new HashMap<>(); _restrictedPids = new HashMap<>();
_unrestrictedPids = new HashSet<>(); _unrestrictedPids = new HashSet<>();
} }
SnakPosition positionRestriction(PropertyIdValue pid) { SnakPosition positionRestriction(PropertyIdValue pid) {
if(_unrestrictedPids.contains(pid)) { if (_unrestrictedPids.contains(pid)) {
return null; return null;
} }
SnakPosition restriction = _restrictedPids.get(pid); SnakPosition restriction = _restrictedPids.get(pid);
@ -45,7 +65,7 @@ public class RestrictedPositionScrutinizer extends StatementScrutinizer {
} else if (_fetcher.isForReferencesOnly(pid)) { } else if (_fetcher.isForReferencesOnly(pid)) {
restriction = SnakPosition.REFERENCE; restriction = SnakPosition.REFERENCE;
} }
// Cache these results: // Cache these results:
if (restriction != null) { if (restriction != null) {
_restrictedPids.put(pid, restriction); _restrictedPids.put(pid, restriction);
@ -55,39 +75,37 @@ public class RestrictedPositionScrutinizer extends StatementScrutinizer {
return restriction; return restriction;
} }
} }
@Override @Override
public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) { public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) {
// Skip the main snak // Skip the main snak
scrutinize(statement.getClaim().getMainSnak(), entityId, SnakPosition.MAINSNAK, added); scrutinize(statement.getClaim().getMainSnak(), entityId, SnakPosition.MAINSNAK, added);
// Qualifiers // Qualifiers
scrutinizeSnakSet(statement.getClaim().getAllQualifiers(), entityId, SnakPosition.QUALIFIER, added); scrutinizeSnakSet(statement.getClaim().getAllQualifiers(), entityId, SnakPosition.QUALIFIER, added);
// References // References
for(Reference ref : statement.getReferences()) { for (Reference ref : statement.getReferences()) {
scrutinizeSnakSet(ref.getAllSnaks(), entityId, SnakPosition.REFERENCE, added); scrutinizeSnakSet(ref.getAllSnaks(), entityId, SnakPosition.REFERENCE, added);
} }
} }
protected void scrutinizeSnakSet(Iterator<Snak> snaks, EntityIdValue entityId, SnakPosition position, boolean added) { protected void scrutinizeSnakSet(Iterator<Snak> snaks, EntityIdValue entityId, SnakPosition position,
while(snaks.hasNext()) { boolean added) {
while (snaks.hasNext()) {
Snak snak = snaks.next(); Snak snak = snaks.next();
scrutinize(snak, entityId, position, added); scrutinize(snak, entityId, position, added);
} }
} }
public void scrutinize(Snak snak, EntityIdValue entityId, SnakPosition position, boolean added) { public void scrutinize(Snak snak, EntityIdValue entityId, SnakPosition position, boolean added) {
SnakPosition restriction = positionRestriction(snak.getPropertyId()); SnakPosition restriction = positionRestriction(snak.getPropertyId());
if (restriction != null && position != restriction) { if (restriction != null && position != restriction) {
String positionStr = position.toString().toLowerCase(); String positionStr = position.toString().toLowerCase();
String restrictionStr = restriction.toString().toLowerCase(); String restrictionStr = restriction.toString().toLowerCase();
QAWarning issue = new QAWarning( QAWarning issue = new QAWarning("property-restricted-to-" + restrictionStr + "-found-in-" + positionStr,
"property-restricted-to-"+restrictionStr+"-found-in-"+positionStr, snak.getPropertyId().getId(), QAWarning.Severity.IMPORTANT, 1);
snak.getPropertyId().getId(),
QAWarning.Severity.IMPORTANT,
1);
issue.setProperty("property_entity", snak.getPropertyId()); issue.setProperty("property_entity", snak.getPropertyId());
addIssue(issue); addIssue(issue);
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import org.openrefine.wikidata.qa.QAWarning; import org.openrefine.wikidata.qa.QAWarning;
@ -5,22 +28,20 @@ import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Snak; import org.wikidata.wdtk.datamodel.interfaces.Snak;
/** /**
* A scrutinizer that checks for self-referential statements. * A scrutinizer that checks for self-referential statements. These statements
* These statements are flagged by Wikibase as suspicious. * are flagged by Wikibase as suspicious.
* *
* @author antonin * @author Antonin Delpeuch
* *
*/ */
public class SelfReferentialScrutinizer extends SnakScrutinizer { public class SelfReferentialScrutinizer extends SnakScrutinizer {
public static final String type = "self-referential-statements"; public static final String type = "self-referential-statements";
@Override @Override
public void scrutinize(Snak snak, EntityIdValue entityId, boolean added) { public void scrutinize(Snak snak, EntityIdValue entityId, boolean added) {
if (entityId.equals(snak.getValue())) { if (entityId.equals(snak.getValue())) {
QAWarning issue = new QAWarning( QAWarning issue = new QAWarning(type, null, QAWarning.Severity.WARNING, 1);
type, null,
QAWarning.Severity.WARNING, 1);
issue.setProperty("example_entity", entityId); issue.setProperty("example_entity", entityId);
addIssue(issue); addIssue(issue);
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.HashSet; import java.util.HashSet;
@ -9,27 +32,25 @@ import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Statement; import org.wikidata.wdtk.datamodel.interfaces.Statement;
/** /**
* For now this scrutinizer only checks for uniqueness at * For now this scrutinizer only checks for uniqueness at the item level (it
* the item level (it ignores qualifiers and references). * ignores qualifiers and references).
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public class SingleValueScrutinizer extends ItemUpdateScrutinizer { public class SingleValueScrutinizer extends ItemUpdateScrutinizer {
public static final String type = "single-valued-property-added-more-than-once"; public static final String type = "single-valued-property-added-more-than-once";
@Override @Override
public void scrutinize(ItemUpdate update) { public void scrutinize(ItemUpdate update) {
Set<PropertyIdValue> seenSingleProperties = new HashSet<>(); Set<PropertyIdValue> seenSingleProperties = new HashSet<>();
for(Statement statement : update.getAddedStatements()) { for (Statement statement : update.getAddedStatements()) {
PropertyIdValue pid = statement.getClaim().getMainSnak().getPropertyId(); PropertyIdValue pid = statement.getClaim().getMainSnak().getPropertyId();
if (seenSingleProperties.contains(pid)) { if (seenSingleProperties.contains(pid)) {
QAWarning issue = new QAWarning( QAWarning issue = new QAWarning(type, pid.getId(), QAWarning.Severity.WARNING, 1);
type, pid.getId(),
QAWarning.Severity.WARNING, 1);
issue.setProperty("property_entity", pid); issue.setProperty("property_entity", pid);
issue.setProperty("example_entity", update.getItemId()); issue.setProperty("example_entity", update.getItemId());
addIssue(issue); addIssue(issue);

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.Iterator; import java.util.Iterator;
@ -8,38 +31,42 @@ import org.wikidata.wdtk.datamodel.interfaces.Snak;
import org.wikidata.wdtk.datamodel.interfaces.Statement; import org.wikidata.wdtk.datamodel.interfaces.Statement;
/** /**
* A scrutinizer that inspects snaks individually, no matter whether they * A scrutinizer that inspects snaks individually, no matter whether they appear
* appear as main snaks, qualifiers or references. * as main snaks, qualifiers or references.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public abstract class SnakScrutinizer extends StatementScrutinizer { public abstract class SnakScrutinizer extends StatementScrutinizer {
/** /**
* This is the method that subclasses should override to implement their checks. * This is the method that subclasses should override to implement their checks.
* @param snak: the snak to inspect *
* @param entityId: the item on which it is going to (dis)appear * @param snak:
* @param added: whether this snak is going to be added or deleted * the snak to inspect
* @param entityId:
* the item on which it is going to (dis)appear
* @param added:
* whether this snak is going to be added or deleted
*/ */
public abstract void scrutinize(Snak snak, EntityIdValue entityId, boolean added); public abstract void scrutinize(Snak snak, EntityIdValue entityId, boolean added);
@Override @Override
public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) { public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) {
// Main snak // Main snak
scrutinize(statement.getClaim().getMainSnak(), entityId, added); scrutinize(statement.getClaim().getMainSnak(), entityId, added);
// Qualifiers // Qualifiers
scrutinizeSnakSet(statement.getClaim().getAllQualifiers(), entityId, added); scrutinizeSnakSet(statement.getClaim().getAllQualifiers(), entityId, added);
// References // References
for(Reference ref : statement.getReferences()) { for (Reference ref : statement.getReferences()) {
scrutinizeSnakSet(ref.getAllSnaks(), entityId, added); scrutinizeSnakSet(ref.getAllSnaks(), entityId, added);
} }
} }
protected void scrutinizeSnakSet(Iterator<Snak> snaks, EntityIdValue entityId, boolean added) { protected void scrutinizeSnakSet(Iterator<Snak> snaks, EntityIdValue entityId, boolean added) {
while(snaks.hasNext()) { while (snaks.hasNext()) {
Snak snak = snaks.next(); Snak snak = snaks.next();
scrutinize(snak, entityId, added); scrutinize(snak, entityId, added);
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import org.openrefine.wikidata.updates.ItemUpdate; import org.openrefine.wikidata.updates.ItemUpdate;
@ -9,7 +32,7 @@ public abstract class StatementScrutinizer extends ItemUpdateScrutinizer {
@Override @Override
public void scrutinize(ItemUpdate update) { public void scrutinize(ItemUpdate update) {
EntityIdValue currentEntityId = update.getItemId(); EntityIdValue currentEntityId = update.getItemId();
for(Statement statement : update.getAddedStatements()) { for (Statement statement : update.getAddedStatements()) {
scrutinize(statement, currentEntityId, true); scrutinize(statement, currentEntityId, true);
} }
for (Statement statement : update.getDeletedStatements()) { for (Statement statement : update.getDeletedStatements()) {
@ -18,11 +41,15 @@ public abstract class StatementScrutinizer extends ItemUpdateScrutinizer {
} }
/** /**
* The method that should be overridden by subclasses, implementing * The method that should be overridden by subclasses, implementing the checks
* the checks on one statement * on one statement
* @param statement: the statement to scrutinize *
* @param entityId: the id of the entity on which this statement is made or removed * @param statement:
* @param added: whether this statement was added or deleted * the statement to scrutinize
* @param entityId:
* the id of the entity on which this statement is made or removed
* @param added:
* whether this statement was added or deleted
*/ */
public abstract void scrutinize(Statement statement, EntityIdValue entityId, boolean added); public abstract void scrutinize(Statement statement, EntityIdValue entityId, boolean added);
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
@ -6,16 +29,16 @@ import org.wikidata.wdtk.datamodel.interfaces.Statement;
/** /**
* A scrutinizer checking for unsourced statements * A scrutinizer checking for unsourced statements
* *
* @author antonin * @author Antonin Delpeuch
* *
*/ */
public class UnsourcedScrutinizer extends StatementScrutinizer { public class UnsourcedScrutinizer extends StatementScrutinizer {
public static final String type = "unsourced-statements"; public static final String type = "unsourced-statements";
@Override @Override
public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) { public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) {
if(statement.getReferences().isEmpty() && added) { if (statement.getReferences().isEmpty() && added) {
warning(type); warning(type);
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import org.openrefine.wikidata.updates.ItemUpdate; import org.openrefine.wikidata.updates.ItemUpdate;
@ -8,22 +31,23 @@ import org.wikidata.wdtk.datamodel.interfaces.Value;
/** /**
* A scrutinizer that inspects the values of snaks and terms * A scrutinizer that inspects the values of snaks and terms
* @author antonin *
* @author Antonin Delpeuch
* *
*/ */
public abstract class ValueScrutinizer extends SnakScrutinizer { public abstract class ValueScrutinizer extends SnakScrutinizer {
@Override @Override
public void scrutinize(ItemUpdate update) { public void scrutinize(ItemUpdate update) {
super.scrutinize(update); super.scrutinize(update);
for(MonolingualTextValue label : update.getLabels()) { for (MonolingualTextValue label : update.getLabels()) {
scrutinize(label); scrutinize(label);
} }
for(MonolingualTextValue alias : update.getAliases()) { for (MonolingualTextValue alias : update.getAliases()) {
scrutinize(alias); scrutinize(alias);
} }
for(MonolingualTextValue description : update.getDescriptions()) { for (MonolingualTextValue description : update.getDescriptions()) {
scrutinize(description); scrutinize(description);
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.HashMap; import java.util.HashMap;
@ -17,20 +40,20 @@ import org.wikidata.wdtk.datamodel.interfaces.Value;
* *
*/ */
public class WhitespaceScrutinizer extends ValueScrutinizer { public class WhitespaceScrutinizer extends ValueScrutinizer {
private Map<String,Pattern> _issuesMap; private Map<String, Pattern> _issuesMap;
public static final String leadingWhitespaceType = "leading-whitespace"; public static final String leadingWhitespaceType = "leading-whitespace";
public static final String trailingWhitespaceType = "trailing-whitespace"; public static final String trailingWhitespaceType = "trailing-whitespace";
public static final String duplicateWhitespaceType = "duplicate-whitespace"; public static final String duplicateWhitespaceType = "duplicate-whitespace";
public static final String nonPrintableCharsType = "non-printable-characters"; public static final String nonPrintableCharsType = "non-printable-characters";
public WhitespaceScrutinizer() { public WhitespaceScrutinizer() {
_issuesMap = new HashMap<>(); _issuesMap = new HashMap<>();
_issuesMap.put(leadingWhitespaceType, Pattern.compile("^\\s")); _issuesMap.put(leadingWhitespaceType, Pattern.compile("^\\s"));
_issuesMap.put(trailingWhitespaceType, Pattern.compile("\\s$")); _issuesMap.put(trailingWhitespaceType, Pattern.compile("\\s$"));
_issuesMap.put(duplicateWhitespaceType, Pattern.compile("\\s\\s")); _issuesMap.put(duplicateWhitespaceType, Pattern.compile("\\s\\s"));
// https://stackoverflow.com/questions/14565934/regular-expression-to-remove-all-non-printable-characters // https://stackoverflow.com/questions/14565934/regular-expression-to-remove-all-non-printable-characters
_issuesMap.put(nonPrintableCharsType, Pattern.compile("[\\x00\\x03\\x08\\x0B\\x0C\\x0E-\\x1F]")); _issuesMap.put(nonPrintableCharsType, Pattern.compile("[\\x00\\x03\\x08\\x0B\\x0C\\x0E-\\x1F]"));
} }
@ -38,21 +61,21 @@ public class WhitespaceScrutinizer extends ValueScrutinizer {
@Override @Override
public void scrutinize(Value value) { public void scrutinize(Value value) {
String str = null; String str = null;
if(MonolingualTextValue.class.isInstance(value)) { if (MonolingualTextValue.class.isInstance(value)) {
str = ((MonolingualTextValue)value).getText(); str = ((MonolingualTextValue) value).getText();
} else if (StringValue.class.isInstance(value)) { } else if (StringValue.class.isInstance(value)) {
str = ((StringValue)value).getString(); str = ((StringValue) value).getString();
} }
if (str != null) { if (str != null) {
for(Entry<String,Pattern> entry : _issuesMap.entrySet()) { for (Entry<String, Pattern> entry : _issuesMap.entrySet()) {
if(entry.getValue().matcher(str).find()) { if (entry.getValue().matcher(str).find()) {
emitWarning(entry.getKey(), str); emitWarning(entry.getKey(), str);
} }
} }
} }
} }
private void emitWarning(String type, String example) { private void emitWarning(String type, String example) {
QAWarning warning = new QAWarning(type, null, QAWarning.Severity.WARNING, 1); QAWarning warning = new QAWarning(type, null, QAWarning.Severity.WARNING, 1);
warning.setProperty("example_string", example); warning.setProperty("example_string", example);

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
@ -10,39 +33,36 @@ import com.google.refine.model.ColumnModel;
import com.google.refine.model.Row; import com.google.refine.model.Row;
/** /**
* A class holding all the necessary information about * A class holding all the necessary information about the context in which a
* the context in which a schema expression is evaluated. * schema expression is evaluated.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public class ExpressionContext { public class ExpressionContext {
private String baseIRI; private String baseIRI;
private int rowId; private int rowId;
private Row row; private Row row;
private ColumnModel columnModel; private ColumnModel columnModel;
private QAWarningStore warningStore; private QAWarningStore warningStore;
/** /**
* Builds an expression context to evaluate a schema on a row * Builds an expression context to evaluate a schema on a row
*
* @param baseIRI * @param baseIRI
* the siteIRI of the schema * the siteIRI of the schema
* @param rowId * @param rowId
* the id of the row currently visited * the id of the row currently visited
* @param row * @param row
* the row itself * the row itself
* @param columnModel * @param columnModel
* lets us access cells by column name * lets us access cells by column name
* @param warningStore * @param warningStore
* where to store the issues encountered when * where to store the issues encountered when evaluating (can be set
* evaluating (can be set to null if these issues should be ignored) * to null if these issues should be ignored)
*/ */
public ExpressionContext( public ExpressionContext(String baseIRI, int rowId, Row row, ColumnModel columnModel, QAWarningStore warningStore) {
String baseIRI,
int rowId,
Row row,
ColumnModel columnModel,
QAWarningStore warningStore) {
Validate.notNull(baseIRI); Validate.notNull(baseIRI);
this.baseIRI = baseIRI; this.baseIRI = baseIRI;
this.rowId = rowId; this.rowId = rowId;
@ -52,18 +72,18 @@ public class ExpressionContext {
this.columnModel = columnModel; this.columnModel = columnModel;
this.warningStore = warningStore; this.warningStore = warningStore;
} }
public String getBaseIRI() { public String getBaseIRI() {
return baseIRI; return baseIRI;
} }
/** /**
* Retrieves a cell in the current row, by column name. * Retrieves a cell in the current row, by column name. If the column does not
* If the column does not exist, null is returned. * exist, null is returned.
* *
* @param name * @param name
* the name of the column to retrieve the cell from * the name of the column to retrieve the cell from
* @return * @return the cell
* the cell
*/ */
public Cell getCellByName(String name) { public Cell getCellByName(String name) {
Column column = columnModel.getColumnByName(name); Column column = columnModel.getColumnByName(name);
@ -74,11 +94,11 @@ public class ExpressionContext {
return null; return null;
} }
} }
public int getRowId() { public int getRowId() {
return rowId; return rowId;
} }
public void addWarning(QAWarning warning) { public void addWarning(QAWarning warning) {
if (warningStore != null) { if (warningStore != null) {
warningStore.addWarning(warning); warningStore.addWarning(warning);

View File

@ -1,12 +1,35 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import org.jsoup.helper.Validate; import org.jsoup.helper.Validate;
import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException; import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException;
@ -19,8 +42,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
/** /**
* A constant for a time value, accepting a number of formats * A constant for a time value, accepting a number of formats which determine
* which determine the precision of the parsed value. * the precision of the parsed value.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
@ -28,60 +51,57 @@ import com.google.common.collect.ImmutableMap;
public class WbDateConstant implements WbExpression<TimeValue> { public class WbDateConstant implements WbExpression<TimeValue> {
/** /**
* Map of formats accepted by the parser. Each format is associated * Map of formats accepted by the parser. Each format is associated to the time
* to the time precision it induces (an integer according to Wikibase's data model). * precision it induces (an integer according to Wikibase's data model).
*/ */
public static Map<SimpleDateFormat,Integer> acceptedFormats = ImmutableMap.<SimpleDateFormat,Integer>builder() public static Map<SimpleDateFormat, Integer> acceptedFormats = ImmutableMap.<SimpleDateFormat, Integer> builder()
.put(new SimpleDateFormat("yyyy"), 9) .put(new SimpleDateFormat("yyyy"), 9).put(new SimpleDateFormat("yyyy-MM"), 10)
.put(new SimpleDateFormat("yyyy-MM"), 10) .put(new SimpleDateFormat("yyyy-MM-dd"), 11).put(new SimpleDateFormat("yyyy-MM-dd'T'HH"), 12)
.put(new SimpleDateFormat("yyyy-MM-dd"), 11) .put(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm"), 13)
.put(new SimpleDateFormat("yyyy-MM-dd'T'HH"), 12) .put(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"), 14).build();
.put(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm"), 13)
.put(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"), 14)
.build();
private TimeValue parsed; private TimeValue parsed;
private String origDatestamp; private String origDatestamp;
/** /**
* Constructor. Used for deserialization from JSON. * Constructor. Used for deserialization from JSON. The object will be
* The object will be constructed even if the time cannot * constructed even if the time cannot be parsed (it will evaluate to null) in
* be parsed (it will evaluate to null) in {@link evaluate}. * {@link evaluate}.
* *
* @param origDatestamp * @param origDatestamp
* the date value as a string * the date value as a string
*/ */
@JsonCreator @JsonCreator
public WbDateConstant( public WbDateConstant(@JsonProperty("value") String origDatestamp) {
@JsonProperty("value") String origDatestamp) {
Validate.notNull(origDatestamp); Validate.notNull(origDatestamp);
this.setOrigDatestamp(origDatestamp); this.setOrigDatestamp(origDatestamp);
} }
@Override @Override
public TimeValue evaluate(ExpressionContext ctxt) public TimeValue evaluate(ExpressionContext ctxt)
throws SkipSchemaExpressionException { throws SkipSchemaExpressionException {
return parsed; return parsed;
} }
/** /**
* Parses a timestamp into a Wikibase {@link TimeValue}. The * Parses a timestamp into a Wikibase {@link TimeValue}. The precision is
* precision is automatically inferred from the format. * automatically inferred from the format.
* *
* @param datestamp * @param datestamp
* the time to parse * the time to parse
* @return * @return
* @throws ParseException * @throws ParseException
* if the time cannot be parsed * if the time cannot be parsed
*/ */
public static TimeValue parse(String datestamp) throws ParseException { public static TimeValue parse(String datestamp)
throws ParseException {
Date date = null; Date date = null;
int precision = 9; // default precision (will be overridden) int precision = 9; // default precision (will be overridden)
for(Entry<SimpleDateFormat,Integer> entry : acceptedFormats.entrySet()) { for (Entry<SimpleDateFormat, Integer> entry : acceptedFormats.entrySet()) {
ParsePosition position = new ParsePosition(0); ParsePosition position = new ParsePosition(0);
String trimmedDatestamp = datestamp.trim(); String trimmedDatestamp = datestamp.trim();
date = entry.getKey().parse(trimmedDatestamp, position); date = entry.getKey().parse(trimmedDatestamp, position);
// Ignore parses which failed or do not consume all the input // Ignore parses which failed or do not consume all the input
if (date != null && position.getIndex() == trimmedDatestamp.length()) { if (date != null && position.getIndex() == trimmedDatestamp.length()) {
precision = entry.getValue(); precision = entry.getValue();
@ -94,21 +114,16 @@ public class WbDateConstant implements WbExpression<TimeValue> {
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
calendar = Calendar.getInstance(); calendar = Calendar.getInstance();
calendar.setTime(date); calendar.setTime(date);
return Datamodel.makeTimeValue( return Datamodel.makeTimeValue(calendar.get(Calendar.YEAR), (byte) (calendar.get(Calendar.MONTH) + 1), // java
calendar.get(Calendar.YEAR), // starts
(byte) (calendar.get(Calendar.MONTH)+1), // java starts at 0 // at
(byte) calendar.get(Calendar.DAY_OF_MONTH), // 0
(byte) calendar.get(Calendar.HOUR_OF_DAY), (byte) calendar.get(Calendar.DAY_OF_MONTH), (byte) calendar.get(Calendar.HOUR_OF_DAY),
(byte) calendar.get(Calendar.MINUTE), (byte) calendar.get(Calendar.MINUTE), (byte) calendar.get(Calendar.SECOND), (byte) precision, 0, 1,
(byte) calendar.get(Calendar.SECOND), calendar.getTimeZone().getRawOffset() / 3600000, TimeValue.CM_GREGORIAN_PRO);
(byte) precision, }
0,
1,
calendar.getTimeZone().getRawOffset()/3600000,
TimeValue.CM_GREGORIAN_PRO);
}
} }
/** /**
* @return the original datestamp * @return the original datestamp
*/ */
@ -120,21 +135,21 @@ public class WbDateConstant implements WbExpression<TimeValue> {
private void setOrigDatestamp(String origDatestamp) { private void setOrigDatestamp(String origDatestamp) {
this.origDatestamp = origDatestamp; this.origDatestamp = origDatestamp;
try { try {
this.parsed = parse(origDatestamp); this.parsed = parse(origDatestamp);
} catch(ParseException e) { } catch (ParseException e) {
throw new IllegalArgumentException("Invalid datestamp provided: "+origDatestamp); throw new IllegalArgumentException("Invalid datestamp provided: " + origDatestamp);
} }
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !WbDateConstant.class.isInstance(other)) { if (other == null || !WbDateConstant.class.isInstance(other)) {
return false; return false;
} }
WbDateConstant otherConstant = (WbDateConstant)other; WbDateConstant otherConstant = (WbDateConstant) other;
return origDatestamp.equals(otherConstant.getOrigDatestamp()); return origDatestamp.equals(otherConstant.getOrigDatestamp());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return origDatestamp.hashCode(); return origDatestamp.hashCode();

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import java.text.ParseException; import java.text.ParseException;
@ -10,19 +33,19 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.google.refine.model.Cell; import com.google.refine.model.Cell;
/** /**
* An expression that represents a time value, extracted from a string. * An expression that represents a time value, extracted from a string. A number
* A number of formats are recognized, see {@link WbDateConstant} for details. * of formats are recognized, see {@link WbDateConstant} for details.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public class WbDateVariable extends WbVariableExpr<TimeValue> { public class WbDateVariable extends WbVariableExpr<TimeValue> {
@JsonCreator @JsonCreator
public WbDateVariable() { public WbDateVariable() {
} }
public WbDateVariable(String columnName) { public WbDateVariable(String columnName) {
setColumnName(columnName); setColumnName(columnName);
} }
@ -37,7 +60,7 @@ public class WbDateVariable extends WbVariableExpr<TimeValue> {
throw new SkipSchemaExpressionException(); throw new SkipSchemaExpressionException();
} }
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
return equalAsVariables(other, WbDateVariable.class); return equalAsVariables(other, WbDateVariable.class);

View File

@ -1,40 +1,60 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException; import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException;
import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
/** /**
* The base interface for all expressions, which evaluate to a * The base interface for all expressions, which evaluate to a particular type T
* particular type T in an ExpressionContext. * in an ExpressionContext.
*/ */
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
include=JsonTypeInfo.As.PROPERTY, @JsonSubTypes({ @Type(value = WbStringConstant.class, name = "wbstringconstant"),
property="type") @Type(value = WbStringVariable.class, name = "wbstringvariable"),
@JsonSubTypes({ @Type(value = WbLocationConstant.class, name = "wblocationconstant"),
@Type(value = WbStringConstant.class, name = "wbstringconstant"), @Type(value = WbLocationVariable.class, name = "wblocationvariable"),
@Type(value = WbStringVariable.class, name = "wbstringvariable"), @Type(value = WbItemConstant.class, name = "wbitemconstant"),
@Type(value = WbLocationConstant.class, name = "wblocationconstant"), @Type(value = WbItemVariable.class, name = "wbitemvariable"),
@Type(value = WbLocationVariable.class, name = "wblocationvariable"), @Type(value = WbLanguageConstant.class, name = "wblanguageconstant"),
@Type(value = WbItemConstant.class, name = "wbitemconstant"), @Type(value = WbLanguageVariable.class, name = "wblanguagevariable"),
@Type(value = WbItemVariable.class, name = "wbitemvariable"), @Type(value = WbDateConstant.class, name = "wbdateconstant"),
@Type(value = WbLanguageConstant.class, name = "wblanguageconstant"), @Type(value = WbDateVariable.class, name = "wbdatevariable"),
@Type(value = WbLanguageVariable.class, name = "wblanguagevariable"), @Type(value = WbMonolingualExpr.class, name = "wbmonolingualexpr"),
@Type(value = WbDateConstant.class, name = "wbdateconstant"), @Type(value = WbPropConstant.class, name = "wbpropconstant"),
@Type(value = WbDateVariable.class, name = "wbdatevariable"), @Type(value = WbLanguageConstant.class, name = "wblanguageconstant"),
@Type(value = WbMonolingualExpr.class, name = "wbmonolingualexpr"), @Type(value = WbLanguageVariable.class, name = "wblanguagevariable"),
@Type(value = WbPropConstant.class, name = "wbpropconstant"), @Type(value = WbQuantityExpr.class, name = "wbquantityexpr"), })
@Type(value = WbLanguageConstant.class, name = "wblanguageconstant"), public interface WbExpression<T> {
@Type(value = WbLanguageVariable.class, name = "wblanguagevariable"),
@Type(value = WbQuantityExpr.class, name="wbquantityexpr"),
})
public interface WbExpression<T> {
/** /**
* Evaluates the value expression in a given context, * Evaluates the value expression in a given context, returns a Wikibase value
* returns a Wikibase value suitable to be the target of a claim. * suitable to be the target of a claim.
*/ */
public T evaluate(ExpressionContext ctxt) throws SkipSchemaExpressionException; public T evaluate(ExpressionContext ctxt)
throws SkipSchemaExpressionException;
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.jsoup.helper.Validate; import org.jsoup.helper.Validate;
@ -8,18 +31,15 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* Represents an item that does not vary, * Represents an item that does not vary, it is independent of the row.
* it is independent of the row.
*/ */
public class WbItemConstant implements WbExpression<ItemIdValue> { public class WbItemConstant implements WbExpression<ItemIdValue> {
private String qid; private String qid;
private String label; private String label;
@JsonCreator @JsonCreator
public WbItemConstant( public WbItemConstant(@JsonProperty("qid") String qid, @JsonProperty("label") String label) {
@JsonProperty("qid") String qid,
@JsonProperty("label") String label) {
Validate.notNull(qid); Validate.notNull(qid);
this.qid = qid; this.qid = qid;
Validate.notNull(label); Validate.notNull(label);
@ -28,10 +48,7 @@ public class WbItemConstant implements WbExpression<ItemIdValue> {
@Override @Override
public ItemIdValue evaluate(ExpressionContext ctxt) { public ItemIdValue evaluate(ExpressionContext ctxt) {
return new SuggestedItemIdValue( return new SuggestedItemIdValue(qid, ctxt.getBaseIRI(), label);
qid,
ctxt.getBaseIRI(),
label);
} }
@JsonProperty("qid") @JsonProperty("qid")
@ -43,16 +60,16 @@ public class WbItemConstant implements WbExpression<ItemIdValue> {
public String getLabel() { public String getLabel() {
return label; return label;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !WbItemConstant.class.isInstance(other)) { if (other == null || !WbItemConstant.class.isInstance(other)) {
return false; return false;
} }
WbItemConstant otherConstant = (WbItemConstant)other; WbItemConstant otherConstant = (WbItemConstant) other;
return (qid.equals(otherConstant.getQid()) && label.equals(otherConstant.getLabel())); return (qid.equals(otherConstant.getQid()) && label.equals(otherConstant.getLabel()));
} }
@Override @Override
public int hashCode() { public int hashCode() {
return qid.hashCode() + label.hashCode(); return qid.hashCode() + label.hashCode();

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import java.util.Collections; import java.util.Collections;
@ -17,51 +40,51 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;
/** /**
* The representation of an item document, which can contain * The representation of an item document, which can contain variables both for
* variables both for its own id and in its contents. * its own id and in its contents.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use=JsonTypeInfo.Id.NONE) @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
public class WbItemDocumentExpr extends JacksonJsonizable implements WbExpression<ItemUpdate> { public class WbItemDocumentExpr extends JacksonJsonizable implements WbExpression<ItemUpdate> {
private WbExpression<? extends ItemIdValue> subject; private WbExpression<? extends ItemIdValue> subject;
private List<WbNameDescExpr> nameDescs; private List<WbNameDescExpr> nameDescs;
private List<WbStatementGroupExpr> statementGroups; private List<WbStatementGroupExpr> statementGroups;
@JsonCreator @JsonCreator
public WbItemDocumentExpr( public WbItemDocumentExpr(@JsonProperty("subject") WbExpression<? extends ItemIdValue> subjectExpr,
@JsonProperty("subject") WbExpression<? extends ItemIdValue> subjectExpr,
@JsonProperty("nameDescs") List<WbNameDescExpr> nameDescExprs, @JsonProperty("nameDescs") List<WbNameDescExpr> nameDescExprs,
@JsonProperty("statementGroups") List<WbStatementGroupExpr> statementGroupExprs) { @JsonProperty("statementGroups") List<WbStatementGroupExpr> statementGroupExprs) {
Validate.notNull(subjectExpr); Validate.notNull(subjectExpr);
this.subject = subjectExpr; this.subject = subjectExpr;
if(nameDescExprs == null) { if (nameDescExprs == null) {
nameDescExprs = Collections.emptyList(); nameDescExprs = Collections.emptyList();
} }
this.nameDescs = nameDescExprs; this.nameDescs = nameDescExprs;
if(statementGroupExprs == null) { if (statementGroupExprs == null) {
statementGroupExprs = Collections.emptyList(); statementGroupExprs = Collections.emptyList();
} }
this.statementGroups = statementGroupExprs; this.statementGroups = statementGroupExprs;
} }
@Override @Override
public ItemUpdate evaluate(ExpressionContext ctxt) throws SkipSchemaExpressionException { public ItemUpdate evaluate(ExpressionContext ctxt)
throws SkipSchemaExpressionException {
ItemIdValue subjectId = getSubject().evaluate(ctxt); ItemIdValue subjectId = getSubject().evaluate(ctxt);
ItemUpdateBuilder update = new ItemUpdateBuilder(subjectId); ItemUpdateBuilder update = new ItemUpdateBuilder(subjectId);
for(WbStatementGroupExpr expr : getStatementGroups()) { for (WbStatementGroupExpr expr : getStatementGroups()) {
try { try {
for(Statement s : expr.evaluate(ctxt, subjectId).getStatements()) { for (Statement s : expr.evaluate(ctxt, subjectId).getStatements()) {
update.addStatement(s); update.addStatement(s);
} }
} catch (SkipSchemaExpressionException e) { } catch (SkipSchemaExpressionException e) {
continue; continue;
} }
} }
for(WbNameDescExpr expr : getNameDescs()) { for (WbNameDescExpr expr : getNameDescs()) {
expr.contributeTo(update, ctxt); expr.contributeTo(update, ctxt);
} }
return update.build(); return update.build();
@ -81,18 +104,17 @@ public class WbItemDocumentExpr extends JacksonJsonizable implements WbExpressio
public List<WbStatementGroupExpr> getStatementGroups() { public List<WbStatementGroupExpr> getStatementGroups() {
return statementGroups; return statementGroups;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !WbItemDocumentExpr.class.isInstance(other)) { if (other == null || !WbItemDocumentExpr.class.isInstance(other)) {
return false; return false;
} }
WbItemDocumentExpr otherExpr = (WbItemDocumentExpr)other; WbItemDocumentExpr otherExpr = (WbItemDocumentExpr) other;
return subject.equals(otherExpr.getSubject()) && return subject.equals(otherExpr.getSubject()) && nameDescs.equals(otherExpr.getNameDescs())
nameDescs.equals(otherExpr.getNameDescs()) && && statementGroups.equals(otherExpr.getStatementGroups());
statementGroups.equals(otherExpr.getStatementGroups());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return subject.hashCode() + nameDescs.hashCode() + statementGroups.hashCode(); return subject.hashCode() + nameDescs.hashCode() + statementGroups.hashCode();

View File

@ -1,6 +1,28 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.openrefine.wikidata.schema.entityvalues.ReconItemIdValue; import org.openrefine.wikidata.schema.entityvalues.ReconItemIdValue;
import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException; import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue; import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
@ -17,33 +39,33 @@ import com.google.refine.model.Recon.Judgment;
* *
*/ */
public class WbItemVariable extends WbVariableExpr<ItemIdValue> { public class WbItemVariable extends WbVariableExpr<ItemIdValue> {
@JsonCreator @JsonCreator
public WbItemVariable() { public WbItemVariable() {
} }
/** /**
* Constructs a variable and sets the column it is bound to. Mostly * Constructs a variable and sets the column it is bound to. Mostly used as a
* used as a convenience method for testing. * convenience method for testing.
* *
* @param columnName * @param columnName
* the name of the column the expression should draw its value from * the name of the column the expression should draw its value from
*/ */
public WbItemVariable(String columnName) { public WbItemVariable(String columnName) {
setColumnName(columnName); setColumnName(columnName);
} }
@Override @Override
public ItemIdValue fromCell(Cell cell, ExpressionContext ctxt) throws SkipSchemaExpressionException { public ItemIdValue fromCell(Cell cell, ExpressionContext ctxt)
throws SkipSchemaExpressionException {
if (cell.recon != null if (cell.recon != null
&& (Judgment.Matched.equals(cell.recon.judgment) || && (Judgment.Matched.equals(cell.recon.judgment) || Judgment.New.equals(cell.recon.judgment))) {
Judgment.New.equals(cell.recon.judgment))) {
return new ReconItemIdValue(cell.recon, cell.value.toString()); return new ReconItemIdValue(cell.recon, cell.value.toString());
} }
throw new SkipSchemaExpressionException(); throw new SkipSchemaExpressionException();
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
return equalAsVariables(other, WbItemVariable.class); return equalAsVariables(other, WbItemVariable.class);

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
@ -14,43 +37,41 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* *
*/ */
public class WbLanguageConstant implements WbExpression<String> { public class WbLanguageConstant implements WbExpression<String> {
protected String _langId; protected String _langId;
protected String _langLabel; protected String _langLabel;
@JsonCreator @JsonCreator
public WbLanguageConstant( public WbLanguageConstant(@JsonProperty("id") String langId, @JsonProperty("label") String langLabel) {
@JsonProperty("id") String langId,
@JsonProperty("label") String langLabel) {
_langId = normalizeLanguageCode(langId); _langId = normalizeLanguageCode(langId);
Validate.notNull(_langId, "A valid language code must be provided."); Validate.notNull(_langId, "A valid language code must be provided.");
Validate.notNull(langLabel); Validate.notNull(langLabel);
_langLabel = langLabel; _langLabel = langLabel;
} }
/** /**
* Checks that a language code is valid and returns its preferred * Checks that a language code is valid and returns its preferred version
* version (converting deprecated language codes to their better values). * (converting deprecated language codes to their better values).
* *
* @param lang * @param lang
* a Wikimedia language code * a Wikimedia language code
* @return * @return the normalized code, or null if the code is invalid.
* the normalized code, or null if the code is invalid.
*/ */
public static String normalizeLanguageCode(String lang) { public static String normalizeLanguageCode(String lang) {
try { try {
WikimediaLanguageCodes.getLanguageCode(lang); WikimediaLanguageCodes.getLanguageCode(lang);
return WikimediaLanguageCodes.fixLanguageCodeIfDeprecated(lang); return WikimediaLanguageCodes.fixLanguageCodeIfDeprecated(lang);
} catch(IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return null; return null;
} }
} }
@Override @Override
public String evaluate(ExpressionContext ctxt) throws SkipSchemaExpressionException { public String evaluate(ExpressionContext ctxt)
throws SkipSchemaExpressionException {
return _langId; return _langId;
} }
/** /**
* @return the language code for this language * @return the language code for this language
*/ */
@ -58,7 +79,7 @@ public class WbLanguageConstant implements WbExpression<String> {
public String getLang() { public String getLang() {
return _langId; return _langId;
} }
/** /**
* @return the name of the language in itself * @return the name of the language in itself
*/ */
@ -66,19 +87,19 @@ public class WbLanguageConstant implements WbExpression<String> {
public String getLabel() { public String getLabel() {
return _langLabel; return _langLabel;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !WbLanguageConstant.class.isInstance(other)) { if (other == null || !WbLanguageConstant.class.isInstance(other)) {
return false; return false;
} }
WbLanguageConstant otherConstant = (WbLanguageConstant)other; WbLanguageConstant otherConstant = (WbLanguageConstant) other;
return _langId.equals(otherConstant.getLang()) && _langLabel.equals(otherConstant.getLabel()); return _langId.equals(otherConstant.getLang()) && _langLabel.equals(otherConstant.getLabel());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return _langId.hashCode(); return _langId.hashCode();
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException; import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException;
@ -7,22 +30,22 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.google.refine.model.Cell; import com.google.refine.model.Cell;
/** /**
* A language variable generates a language code from a cell. * A language variable generates a language code from a cell. It checks its
* It checks its values against a known list of valid language codes * values against a known list of valid language codes and fixes on the fly the
* and fixes on the fly the deprecated ones (see {@link WbLanguageConstant}). * deprecated ones (see {@link WbLanguageConstant}).
*/ */
public class WbLanguageVariable extends WbVariableExpr<String> { public class WbLanguageVariable extends WbVariableExpr<String> {
@JsonCreator @JsonCreator
public WbLanguageVariable() { public WbLanguageVariable() {
} }
/** /**
* Constructs a variable and sets the column it is bound to. Mostly * Constructs a variable and sets the column it is bound to. Mostly used as a
* used as a convenience method for testing. * convenience method for testing.
* *
* @param columnName * @param columnName
* the name of the column the expression should draw its value from * the name of the column the expression should draw its value from
*/ */
public WbLanguageVariable(String columnName) { public WbLanguageVariable(String columnName) {
setColumnName(columnName); setColumnName(columnName);
@ -40,7 +63,7 @@ public class WbLanguageVariable extends WbVariableExpr<String> {
} }
throw new SkipSchemaExpressionException(); throw new SkipSchemaExpressionException();
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
return equalAsVariables(other, WbLanguageVariable.class); return equalAsVariables(other, WbLanguageVariable.class);

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import java.text.ParseException; import java.text.ParseException;
@ -11,53 +34,52 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* A constant for a geographical location. The accepted format is lat,lng or lat/lng. * A constant for a geographical location. The accepted format is lat,lng or
* lat/lng.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public class WbLocationConstant implements WbExpression<GlobeCoordinatesValue> { public class WbLocationConstant implements WbExpression<GlobeCoordinatesValue> {
public static final double defaultPrecision = GlobeCoordinatesValue.PREC_TEN_MICRO_DEGREE; public static final double defaultPrecision = GlobeCoordinatesValue.PREC_TEN_MICRO_DEGREE;
private String value; private String value;
private GlobeCoordinatesValue parsed; private GlobeCoordinatesValue parsed;
@JsonCreator @JsonCreator
public WbLocationConstant( public WbLocationConstant(@JsonProperty("value") String origValue) throws ParseException {
@JsonProperty("value") String origValue) throws ParseException {
this.value = origValue; this.value = origValue;
Validate.notNull(origValue); Validate.notNull(origValue);
this.parsed = parse(origValue); this.parsed = parse(origValue);
Validate.notNull(this.parsed); Validate.notNull(this.parsed);
} }
/** /**
* Parses a string to a location. * Parses a string to a location.
* *
* @param expr * @param expr
* the string to parse * the string to parse
* @return * @return the parsed location
* the parsed location
* @throws ParseException * @throws ParseException
*/ */
public static GlobeCoordinatesValue parse(String expr) throws ParseException { public static GlobeCoordinatesValue parse(String expr)
throws ParseException {
double lat = 0; double lat = 0;
double lng = 0; double lng = 0;
double precision = defaultPrecision; double precision = defaultPrecision;
String[] parts = expr.split("[,/]"); String[] parts = expr.split("[,/]");
if (parts.length >= 2 && parts.length <= 3) { if (parts.length >= 2 && parts.length <= 3) {
try { try {
lat = Double.parseDouble(parts[0]); lat = Double.parseDouble(parts[0]);
lng = Double.parseDouble(parts[1]); lng = Double.parseDouble(parts[1]);
if (parts.length == 3) { if (parts.length == 3) {
precision = Double.parseDouble(parts[2]); precision = Double.parseDouble(parts[2]);
} }
return Datamodel.makeGlobeCoordinatesValue(lat, lng, precision, return Datamodel.makeGlobeCoordinatesValue(lat, lng, precision, GlobeCoordinatesValue.GLOBE_EARTH);
GlobeCoordinatesValue.GLOBE_EARTH); } catch (NumberFormatException e) {
} catch(NumberFormatException e) { ;
; }
}
} }
throw new ParseException("Invalid globe coordinates", 0); throw new ParseException("Invalid globe coordinates", 0);
} }
@ -67,7 +89,7 @@ public class WbLocationConstant implements WbExpression<GlobeCoordinatesValue> {
throws SkipSchemaExpressionException { throws SkipSchemaExpressionException {
return parsed; return parsed;
} }
/** /**
* @return the original value as a string. * @return the original value as a string.
*/ */
@ -75,16 +97,16 @@ public class WbLocationConstant implements WbExpression<GlobeCoordinatesValue> {
public String getValue() { public String getValue() {
return value; return value;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !WbLocationConstant.class.isInstance(other)) { if (other == null || !WbLocationConstant.class.isInstance(other)) {
return false; return false;
} }
WbLocationConstant otherConstant = (WbLocationConstant)other; WbLocationConstant otherConstant = (WbLocationConstant) other;
return value.equals(otherConstant.getValue()); return value.equals(otherConstant.getValue());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return value.hashCode(); return value.hashCode();

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import java.text.ParseException; import java.text.ParseException;
@ -9,12 +32,11 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.google.refine.model.Cell; import com.google.refine.model.Cell;
public class WbLocationVariable extends WbVariableExpr<GlobeCoordinatesValue> { public class WbLocationVariable extends WbVariableExpr<GlobeCoordinatesValue> {
@JsonCreator @JsonCreator
public WbLocationVariable() { public WbLocationVariable() {
} }
public WbLocationVariable(String columnName) { public WbLocationVariable(String columnName) {
@ -31,7 +53,7 @@ public class WbLocationVariable extends WbVariableExpr<GlobeCoordinatesValue> {
throw new SkipSchemaExpressionException(); throw new SkipSchemaExpressionException();
} }
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
return equalAsVariables(other, WbLocationVariable.class); return equalAsVariables(other, WbLocationVariable.class);

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
@ -10,15 +33,13 @@ import org.wikidata.wdtk.datamodel.interfaces.StringValue;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
public class WbMonolingualExpr implements WbExpression<MonolingualTextValue> { public class WbMonolingualExpr implements WbExpression<MonolingualTextValue> {
private WbExpression<? extends String> languageExpr; private WbExpression<? extends String> languageExpr;
private WbExpression<? extends StringValue> valueExpr; private WbExpression<? extends StringValue> valueExpr;
@JsonCreator @JsonCreator
public WbMonolingualExpr( public WbMonolingualExpr(@JsonProperty("language") WbExpression<? extends String> languageExpr,
@JsonProperty("language") WbExpression<? extends String> languageExpr,
@JsonProperty("value") WbExpression<? extends StringValue> valueExpr) { @JsonProperty("value") WbExpression<? extends StringValue> valueExpr) {
Validate.notNull(languageExpr); Validate.notNull(languageExpr);
this.languageExpr = languageExpr; this.languageExpr = languageExpr;
@ -33,13 +54,9 @@ public class WbMonolingualExpr implements WbExpression<MonolingualTextValue> {
try { try {
String lang = getLanguageExpr().evaluate(ctxt); String lang = getLanguageExpr().evaluate(ctxt);
return Datamodel.makeMonolingualTextValue(text, lang); return Datamodel.makeMonolingualTextValue(text, lang);
} catch(SkipSchemaExpressionException e) { } catch (SkipSchemaExpressionException e) {
QAWarning warning = new QAWarning( QAWarning warning = new QAWarning("monolingual-text-without-language", null, QAWarning.Severity.WARNING, 1);
"monolingual-text-without-language",
null,
QAWarning.Severity.WARNING,
1);
warning.setProperty("example_text", text); warning.setProperty("example_text", text);
ctxt.addWarning(warning); ctxt.addWarning(warning);
throw new SkipSchemaExpressionException(); throw new SkipSchemaExpressionException();
@ -55,17 +72,16 @@ public class WbMonolingualExpr implements WbExpression<MonolingualTextValue> {
public WbExpression<? extends StringValue> getValueExpr() { public WbExpression<? extends StringValue> getValueExpr() {
return valueExpr; return valueExpr;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !WbMonolingualExpr.class.isInstance(other)) { if (other == null || !WbMonolingualExpr.class.isInstance(other)) {
return false; return false;
} }
WbMonolingualExpr otherExpr = (WbMonolingualExpr)other; WbMonolingualExpr otherExpr = (WbMonolingualExpr) other;
return languageExpr.equals(otherExpr.getLanguageExpr()) && return languageExpr.equals(otherExpr.getLanguageExpr()) && valueExpr.equals(otherExpr.getValueExpr());
valueExpr.equals(otherExpr.getValueExpr());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return languageExpr.hashCode() + valueExpr.hashCode(); return languageExpr.hashCode() + valueExpr.hashCode();

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.jsoup.helper.Validate; import org.jsoup.helper.Validate;
@ -10,56 +33,53 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* An expression that represent a term (label, description or alias). * An expression that represent a term (label, description or alias). The
* The structure is slightly different from other expressions because * structure is slightly different from other expressions because we need to
* we need to call different methods on {@link ItemUpdateBuilder}. * call different methods on {@link ItemUpdateBuilder}.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class WbNameDescExpr { public class WbNameDescExpr {
enum NameDescrType { enum NameDescrType {
LABEL, LABEL, DESCRIPTION, ALIAS,
DESCRIPTION,
ALIAS,
} }
private NameDescrType type; private NameDescrType type;
private WbMonolingualExpr value; private WbMonolingualExpr value;
@JsonCreator @JsonCreator
public WbNameDescExpr( public WbNameDescExpr(@JsonProperty("name_type") NameDescrType type,
@JsonProperty("name_type") NameDescrType type,
@JsonProperty("value") WbMonolingualExpr value) { @JsonProperty("value") WbMonolingualExpr value) {
Validate.notNull(type); Validate.notNull(type);
this.type = type; this.type = type;
Validate.notNull(value); Validate.notNull(value);
this.value = value; this.value = value;
} }
/** /**
* Evaluates the expression and adds the result to the item update. * Evaluates the expression and adds the result to the item update.
* *
* @param item * @param item
* the item update where the term should be stored * the item update where the term should be stored
* @param ctxt * @param ctxt
* the evaluation context for the expression * the evaluation context for the expression
*/ */
public void contributeTo(ItemUpdateBuilder item, ExpressionContext ctxt) { public void contributeTo(ItemUpdateBuilder item, ExpressionContext ctxt) {
try { try {
MonolingualTextValue val = getValue().evaluate(ctxt); MonolingualTextValue val = getValue().evaluate(ctxt);
switch (getType()) { switch (getType()) {
case LABEL: case LABEL:
item.addLabel(val); item.addLabel(val);
break; break;
case DESCRIPTION: case DESCRIPTION:
item.addDescription(val); item.addDescription(val);
break; break;
case ALIAS: case ALIAS:
item.addAlias(val); item.addAlias(val);
break; break;
} }
} catch (SkipSchemaExpressionException e) { } catch (SkipSchemaExpressionException e) {
return; return;
@ -75,17 +95,16 @@ public class WbNameDescExpr {
public WbMonolingualExpr getValue() { public WbMonolingualExpr getValue() {
return value; return value;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !WbNameDescExpr.class.isInstance(other)) { if (other == null || !WbNameDescExpr.class.isInstance(other)) {
return false; return false;
} }
WbNameDescExpr otherExpr = (WbNameDescExpr)other; WbNameDescExpr otherExpr = (WbNameDescExpr) other;
return type.equals(otherExpr.getType()) && return type.equals(otherExpr.getType()) && value.equals(otherExpr.getValue());
value.equals(otherExpr.getValue());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return type.hashCode() + value.hashCode(); return type.hashCode() + value.hashCode();

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.jsoup.helper.Validate; import org.jsoup.helper.Validate;
@ -14,15 +37,13 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* *
*/ */
public class WbPropConstant implements WbExpression<PropertyIdValue> { public class WbPropConstant implements WbExpression<PropertyIdValue> {
private String pid; private String pid;
private String label; private String label;
private String datatype; private String datatype;
@JsonCreator @JsonCreator
public WbPropConstant( public WbPropConstant(@JsonProperty("pid") String pid, @JsonProperty("label") String label,
@JsonProperty("pid") String pid,
@JsonProperty("label") String label,
@JsonProperty("datatype") String datatype) { @JsonProperty("datatype") String datatype) {
Validate.notNull(pid); Validate.notNull(pid);
this.pid = pid; this.pid = pid;
@ -35,7 +56,7 @@ public class WbPropConstant implements WbExpression<PropertyIdValue> {
public PropertyIdValue evaluate(ExpressionContext ctxt) { public PropertyIdValue evaluate(ExpressionContext ctxt) {
return new SuggestedPropertyIdValue(pid, ctxt.getBaseIRI(), label); return new SuggestedPropertyIdValue(pid, ctxt.getBaseIRI(), label);
} }
@JsonProperty("pid") @JsonProperty("pid")
public String getPid() { public String getPid() {
return pid; return pid;
@ -45,19 +66,20 @@ public class WbPropConstant implements WbExpression<PropertyIdValue> {
public String getLabel() { public String getLabel() {
return label; return label;
} }
@JsonProperty("datatype") @JsonProperty("datatype")
public String getDatatype() { public String getDatatype() {
return datatype; return datatype;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !WbPropConstant.class.isInstance(other)) { if (other == null || !WbPropConstant.class.isInstance(other)) {
return false; return false;
} }
WbPropConstant otherConstant = (WbPropConstant)other; WbPropConstant otherConstant = (WbPropConstant) other;
return pid.equals(otherConstant.getPid()) && label.equals(otherConstant.getLabel()) && datatype.equals(otherConstant.getDatatype()); return pid.equals(otherConstant.getPid()) && label.equals(otherConstant.getLabel())
&& datatype.equals(otherConstant.getDatatype());
} }
@Override @Override

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -13,22 +36,21 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
public class WbQuantityExpr implements WbExpression<QuantityValue> { public class WbQuantityExpr implements WbExpression<QuantityValue> {
private final WbExpression<? extends StringValue> amountExpr; private final WbExpression<? extends StringValue> amountExpr;
private final WbExpression<? extends ItemIdValue> unitExpr; private final WbExpression<? extends ItemIdValue> unitExpr;
/** /**
* Creates an expression for a quantity, which * Creates an expression for a quantity, which contains two sub-expressions: one
* contains two sub-expressions: one for the amount (a string with * for the amount (a string with a particular format) and one for the unit,
* a particular format) and one for the unit, which is optional. * which is optional.
* *
* Setting unitExpr to null will give quantities without units. Setting * Setting unitExpr to null will give quantities without units. Setting it to a
* it to a non-null value will make the unit mandatory: if the unit expression * non-null value will make the unit mandatory: if the unit expression fails to
* fails to evaluate, the whole quantity expression will fail too. * evaluate, the whole quantity expression will fail too.
*/ */
@JsonCreator @JsonCreator
public WbQuantityExpr( public WbQuantityExpr(@JsonProperty("amount") WbExpression<? extends StringValue> amountExpr,
@JsonProperty("amount") WbExpression<? extends StringValue> amountExpr,
@JsonProperty("unit") WbExpression<? extends ItemIdValue> unitExpr) { @JsonProperty("unit") WbExpression<? extends ItemIdValue> unitExpr) {
Validate.notNull(amountExpr); Validate.notNull(amountExpr);
this.amountExpr = amountExpr; this.amountExpr = amountExpr;
@ -40,22 +62,22 @@ public class WbQuantityExpr implements WbExpression<QuantityValue> {
throws SkipSchemaExpressionException { throws SkipSchemaExpressionException {
StringValue amount = getLanguageExpr().evaluate(ctxt); StringValue amount = getLanguageExpr().evaluate(ctxt);
// we know the amount is nonnull, nonempty here // we know the amount is nonnull, nonempty here
BigDecimal parsedAmount = null; BigDecimal parsedAmount = null;
try { try {
parsedAmount = new BigDecimal(amount.getString()); parsedAmount = new BigDecimal(amount.getString());
} catch(NumberFormatException e) { } catch (NumberFormatException e) {
throw new SkipSchemaExpressionException(); throw new SkipSchemaExpressionException();
} }
if(getUnitExpr() != null) { if (getUnitExpr() != null) {
ItemIdValue unit = getUnitExpr().evaluate(ctxt); ItemIdValue unit = getUnitExpr().evaluate(ctxt);
return Datamodel.makeQuantityValue(parsedAmount, unit.getIri()); return Datamodel.makeQuantityValue(parsedAmount, unit.getIri());
} }
return Datamodel.makeQuantityValue(parsedAmount); return Datamodel.makeQuantityValue(parsedAmount);
} }
@JsonProperty("amount") @JsonProperty("amount")
public WbExpression<? extends StringValue> getLanguageExpr() { public WbExpression<? extends StringValue> getLanguageExpr() {
return amountExpr; return amountExpr;

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import java.util.ArrayList; import java.util.ArrayList;
@ -22,19 +45,20 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
* *
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use=JsonTypeInfo.Id.NONE) @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
public class WbReferenceExpr implements WbExpression<Reference> { public class WbReferenceExpr implements WbExpression<Reference> {
private List<WbSnakExpr> snakExprs; private List<WbSnakExpr> snakExprs;
@JsonCreator @JsonCreator
public WbReferenceExpr( public WbReferenceExpr(@JsonProperty("snaks") List<WbSnakExpr> snakExprs) {
@JsonProperty("snaks") List<WbSnakExpr> snakExprs) {
Validate.notNull(snakExprs); Validate.notNull(snakExprs);
this.snakExprs = snakExprs; this.snakExprs = snakExprs;
} }
@Override @Override
public Reference evaluate(ExpressionContext ctxt) throws SkipSchemaExpressionException { public Reference evaluate(ExpressionContext ctxt)
throws SkipSchemaExpressionException {
List<SnakGroup> snakGroups = new ArrayList<SnakGroup>(); List<SnakGroup> snakGroups = new ArrayList<SnakGroup>();
for (WbSnakExpr expr : getSnaks()) { for (WbSnakExpr expr : getSnaks()) {
List<Snak> snakList = new ArrayList<Snak>(1); List<Snak> snakList = new ArrayList<Snak>(1);
@ -45,7 +69,7 @@ public class WbReferenceExpr implements WbExpression<Reference> {
continue; continue;
} }
} }
if (! snakGroups.isEmpty()) { if (!snakGroups.isEmpty()) {
return Datamodel.makeReference(snakGroups); return Datamodel.makeReference(snakGroups);
} else { } else {
throw new SkipSchemaExpressionException(); throw new SkipSchemaExpressionException();
@ -59,13 +83,13 @@ public class WbReferenceExpr implements WbExpression<Reference> {
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !WbReferenceExpr.class.isInstance(other)) { if (other == null || !WbReferenceExpr.class.isInstance(other)) {
return false; return false;
} }
WbReferenceExpr otherExpr = (WbReferenceExpr)other; WbReferenceExpr otherExpr = (WbReferenceExpr) other;
return snakExprs.equals(otherExpr.getSnaks()); return snakExprs.equals(otherExpr.getSnaks());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return snakExprs.hashCode(); return snakExprs.hashCode();

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.jsoup.helper.Validate; import org.jsoup.helper.Validate;
@ -19,15 +42,14 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
* *
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use=JsonTypeInfo.Id.NONE) @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
public class WbSnakExpr implements WbExpression<Snak> { public class WbSnakExpr implements WbExpression<Snak> {
private WbExpression<? extends PropertyIdValue> prop; private WbExpression<? extends PropertyIdValue> prop;
private WbExpression<? extends Value> value; private WbExpression<? extends Value> value;
@JsonCreator @JsonCreator
public WbSnakExpr( public WbSnakExpr(@JsonProperty("prop") WbExpression<? extends PropertyIdValue> propExpr,
@JsonProperty("prop") WbExpression<? extends PropertyIdValue> propExpr,
@JsonProperty("value") WbExpression<? extends Value> valueExpr) { @JsonProperty("value") WbExpression<? extends Value> valueExpr) {
Validate.notNull(propExpr); Validate.notNull(propExpr);
this.prop = propExpr; this.prop = propExpr;
@ -36,7 +58,8 @@ public class WbSnakExpr implements WbExpression<Snak> {
} }
@Override @Override
public Snak evaluate(ExpressionContext ctxt) throws SkipSchemaExpressionException { public Snak evaluate(ExpressionContext ctxt)
throws SkipSchemaExpressionException {
PropertyIdValue propertyId = getProp().evaluate(ctxt); PropertyIdValue propertyId = getProp().evaluate(ctxt);
Value evaluatedValue = value.evaluate(ctxt); Value evaluatedValue = value.evaluate(ctxt);
return Datamodel.makeValueSnak(propertyId, evaluatedValue); return Datamodel.makeValueSnak(propertyId, evaluatedValue);
@ -51,7 +74,7 @@ public class WbSnakExpr implements WbExpression<Snak> {
public WbExpression<? extends Value> getValue() { public WbExpression<? extends Value> getValue() {
return value; return value;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (other == null || !WbSnakExpr.class.isInstance(other)) { if (other == null || !WbSnakExpr.class.isInstance(other)) {
@ -60,7 +83,7 @@ public class WbSnakExpr implements WbExpression<Snak> {
WbSnakExpr otherExpr = (WbSnakExpr) other; WbSnakExpr otherExpr = (WbSnakExpr) other;
return prop.equals(otherExpr.getProp()) && value.equals(otherExpr.getValue()); return prop.equals(otherExpr.getProp()) && value.equals(otherExpr.getValue());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return prop.hashCode() + value.hashCode(); return prop.hashCode() + value.hashCode();

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import java.util.ArrayList; import java.util.ArrayList;
@ -10,10 +33,10 @@ import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException;
import org.wikidata.wdtk.datamodel.helpers.Datamodel; import org.wikidata.wdtk.datamodel.helpers.Datamodel;
import org.wikidata.wdtk.datamodel.interfaces.Claim; import org.wikidata.wdtk.datamodel.interfaces.Claim;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue; import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Reference; import org.wikidata.wdtk.datamodel.interfaces.Reference;
import org.wikidata.wdtk.datamodel.interfaces.Snak; import org.wikidata.wdtk.datamodel.interfaces.Snak;
import org.wikidata.wdtk.datamodel.interfaces.SnakGroup; import org.wikidata.wdtk.datamodel.interfaces.SnakGroup;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Statement; import org.wikidata.wdtk.datamodel.interfaces.Statement;
import org.wikidata.wdtk.datamodel.interfaces.StatementRank; import org.wikidata.wdtk.datamodel.interfaces.StatementRank;
import org.wikidata.wdtk.datamodel.interfaces.Value; import org.wikidata.wdtk.datamodel.interfaces.Value;
@ -24,14 +47,13 @@ import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class WbStatementExpr { public class WbStatementExpr {
private WbExpression<? extends Value> mainSnakValueExpr; private WbExpression<? extends Value> mainSnakValueExpr;
private List<WbSnakExpr> qualifierExprs; private List<WbSnakExpr> qualifierExprs;
private List<WbReferenceExpr> referenceExprs; private List<WbReferenceExpr> referenceExprs;
@JsonCreator @JsonCreator
public WbStatementExpr( public WbStatementExpr(@JsonProperty("value") WbExpression<? extends Value> mainSnakValueExpr,
@JsonProperty("value") WbExpression<? extends Value> mainSnakValueExpr,
@JsonProperty("qualifiers") List<WbSnakExpr> qualifierExprs, @JsonProperty("qualifiers") List<WbSnakExpr> qualifierExprs,
@JsonProperty("references") List<WbReferenceExpr> referenceExprs) { @JsonProperty("references") List<WbReferenceExpr> referenceExprs) {
Validate.notNull(mainSnakValueExpr); Validate.notNull(mainSnakValueExpr);
@ -45,7 +67,7 @@ public class WbStatementExpr {
} }
this.referenceExprs = referenceExprs; this.referenceExprs = referenceExprs;
} }
public static List<SnakGroup> groupSnaks(List<Snak> snaks) { public static List<SnakGroup> groupSnaks(List<Snak> snaks) {
List<SnakGroup> snakGroups = new ArrayList<SnakGroup>(); List<SnakGroup> snakGroups = new ArrayList<SnakGroup>();
for (Snak snak : snaks) { for (Snak snak : snaks) {
@ -55,23 +77,19 @@ public class WbStatementExpr {
} }
return snakGroups; return snakGroups;
} }
public Statement evaluate(ExpressionContext ctxt, ItemIdValue subject, PropertyIdValue propertyId) public Statement evaluate(ExpressionContext ctxt, ItemIdValue subject, PropertyIdValue propertyId)
throws SkipSchemaExpressionException { throws SkipSchemaExpressionException {
Value mainSnakValue = getMainsnak().evaluate(ctxt); Value mainSnakValue = getMainsnak().evaluate(ctxt);
Snak mainSnak = Datamodel.makeValueSnak(propertyId, mainSnakValue); Snak mainSnak = Datamodel.makeValueSnak(propertyId, mainSnakValue);
// evaluate qualifiers // evaluate qualifiers
List<Snak> qualifiers = new ArrayList<Snak>(getQualifiers().size()); List<Snak> qualifiers = new ArrayList<Snak>(getQualifiers().size());
for (WbSnakExpr qExpr : getQualifiers()) { for (WbSnakExpr qExpr : getQualifiers()) {
try { try {
qualifiers.add(qExpr.evaluate(ctxt)); qualifiers.add(qExpr.evaluate(ctxt));
} catch(SkipSchemaExpressionException e) { } catch (SkipSchemaExpressionException e) {
QAWarning warning = new QAWarning( QAWarning warning = new QAWarning("ignored-qualifiers", null, QAWarning.Severity.INFO, 1);
"ignored-qualifiers",
null,
QAWarning.Severity.INFO,
1);
warning.setProperty("example_entity", subject); warning.setProperty("example_entity", subject);
warning.setProperty("example_property_entity", mainSnak.getPropertyId()); warning.setProperty("example_property_entity", mainSnak.getPropertyId());
ctxt.addWarning(warning); ctxt.addWarning(warning);
@ -79,24 +97,20 @@ public class WbStatementExpr {
} }
List<SnakGroup> groupedQualifiers = groupSnaks(qualifiers); List<SnakGroup> groupedQualifiers = groupSnaks(qualifiers);
Claim claim = Datamodel.makeClaim(subject, mainSnak, groupedQualifiers); Claim claim = Datamodel.makeClaim(subject, mainSnak, groupedQualifiers);
// evaluate references // evaluate references
List<Reference> references = new ArrayList<Reference>(); List<Reference> references = new ArrayList<Reference>();
for (WbReferenceExpr rExpr : getReferences()) { for (WbReferenceExpr rExpr : getReferences()) {
try { try {
references.add(rExpr.evaluate(ctxt)); references.add(rExpr.evaluate(ctxt));
} catch(SkipSchemaExpressionException e) { } catch (SkipSchemaExpressionException e) {
QAWarning warning = new QAWarning( QAWarning warning = new QAWarning("ignored-references", null, QAWarning.Severity.INFO, 1);
"ignored-references",
null,
QAWarning.Severity.INFO,
1);
warning.setProperty("example_entity", subject); warning.setProperty("example_entity", subject);
warning.setProperty("example_property_entity", mainSnak.getPropertyId()); warning.setProperty("example_property_entity", mainSnak.getPropertyId());
ctxt.addWarning(warning); ctxt.addWarning(warning);
} }
} }
StatementRank rank = StatementRank.NORMAL; StatementRank rank = StatementRank.NORMAL;
return Datamodel.makeStatement(claim, references, rank, ""); return Datamodel.makeStatement(claim, references, rank, "");
} }
@ -115,18 +129,17 @@ public class WbStatementExpr {
public List<WbReferenceExpr> getReferences() { public List<WbReferenceExpr> getReferences() {
return referenceExprs; return referenceExprs;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (other == null || !WbStatementExpr.class.isInstance(other)) { if (other == null || !WbStatementExpr.class.isInstance(other)) {
return false; return false;
} }
WbStatementExpr otherExpr = (WbStatementExpr)other; WbStatementExpr otherExpr = (WbStatementExpr) other;
return mainSnakValueExpr.equals(otherExpr.getMainsnak()) && return mainSnakValueExpr.equals(otherExpr.getMainsnak()) && qualifierExprs.equals(otherExpr.getQualifiers())
qualifierExprs.equals(otherExpr.getQualifiers()) && && referenceExprs.equals(otherExpr.getReferences());
referenceExprs.equals(otherExpr.getReferences());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return mainSnakValueExpr.hashCode() + qualifierExprs.hashCode() + referenceExprs.hashCode(); return mainSnakValueExpr.hashCode() + qualifierExprs.hashCode() + referenceExprs.hashCode();

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import java.util.ArrayList; import java.util.ArrayList;
@ -14,17 +37,15 @@ import org.wikidata.wdtk.datamodel.interfaces.StatementGroup;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class WbStatementGroupExpr { public class WbStatementGroupExpr {
private WbExpression<? extends PropertyIdValue> propertyExpr; private WbExpression<? extends PropertyIdValue> propertyExpr;
private List<WbStatementExpr> statementExprs; private List<WbStatementExpr> statementExprs;
@JsonCreator @JsonCreator
public WbStatementGroupExpr( public WbStatementGroupExpr(@JsonProperty("property") WbExpression<? extends PropertyIdValue> propertyExpr,
@JsonProperty("property") WbExpression<? extends PropertyIdValue> propertyExpr,
@JsonProperty("statements") List<WbStatementExpr> claimExprs) { @JsonProperty("statements") List<WbStatementExpr> claimExprs) {
Validate.notNull(propertyExpr); Validate.notNull(propertyExpr);
this.propertyExpr = propertyExpr; this.propertyExpr = propertyExpr;
@ -33,10 +54,11 @@ public class WbStatementGroupExpr {
this.statementExprs = claimExprs; this.statementExprs = claimExprs;
} }
public StatementGroup evaluate(ExpressionContext ctxt, ItemIdValue subject) throws SkipSchemaExpressionException { public StatementGroup evaluate(ExpressionContext ctxt, ItemIdValue subject)
throws SkipSchemaExpressionException {
PropertyIdValue propertyId = propertyExpr.evaluate(ctxt); PropertyIdValue propertyId = propertyExpr.evaluate(ctxt);
List<Statement> statements = new ArrayList<Statement>(statementExprs.size()); List<Statement> statements = new ArrayList<Statement>(statementExprs.size());
for(WbStatementExpr expr : statementExprs) { for (WbStatementExpr expr : statementExprs) {
try { try {
statements.add(expr.evaluate(ctxt, subject, propertyId)); statements.add(expr.evaluate(ctxt, subject, propertyId));
} catch (SkipSchemaExpressionException e) { } catch (SkipSchemaExpressionException e) {
@ -59,17 +81,16 @@ public class WbStatementGroupExpr {
public List<WbStatementExpr> getStatements() { public List<WbStatementExpr> getStatements() {
return statementExprs; return statementExprs;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (other == null || !WbStatementGroupExpr.class.isInstance(other)) { if (other == null || !WbStatementGroupExpr.class.isInstance(other)) {
return false; return false;
} }
WbStatementGroupExpr otherExpr = (WbStatementGroupExpr)other; WbStatementGroupExpr otherExpr = (WbStatementGroupExpr) other;
return propertyExpr.equals(otherExpr.getProperty()) && return propertyExpr.equals(otherExpr.getProperty()) && statementExprs.equals(otherExpr.getStatements());
statementExprs.equals(otherExpr.getStatements());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return propertyExpr.hashCode() + statementExprs.hashCode(); return propertyExpr.hashCode() + statementExprs.hashCode();

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
@ -7,22 +30,21 @@ import org.wikidata.wdtk.datamodel.interfaces.StringValue;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
public class WbStringConstant implements WbExpression<StringValue> { public class WbStringConstant implements WbExpression<StringValue> {
private String value; private String value;
@JsonCreator @JsonCreator
public WbStringConstant(@JsonProperty("value") String value) { public WbStringConstant(@JsonProperty("value") String value) {
Validate.notNull(value); Validate.notNull(value);
this.value = value; this.value = value;
} }
@Override @Override
public StringValue evaluate(ExpressionContext ctxt) { public StringValue evaluate(ExpressionContext ctxt) {
return Datamodel.makeStringValue(value); return Datamodel.makeStringValue(value);
} }
@JsonProperty("value") @JsonProperty("value")
public String getValue() { public String getValue() {
return value; return value;

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException; import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException;
@ -15,17 +38,17 @@ import com.google.refine.model.Cell;
* *
*/ */
public class WbStringVariable extends WbVariableExpr<StringValue> { public class WbStringVariable extends WbVariableExpr<StringValue> {
@JsonCreator @JsonCreator
public WbStringVariable() { public WbStringVariable() {
} }
/** /**
* Constructs a variable and sets the column it is bound to. Mostly * Constructs a variable and sets the column it is bound to. Mostly used as a
* used as a convenience method for testing. * convenience method for testing.
* *
* @param columnName * @param columnName
* the name of the column the expression should draw its value from * the name of the column the expression should draw its value from
*/ */
public WbStringVariable(String columnName) { public WbStringVariable(String columnName) {
setColumnName(columnName); setColumnName(columnName);
@ -39,7 +62,7 @@ public class WbStringVariable extends WbVariableExpr<StringValue> {
} }
throw new SkipSchemaExpressionException(); throw new SkipSchemaExpressionException();
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
return equalAsVariables(other, WbStringVariable.class); return equalAsVariables(other, WbStringVariable.class);

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException; import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException;
@ -8,18 +31,18 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.refine.model.Cell; import com.google.refine.model.Cell;
/** /**
* A base class for expressions which draw their values * A base class for expressions which draw their values from a particular
* from a particular column. * column.
* *
* @author antonin * @author Antonin Delpeuch
* *
* @param <T> * @param <T>
* the type of Wikibase value returned by the expression. * the type of Wikibase value returned by the expression.
*/ */
public abstract class WbVariableExpr<T> implements WbExpression<T> { public abstract class WbVariableExpr<T> implements WbExpression<T> {
private String columnName; private String columnName;
/** /**
* Constructs a variable without setting the column name yet. * Constructs a variable without setting the column name yet.
*/ */
@ -27,21 +50,20 @@ public abstract class WbVariableExpr<T> implements WbExpression<T> {
public WbVariableExpr() { public WbVariableExpr() {
columnName = null; columnName = null;
} }
/** /**
* Returns the column name used by the variable. * Returns the column name used by the variable.
* @return *
* the OpenRefine column name * @return the OpenRefine column name
*/ */
@JsonProperty("columnName") @JsonProperty("columnName")
public String getColumnName() { public String getColumnName() {
return columnName; return columnName;
} }
/** /**
* Changes the column name used by the variable. * Changes the column name used by the variable. This is useful for
* This is useful for deserialization, as well as updates when * deserialization, as well as updates when column names change.
* column names change.
*/ */
@JsonProperty("columnName") @JsonProperty("columnName")
public void setColumnName(String columnName) { public void setColumnName(String columnName) {
@ -60,43 +82,42 @@ public abstract class WbVariableExpr<T> implements WbExpression<T> {
} }
throw new SkipSchemaExpressionException(); throw new SkipSchemaExpressionException();
} }
/** /**
* Method that should be implemented by subclasses, * Method that should be implemented by subclasses, converting an OpenRefine
* converting an OpenRefine cell to a Wikibase value. * cell to a Wikibase value. Access to other values and emiting warnings is
* Access to other values and emiting warnings is possible via * possible via the supplied EvaluationContext object.
* the supplied EvaluationContext object.
* *
* @param cell * @param cell
* the cell to convert * the cell to convert
* @param ctxt * @param ctxt
* the evaluation context * the evaluation context
* @return * @return the corresponding Wikibase value
* the corresponding Wikibase value
*/ */
public abstract T fromCell(Cell cell, ExpressionContext ctxt) throws SkipSchemaExpressionException; public abstract T fromCell(Cell cell, ExpressionContext ctxt)
throws SkipSchemaExpressionException;
/** /**
* Helper for equality methods of subclasses. * Helper for equality methods of subclasses.
* *
* @param other * @param other
* the object to compare * the object to compare
* @param columnName * @param columnName
* the column name to compare to * the column name to compare to
* @param targetClass * @param targetClass
* the target class for equality * the target class for equality
* @return * @return
*/ */
protected boolean equalAsVariables(Object other, Class<? extends WbVariableExpr<?>> targetClass) { protected boolean equalAsVariables(Object other, Class<? extends WbVariableExpr<?>> targetClass) {
if(other == null || !targetClass.isInstance(other)) { if (other == null || !targetClass.isInstance(other)) {
return false; return false;
} }
return columnName.equals(targetClass.cast(other).getColumnName()); return columnName.equals(targetClass.cast(other).getColumnName());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return columnName.hashCode(); return columnName.hashCode();
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema; package org.openrefine.wikidata.schema;
import java.util.ArrayList; import java.util.ArrayList;
@ -8,6 +31,10 @@ import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONWriter; import org.json.JSONWriter;
import org.openrefine.wikidata.qa.QAWarningStore;
import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException;
import org.openrefine.wikidata.updates.ItemUpdate;
import org.openrefine.wikidata.utils.JacksonJsonizable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -17,17 +44,10 @@ import com.google.refine.browsing.RowVisitor;
import com.google.refine.model.OverlayModel; import com.google.refine.model.OverlayModel;
import com.google.refine.model.Project; import com.google.refine.model.Project;
import com.google.refine.model.Row; import com.google.refine.model.Row;
import org.openrefine.wikidata.schema.WbItemDocumentExpr;
import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException;
import org.openrefine.wikidata.updates.ItemUpdate;
import org.openrefine.wikidata.updates.ItemUpdateBuilder;
import org.openrefine.wikidata.qa.QAWarningStore;
import org.openrefine.wikidata.schema.ExpressionContext;
import org.openrefine.wikidata.utils.JacksonJsonizable;
/** /**
* Main class representing a skeleton of Wikibase edits with * Main class representing a skeleton of Wikibase edits with OpenRefine columns
* OpenRefine columns as variables. * as variables.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
@ -35,18 +55,18 @@ import org.openrefine.wikidata.utils.JacksonJsonizable;
public class WikibaseSchema implements OverlayModel { public class WikibaseSchema implements OverlayModel {
final static Logger logger = LoggerFactory.getLogger("RdfSchema"); final static Logger logger = LoggerFactory.getLogger("RdfSchema");
protected List<WbItemDocumentExpr> itemDocumentExprs = new ArrayList<WbItemDocumentExpr>(); protected List<WbItemDocumentExpr> itemDocumentExprs = new ArrayList<WbItemDocumentExpr>();
protected String baseIri = "http://www.wikidata.org/entity/"; protected String baseIri = "http://www.wikidata.org/entity/";
/** /**
* Constructor. * Constructor.
*/ */
public WikibaseSchema() { public WikibaseSchema() {
} }
/** /**
* @return the site IRI of the Wikibase instance referenced by this schema * @return the site IRI of the Wikibase instance referenced by this schema
*/ */
@ -60,24 +80,24 @@ public class WikibaseSchema implements OverlayModel {
public List<WbItemDocumentExpr> getItemDocumentExpressions() { public List<WbItemDocumentExpr> getItemDocumentExpressions() {
return itemDocumentExprs; return itemDocumentExprs;
} }
public void setItemDocumentExpressions(List<WbItemDocumentExpr> exprs) { public void setItemDocumentExpressions(List<WbItemDocumentExpr> exprs) {
this.itemDocumentExprs = exprs; this.itemDocumentExprs = exprs;
} }
/** /**
* Evaluates all item documents in a particular expression context. * Evaluates all item documents in a particular expression context. This
* This specifies, among others, a row where the values of the variables * specifies, among others, a row where the values of the variables will be
* will be read. * read.
* *
* @param ctxt * @param ctxt
* the context in which the schema should be evaluated. * the context in which the schema should be evaluated.
* @return * @return
*/ */
public List<ItemUpdate> evaluateItemDocuments(ExpressionContext ctxt) { public List<ItemUpdate> evaluateItemDocuments(ExpressionContext ctxt) {
List<ItemUpdate> result = new ArrayList<>(); List<ItemUpdate> result = new ArrayList<>();
for (WbItemDocumentExpr expr : itemDocumentExprs) { for (WbItemDocumentExpr expr : itemDocumentExprs) {
try { try {
result.add(expr.evaluate(ctxt)); result.add(expr.evaluate(ctxt));
} catch (SkipSchemaExpressionException e) { } catch (SkipSchemaExpressionException e) {
@ -86,24 +106,23 @@ public class WikibaseSchema implements OverlayModel {
} }
return result; return result;
} }
/** /**
* Evaluates the schema on a project, returning a list of ItemUpdates * Evaluates the schema on a project, returning a list of ItemUpdates generated
* generated by the schema. * by the schema.
* *
* Some warnings will be emitted in the warning store: those are only * Some warnings will be emitted in the warning store: those are only the ones
* the ones that are generated at evaluation time (such as invalid formats * that are generated at evaluation time (such as invalid formats for dates).
* for dates). Issues detected on candidate statements (such as constraint * Issues detected on candidate statements (such as constraint violations) are
* violations) are not included at this stage. * not included at this stage.
* *
* @param project * @param project
* the project on which the schema should be evaluated * the project on which the schema should be evaluated
* @param engine * @param engine
* the engine, which gives access to the current facets * the engine, which gives access to the current facets
* @param warningStore * @param warningStore
* a store in which issues will be emitted * a store in which issues will be emitted
* @return item updates are stored in their * @return item updates are stored in their generating order (not merged yet).
* generating order (not merged yet).
*/ */
public List<ItemUpdate> evaluate(Project project, Engine engine, QAWarningStore warningStore) { public List<ItemUpdate> evaluate(Project project, Engine engine, QAWarningStore warningStore) {
List<ItemUpdate> result = new ArrayList<>(); List<ItemUpdate> result = new ArrayList<>();
@ -111,35 +130,32 @@ public class WikibaseSchema implements OverlayModel {
filteredRows.accept(project, new EvaluatingRowVisitor(result, warningStore)); filteredRows.accept(project, new EvaluatingRowVisitor(result, warningStore));
return result; return result;
} }
/** /**
* Same as above, ignoring any warnings. * Same as above, ignoring any warnings.
*/ */
public List<ItemUpdate> evaluate(Project project, Engine engine) { public List<ItemUpdate> evaluate(Project project, Engine engine) {
return evaluate(project, engine, null); return evaluate(project, engine, null);
} }
protected class EvaluatingRowVisitor implements RowVisitor { protected class EvaluatingRowVisitor implements RowVisitor {
private List<ItemUpdate> result; private List<ItemUpdate> result;
private QAWarningStore warningStore; private QAWarningStore warningStore;
public EvaluatingRowVisitor(List<ItemUpdate> result, QAWarningStore warningStore) { public EvaluatingRowVisitor(List<ItemUpdate> result, QAWarningStore warningStore) {
this.result = result; this.result = result;
this.warningStore = warningStore; this.warningStore = warningStore;
} }
@Override @Override
public void start(Project project) { public void start(Project project) {
; ;
} }
@Override @Override
public boolean visit(Project project, int rowIndex, Row row) { public boolean visit(Project project, int rowIndex, Row row) {
ExpressionContext ctxt = new ExpressionContext( ExpressionContext ctxt = new ExpressionContext(baseIri, rowIndex, row, project.columnModel, warningStore);
baseIri,
rowIndex,
row,
project.columnModel,
warningStore);
result.addAll(evaluateItemDocuments(ctxt)); result.addAll(evaluateItemDocuments(ctxt));
return false; return false;
} }
@ -150,12 +166,14 @@ public class WikibaseSchema implements OverlayModel {
} }
} }
static public WikibaseSchema reconstruct(JSONObject o) throws JSONException { static public WikibaseSchema reconstruct(JSONObject o)
throws JSONException {
JSONArray changeArr = o.getJSONArray("itemDocuments"); JSONArray changeArr = o.getJSONArray("itemDocuments");
WikibaseSchema schema = new WikibaseSchema(); WikibaseSchema schema = new WikibaseSchema();
for (int i = 0; i != changeArr.length(); i++) { for (int i = 0; i != changeArr.length(); i++) {
WbItemDocumentExpr changeExpr = JacksonJsonizable.fromJSONClass(changeArr.getJSONObject(i), WbItemDocumentExpr.class); WbItemDocumentExpr changeExpr = JacksonJsonizable.fromJSONClass(changeArr.getJSONObject(i),
WbItemDocumentExpr.class);
schema.itemDocumentExprs.add(changeExpr); schema.itemDocumentExprs.add(changeExpr);
} }
return schema; return schema;
@ -173,30 +191,31 @@ public class WikibaseSchema implements OverlayModel {
writer.endArray(); writer.endArray();
writer.endObject(); writer.endObject();
} }
static public WikibaseSchema load(Project project, JSONObject obj) throws Exception { static public WikibaseSchema load(Project project, JSONObject obj)
throws Exception {
return reconstruct(obj); return reconstruct(obj);
} }
@Override @Override
public void onBeforeSave(Project project) { public void onBeforeSave(Project project) {
} }
@Override @Override
public void onAfterSave(Project project) { public void onAfterSave(Project project) {
} }
@Override @Override
public void dispose(Project project) { public void dispose(Project project) {
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !WikibaseSchema.class.isInstance(other)) { if (other == null || !WikibaseSchema.class.isInstance(other)) {
return false; return false;
} }
WikibaseSchema otherSchema = (WikibaseSchema)other; WikibaseSchema otherSchema = (WikibaseSchema) other;
return itemDocumentExprs.equals(otherSchema.getItemDocumentExpressions()); return itemDocumentExprs.equals(otherSchema.getItemDocumentExpressions());
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema.entityvalues; package org.openrefine.wikidata.schema.entityvalues;
import java.util.List; import java.util.List;
@ -5,34 +28,31 @@ import java.util.List;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
/** /**
* An entity id value that also comes with * An entity id value that also comes with a label and possibly types.
* a label and possibly types.
* *
* The rationale behind this classes is that OpenRefine * The rationale behind this classes is that OpenRefine already stores labels
* already stores labels and types for the Wikidata items * and types for the Wikidata items it knows about (in the reconciliation data),
* it knows about (in the reconciliation data), so it is * so it is worth keeping this data to avoid re-fetching it when we need it.
* worth keeping this data to avoid re-fetching it when
* we need it.
* *
* @author antonin * @author Antonin Delpeuch
* *
*/ */
public interface PrefetchedEntityIdValue extends EntityIdValue { public interface PrefetchedEntityIdValue extends EntityIdValue {
/** /**
* This should return the label "as we got it", with no guarantee * This should return the label "as we got it", with no guarantee that it is
* that it is current or that its language matches that of the user. * current or that its language matches that of the user. In general though,
* In general though, that should be the case if the user always uses * that should be the case if the user always uses OpenRefine with the same
* OpenRefine with the same language settings. * language settings.
* *
* @return the preferred label of the entity * @return the preferred label of the entity
*/ */
public String getLabel(); public String getLabel();
/** /**
* Returns a list of types for this item. Again these are the types * Returns a list of types for this item. Again these are the types as they were
* as they were originally fetched from the reconciliation interface: * originally fetched from the reconciliation interface: they can diverge from
* they can diverge from what is currently on the item. * what is currently on the item.
* *
* Empty lists should be returned for * Empty lists should be returned for
*/ */

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema.entityvalues; package org.openrefine.wikidata.schema.entityvalues;
import java.util.ArrayList; import java.util.ArrayList;
@ -14,43 +37,41 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.refine.model.Recon; import com.google.refine.model.Recon;
/** /**
* An EntityIdValue that holds not just the id but also * An EntityIdValue that holds not just the id but also the label as fetched by
* the label as fetched by either the reconciliation interface * either the reconciliation interface or the suggester and its type, both
* or the suggester and its type, both stored as reconciliation * stored as reconciliation candidates.
* candidates.
* *
* This label will be localized depending on the language chosen * This label will be localized depending on the language chosen by the user for
* by the user for OpenRefine's interface. Storing it lets us * OpenRefine's interface. Storing it lets us reuse it later on without having
* reuse it later on without having to re-fetch it. * to re-fetch it.
* *
* Storing the types also lets us perform some constraint checks * Storing the types also lets us perform some constraint checks without
* without re-fetching the types of many items. * re-fetching the types of many items.
* *
* @author antonin * @author Antonin Delpeuch
* *
*/ */
public abstract class ReconEntityIdValue implements PrefetchedEntityIdValue { public abstract class ReconEntityIdValue implements PrefetchedEntityIdValue {
private Recon _recon; private Recon _recon;
private String _cellValue; private String _cellValue;
public ReconEntityIdValue(Recon match, String cellValue) { public ReconEntityIdValue(Recon match, String cellValue) {
_recon = match; _recon = match;
_cellValue = cellValue; _cellValue = cellValue;
assert (Recon.Judgment.Matched.equals(_recon.judgment) || assert (Recon.Judgment.Matched.equals(_recon.judgment) || Recon.Judgment.New.equals(_recon.judgment));
Recon.Judgment.New.equals(_recon.judgment));
} }
@JsonIgnore @JsonIgnore
public boolean isMatched() { public boolean isMatched() {
return Recon.Judgment.Matched.equals(_recon.judgment) && _recon.match != null; return Recon.Judgment.Matched.equals(_recon.judgment) && _recon.match != null;
} }
@JsonIgnore @JsonIgnore
public boolean isNew() { public boolean isNew() {
return !isMatched(); return !isMatched();
} }
public String getLabel() { public String getLabel() {
if (isMatched()) { if (isMatched()) {
return _recon.match.name; return _recon.match.name;
@ -66,32 +87,28 @@ public abstract class ReconEntityIdValue implements PrefetchedEntityIdValue {
return new ArrayList<>(); return new ArrayList<>();
} }
} }
@Override @Override
public abstract String getEntityType(); public abstract String getEntityType();
/** /**
* Returns the integer used internally in OpenRefine to identify the new * Returns the integer used internally in OpenRefine to identify the new item.
* item.
* *
* @return * @return the judgment history entry id of the reconciled cell
* the judgment history entry id of the reconciled cell
*/ */
public long getReconInternalId() { public long getReconInternalId() {
return getRecon().judgmentHistoryEntry; return getRecon().judgmentHistoryEntry;
} }
/** /**
* Returns the reconciliation object corresponding to this entity. * Returns the reconciliation object corresponding to this entity.
* *
* @return * @return the full reconciliation metadata of the corresponding cell
* the full reconciliation metadata of the corresponding cell
*/ */
public Recon getRecon() { public Recon getRecon() {
return _recon; return _recon;
} }
/** /**
* Returns the id of the reconciled item * Returns the id of the reconciled item
*/ */
@ -100,9 +117,9 @@ public abstract class ReconEntityIdValue implements PrefetchedEntityIdValue {
if (isMatched()) { if (isMatched()) {
return _recon.match.id; return _recon.match.id;
} else if (ET_ITEM.equals(getEntityType())) { } else if (ET_ITEM.equals(getEntityType())) {
return "Q"+getReconInternalId(); return "Q" + getReconInternalId();
} else if (ET_PROPERTY.equals(getEntityType())) { } else if (ET_PROPERTY.equals(getEntityType())) {
return "P"+getReconInternalId(); return "P" + getReconInternalId();
} }
return null; return null;
} }
@ -125,13 +142,13 @@ public abstract class ReconEntityIdValue implements PrefetchedEntityIdValue {
public <T> T accept(ValueVisitor<T> valueVisitor) { public <T> T accept(ValueVisitor<T> valueVisitor) {
return valueVisitor.visit(this); return valueVisitor.visit(this);
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
return Equality.equalsEntityIdValue(this, other); return Equality.equalsEntityIdValue(this, other);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Hash.hashCode(this); return Hash.hashCode(this);
@ -139,10 +156,10 @@ public abstract class ReconEntityIdValue implements PrefetchedEntityIdValue {
@Override @Override
public String toString() { public String toString() {
if(isNew()) { if (isNew()) {
return "new item (reconciled from " + getReconInternalId() +")"; return "new item (reconciled from " + getReconInternalId() + ")";
} else { } else {
return getIri() + " (reconciled from " + getReconInternalId()+")"; return getIri() + " (reconciled from " + getReconInternalId() + ")";
} }
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema.entityvalues; package org.openrefine.wikidata.schema.entityvalues;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue; import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema.entityvalues; package org.openrefine.wikidata.schema.entityvalues;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue; import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;

View File

@ -1,22 +1,44 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema.entityvalues; package org.openrefine.wikidata.schema.entityvalues;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.wikidata.wdtk.datamodel.helpers.Hash; import org.wikidata.wdtk.datamodel.helpers.Hash;
import org.wikidata.wdtk.datamodel.helpers.ToString;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
import org.wikidata.wdtk.datamodel.interfaces.ValueVisitor; import org.wikidata.wdtk.datamodel.interfaces.ValueVisitor;
/** /**
* An EntityIdValue that we have obtained from a suggest widget * An EntityIdValue that we have obtained from a suggest widget in the schema
* in the schema alignment dialog. * alignment dialog.
* *
* @author antonin * @author Antonin Delpeuch
* *
*/ */
public abstract class SuggestedEntityIdValue implements PrefetchedEntityIdValue { public abstract class SuggestedEntityIdValue implements PrefetchedEntityIdValue {
private String _id; private String _id;
private String _siteIRI; private String _siteIRI;
private String _label; private String _label;
@ -31,22 +53,22 @@ public abstract class SuggestedEntityIdValue implements PrefetchedEntityIdValue
public String getId() { public String getId() {
return _id; return _id;
} }
@Override @Override
public String getSiteIri() { public String getSiteIri() {
return _siteIRI; return _siteIRI;
} }
@Override @Override
public String getLabel() { public String getLabel() {
return _label; return _label;
} }
@Override @Override
public List<String> getTypes() { public List<String> getTypes() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public String getIri() { public String getIri() {
return getSiteIri() + getId(); return getSiteIri() + getId();
@ -56,14 +78,13 @@ public abstract class SuggestedEntityIdValue implements PrefetchedEntityIdValue
public <T> T accept(ValueVisitor<T> valueVisitor) { public <T> T accept(ValueVisitor<T> valueVisitor) {
return valueVisitor.visit(this); return valueVisitor.visit(this);
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (other == null || if (other == null || !EntityIdValue.class.isInstance(other)) {
!EntityIdValue.class.isInstance(other)) { return false;
return false;
} }
final EntityIdValue otherNew = (EntityIdValue)other; final EntityIdValue otherNew = (EntityIdValue) other;
return getIri().equals(otherNew.getIri()); return getIri().equals(otherNew.getIri());
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema.entityvalues; package org.openrefine.wikidata.schema.entityvalues;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue; import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema.entityvalues; package org.openrefine.wikidata.schema.entityvalues;
import org.wikidata.wdtk.datamodel.helpers.ToString; import org.wikidata.wdtk.datamodel.helpers.ToString;
@ -16,6 +39,6 @@ public class SuggestedPropertyIdValue extends SuggestedEntityIdValue implements
@Override @Override
public String toString() { public String toString() {
return "suggested "+ToString.toString(this)+" (\""+getLabel()+"\")"; return "suggested " + ToString.toString(this) + " (\"" + getLabel() + "\")";
} }
} }

View File

@ -1,6 +1,29 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema.exceptions; package org.openrefine.wikidata.schema.exceptions;
public class InvalidSchemaException extends Exception { public class InvalidSchemaException extends Exception {
static final long serialVersionUID = 494837587034L; static final long serialVersionUID = 494837587034L;
} }

View File

@ -1,6 +1,29 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.schema.exceptions; package org.openrefine.wikidata.schema.exceptions;
public class SkipSchemaExpressionException extends Exception { public class SkipSchemaExpressionException extends Exception {
static final long serialVersionUID = 738592057L; static final long serialVersionUID = 738592057L;
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.updates; package org.openrefine.wikidata.updates;
import java.util.ArrayList; import java.util.ArrayList;
@ -23,42 +46,43 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* A class to plan an update of an item, after evaluating the statements * A class to plan an update of an item, after evaluating the statements but
* but before fetching the current content of the item (this is why it does not * before fetching the current content of the item (this is why it does not
* extend StatementsUpdate). * extend StatementsUpdate).
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
*/ */
public class ItemUpdate { public class ItemUpdate {
private final ItemIdValue qid; private final ItemIdValue qid;
private final List<Statement> addedStatements; private final List<Statement> addedStatements;
private final Set<Statement> deletedStatements; private final Set<Statement> deletedStatements;
private final Set<MonolingualTextValue> labels; private final Set<MonolingualTextValue> labels;
private final Set<MonolingualTextValue> descriptions; private final Set<MonolingualTextValue> descriptions;
private final Set<MonolingualTextValue> aliases; private final Set<MonolingualTextValue> aliases;
/** /**
* Constructor. * Constructor.
* *
* @param qid * @param qid
* the subject of the document. It can be a reconciled item value for new items. * the subject of the document. It can be a reconciled item value for
* new items.
* @param addedStatements * @param addedStatements
* the statements to add on the item. They should be distinct. They * the statements to add on the item. They should be distinct. They
* are modelled as a list because their insertion order matters. * are modelled as a list because their insertion order matters.
* @param deletedStatements * @param deletedStatements
* the statements to remove from the item * the statements to remove from the item
* @param labels * @param labels
* the labels to add on the item * the labels to add on the item
* @param descriptions * @param descriptions
* the descriptions to add on the item * the descriptions to add on the item
* @param aliases * @param aliases
* the aliases to add on the item. In theory their order should matter * the aliases to add on the item. In theory their order should
* but in practice people rarely rely on the order of aliases so this * matter but in practice people rarely rely on the order of aliases
* is just kept as a set for simplicity. * so this is just kept as a set for simplicity.
*/ */
@JsonCreator @JsonCreator
public ItemUpdate( public ItemUpdate(@JsonProperty("subject") ItemIdValue qid,
@JsonProperty("subject") ItemIdValue qid,
@JsonProperty("addedStatements") List<Statement> addedStatements, @JsonProperty("addedStatements") List<Statement> addedStatements,
@JsonProperty("deletedStatements") Set<Statement> deletedStatements, @JsonProperty("deletedStatements") Set<Statement> deletedStatements,
@JsonProperty("labels") Set<MonolingualTextValue> labels, @JsonProperty("labels") Set<MonolingualTextValue> labels,
@ -66,28 +90,28 @@ public class ItemUpdate {
@JsonProperty("addedAliases") Set<MonolingualTextValue> aliases) { @JsonProperty("addedAliases") Set<MonolingualTextValue> aliases) {
Validate.notNull(qid); Validate.notNull(qid);
this.qid = qid; this.qid = qid;
if(addedStatements == null) { if (addedStatements == null) {
addedStatements = Collections.emptyList(); addedStatements = Collections.emptyList();
} }
this.addedStatements = addedStatements; this.addedStatements = addedStatements;
if(deletedStatements == null) { if (deletedStatements == null) {
deletedStatements = Collections.emptySet(); deletedStatements = Collections.emptySet();
} }
this.deletedStatements = deletedStatements; this.deletedStatements = deletedStatements;
if(labels == null) { if (labels == null) {
labels = Collections.emptySet(); labels = Collections.emptySet();
} }
this.labels = labels; this.labels = labels;
if(descriptions == null) { if (descriptions == null) {
descriptions = Collections.emptySet(); descriptions = Collections.emptySet();
} }
this.descriptions = descriptions; this.descriptions = descriptions;
if(aliases == null) { if (aliases == null) {
aliases = Collections.emptySet(); aliases = Collections.emptySet();
} }
this.aliases = aliases; this.aliases = aliases;
} }
/** /**
* @return the subject of the item * @return the subject of the item
*/ */
@ -95,10 +119,10 @@ public class ItemUpdate {
public ItemIdValue getItemId() { public ItemIdValue getItemId() {
return qid; return qid;
} }
/** /**
* Added statements are recorded as a list because * Added statements are recorded as a list because their order of insertion
* their order of insertion matters. * matters.
* *
* @return the list of all added statements * @return the list of all added statements
*/ */
@ -106,7 +130,7 @@ public class ItemUpdate {
public List<Statement> getAddedStatements() { public List<Statement> getAddedStatements() {
return addedStatements; return addedStatements;
} }
/** /**
* @return the list of all deleted statements * @return the list of all deleted statements
*/ */
@ -114,7 +138,7 @@ public class ItemUpdate {
public Set<Statement> getDeletedStatements() { public Set<Statement> getDeletedStatements() {
return deletedStatements; return deletedStatements;
} }
/** /**
* @return the list of updated labels * @return the list of updated labels
*/ */
@ -122,7 +146,7 @@ public class ItemUpdate {
public Set<MonolingualTextValue> getLabels() { public Set<MonolingualTextValue> getLabels() {
return labels; return labels;
} }
/** /**
* @return the list of updated descriptions * @return the list of updated descriptions
*/ */
@ -130,7 +154,7 @@ public class ItemUpdate {
public Set<MonolingualTextValue> getDescriptions() { public Set<MonolingualTextValue> getDescriptions() {
return descriptions; return descriptions;
} }
/** /**
* @return the list of updated aliases * @return the list of updated aliases
*/ */
@ -138,7 +162,7 @@ public class ItemUpdate {
public Set<MonolingualTextValue> getAliases() { public Set<MonolingualTextValue> getAliases() {
return aliases; return aliases;
} }
/** /**
* @return true when this change is empty and its subject is not new * @return true when this change is empty and its subject is not new
*/ */
@ -146,30 +170,27 @@ public class ItemUpdate {
public boolean isNull() { public boolean isNull() {
return isEmpty() && !isNew(); return isEmpty() && !isNew();
} }
/** /**
* @return true when this change leaves the content of the document untouched * @return true when this change leaves the content of the document untouched
*/ */
@JsonIgnore @JsonIgnore
public boolean isEmpty() { public boolean isEmpty() {
return (addedStatements.isEmpty() return (addedStatements.isEmpty() && deletedStatements.isEmpty() && labels.isEmpty() && descriptions.isEmpty()
&& deletedStatements.isEmpty()
&& labels.isEmpty()
&& descriptions.isEmpty()
&& aliases.isEmpty()); && aliases.isEmpty());
} }
/** /**
* Merges all the changes in other into this instance. * Merges all the changes in other into this instance. Both updates should have
* Both updates should have the same subject. * the same subject.
* *
* @param other * @param other
* the other change that should be merged * the other change that should be merged
*/ */
public ItemUpdate merge(ItemUpdate other) { public ItemUpdate merge(ItemUpdate other) {
Validate.isTrue(qid.equals(other.getItemId())); Validate.isTrue(qid.equals(other.getItemId()));
List<Statement> newAddedStatements = new ArrayList<>(addedStatements); List<Statement> newAddedStatements = new ArrayList<>(addedStatements);
for(Statement statement : other.getAddedStatements()) { for (Statement statement : other.getAddedStatements()) {
if (!newAddedStatements.contains(statement)) { if (!newAddedStatements.contains(statement)) {
newAddedStatements.add(statement); newAddedStatements.add(statement);
} }
@ -182,20 +203,17 @@ public class ItemUpdate {
newDescriptions.addAll(other.getDescriptions()); newDescriptions.addAll(other.getDescriptions());
Set<MonolingualTextValue> newAliases = new HashSet<>(aliases); Set<MonolingualTextValue> newAliases = new HashSet<>(aliases);
newAliases.addAll(other.getAliases()); newAliases.addAll(other.getAliases());
return new ItemUpdate( return new ItemUpdate(qid, newAddedStatements, newDeletedStatements, newLabels, newDescriptions, newAliases);
qid, newAddedStatements, newDeletedStatements,
newLabels, newDescriptions, newAliases);
} }
/** /**
* Group added statements in StatementGroups: useful if the * Group added statements in StatementGroups: useful if the item is new.
* item is new.
* *
* @return a grouped version of getAddedStatements() * @return a grouped version of getAddedStatements()
*/ */
public List<StatementGroup> getAddedStatementGroups() { public List<StatementGroup> getAddedStatementGroups() {
Map<PropertyIdValue, List<Statement>> map = new HashMap<>(); Map<PropertyIdValue, List<Statement>> map = new HashMap<>();
for(Statement statement : getAddedStatements()) { for (Statement statement : getAddedStatements()) {
PropertyIdValue propertyId = statement.getClaim().getMainSnak().getPropertyId(); PropertyIdValue propertyId = statement.getClaim().getMainSnak().getPropertyId();
if (!map.containsKey(propertyId)) { if (!map.containsKey(propertyId)) {
map.put(propertyId, new ArrayList<Statement>()); map.put(propertyId, new ArrayList<Statement>());
@ -203,26 +221,26 @@ public class ItemUpdate {
map.get(propertyId).add(statement); map.get(propertyId).add(statement);
} }
List<StatementGroup> result = new ArrayList<>(); List<StatementGroup> result = new ArrayList<>();
for(Map.Entry<PropertyIdValue, List<Statement>> entry : map.entrySet()) { for (Map.Entry<PropertyIdValue, List<Statement>> entry : map.entrySet()) {
result.add(new StatementGroupImpl(entry.getValue())); result.add(new StatementGroupImpl(entry.getValue()));
} }
return result; return result;
} }
/** /**
* Group a list of ItemUpdates by subject: this is useful to make one single edit * Group a list of ItemUpdates by subject: this is useful to make one single
* per item. * edit per item.
* *
* @param itemDocuments * @param itemDocuments
* @return a map from item ids to merged ItemUpdate for that id * @return a map from item ids to merged ItemUpdate for that id
*/ */
public static Map<EntityIdValue, ItemUpdate> groupBySubject(List<ItemUpdate> itemDocuments) { public static Map<EntityIdValue, ItemUpdate> groupBySubject(List<ItemUpdate> itemDocuments) {
Map<EntityIdValue, ItemUpdate> map = new HashMap<>(); Map<EntityIdValue, ItemUpdate> map = new HashMap<>();
for(ItemUpdate update : itemDocuments) { for (ItemUpdate update : itemDocuments) {
if (update.isNull()) { if (update.isNull()) {
continue; continue;
} }
ItemIdValue qid = update.getItemId(); ItemIdValue qid = update.getItemId();
if (map.containsKey(qid)) { if (map.containsKey(qid)) {
ItemUpdate oldUpdate = map.get(qid); ItemUpdate oldUpdate = map.get(qid);
@ -233,59 +251,53 @@ public class ItemUpdate {
} }
return map; return map;
} }
/** /**
* Is this update about a new item? * Is this update about a new item?
*/ */
public boolean isNew() { public boolean isNew() {
return EntityIdValue.SITE_LOCAL.equals(getItemId().getSiteIri()); return EntityIdValue.SITE_LOCAL.equals(getItemId().getSiteIri());
} }
/** /**
* This should only be used when creating a new item. * This should only be used when creating a new item. This ensures that we never
* This ensures that we never add an alias without adding * add an alias without adding a label in the same language.
* a label in the same language.
*/ */
public ItemUpdate normalizeLabelsAndAliases() { public ItemUpdate normalizeLabelsAndAliases() {
// Ensure that we are only adding aliases with labels // Ensure that we are only adding aliases with labels
Set<String> labelLanguages = labels.stream() Set<String> labelLanguages = labels.stream().map(l -> l.getLanguageCode()).collect(Collectors.toSet());
.map(l -> l.getLanguageCode())
.collect(Collectors.toSet());
Set<MonolingualTextValue> filteredAliases = new HashSet<>(); Set<MonolingualTextValue> filteredAliases = new HashSet<>();
Set<MonolingualTextValue> newLabels = new HashSet<>(labels); Set<MonolingualTextValue> newLabels = new HashSet<>(labels);
for(MonolingualTextValue alias : aliases) { for (MonolingualTextValue alias : aliases) {
if(!labelLanguages.contains(alias.getLanguageCode())) { if (!labelLanguages.contains(alias.getLanguageCode())) {
labelLanguages.add(alias.getLanguageCode()); labelLanguages.add(alias.getLanguageCode());
newLabels.add(alias); newLabels.add(alias);
} else { } else {
filteredAliases.add(alias); filteredAliases.add(alias);
} }
} }
return new ItemUpdate(qid, addedStatements, deletedStatements, return new ItemUpdate(qid, addedStatements, deletedStatements, newLabels, descriptions, filteredAliases);
newLabels, descriptions, filteredAliases);
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if(other == null || !ItemUpdate.class.isInstance(other)) { if (other == null || !ItemUpdate.class.isInstance(other)) {
return false; return false;
} }
ItemUpdate otherUpdate = (ItemUpdate)other; ItemUpdate otherUpdate = (ItemUpdate) other;
return qid.equals(otherUpdate.getItemId())&& return qid.equals(otherUpdate.getItemId()) && addedStatements.equals(otherUpdate.getAddedStatements())
addedStatements.equals(otherUpdate.getAddedStatements()) && && deletedStatements.equals(otherUpdate.getDeletedStatements())
deletedStatements.equals(otherUpdate.getDeletedStatements()) && && labels.equals(otherUpdate.getLabels()) && descriptions.equals(otherUpdate.getDescriptions())
labels.equals(otherUpdate.getLabels()) && && aliases.equals(otherUpdate.getAliases());
descriptions.equals(otherUpdate.getDescriptions()) &&
aliases.equals(otherUpdate.getAliases());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return qid.hashCode() + addedStatements.hashCode() + deletedStatements.hashCode() + return qid.hashCode() + addedStatements.hashCode() + deletedStatements.hashCode() + labels.hashCode()
labels.hashCode() + descriptions.hashCode() + aliases.hashCode(); + descriptions.hashCode() + aliases.hashCode();
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
@ -317,5 +329,5 @@ public class ItemUpdate {
builder.append("\n>"); builder.append("\n>");
return builder.toString(); return builder.toString();
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.updates; package org.openrefine.wikidata.updates;
import java.util.Set; import java.util.Set;
@ -10,7 +33,6 @@ import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
import org.wikidata.wdtk.datamodel.interfaces.MonolingualTextValue; import org.wikidata.wdtk.datamodel.interfaces.MonolingualTextValue;
import org.wikidata.wdtk.datamodel.interfaces.Statement; import org.wikidata.wdtk.datamodel.interfaces.Statement;
/** /**
* Constructs a {@link ItemUpdate} incrementally. * Constructs a {@link ItemUpdate} incrementally.
* *
@ -18,6 +40,7 @@ import org.wikidata.wdtk.datamodel.interfaces.Statement;
* *
*/ */
public class ItemUpdateBuilder { public class ItemUpdateBuilder {
private ItemIdValue qid; private ItemIdValue qid;
private List<Statement> addedStatements; private List<Statement> addedStatements;
private Set<Statement> deletedStatements; private Set<Statement> deletedStatements;
@ -25,12 +48,13 @@ public class ItemUpdateBuilder {
private Set<MonolingualTextValue> descriptions; private Set<MonolingualTextValue> descriptions;
private Set<MonolingualTextValue> aliases; private Set<MonolingualTextValue> aliases;
private boolean built; private boolean built;
/** /**
* Constructor. * Constructor.
* *
* @param qid * @param qid
* the subject of the document. It can be a reconciled item value for new items. * the subject of the document. It can be a reconciled item value for
* new items.
*/ */
public ItemUpdateBuilder(ItemIdValue qid) { public ItemUpdateBuilder(ItemIdValue qid) {
Validate.notNull(qid); Validate.notNull(qid);
@ -42,50 +66,50 @@ public class ItemUpdateBuilder {
this.aliases = new HashSet<MonolingualTextValue>(); this.aliases = new HashSet<MonolingualTextValue>();
this.built = false; this.built = false;
} }
/** /**
* Mark a statement for insertion. If it matches an existing * Mark a statement for insertion. If it matches an existing statement, it will
* statement, it will update the statement instead. * update the statement instead.
* *
* @param statement * @param statement
* the statement to add or update * the statement to add or update
*/ */
public ItemUpdateBuilder addStatement(Statement statement) { public ItemUpdateBuilder addStatement(Statement statement) {
Validate.isTrue(!built, "ItemUpdate has already been built"); Validate.isTrue(!built, "ItemUpdate has already been built");
addedStatements.add(statement); addedStatements.add(statement);
return this; return this;
} }
/** /**
* Mark a statement for deletion. If no such statement exists, * Mark a statement for deletion. If no such statement exists, nothing will be
* nothing will be deleted. * deleted.
* *
* @param statement * @param statement
* the statement to delete * the statement to delete
*/ */
public ItemUpdateBuilder deleteStatement(Statement statement) { public ItemUpdateBuilder deleteStatement(Statement statement) {
Validate.isTrue(!built, "ItemUpdate has already been built"); Validate.isTrue(!built, "ItemUpdate has already been built");
deletedStatements.add(statement); deletedStatements.add(statement);
return this; return this;
} }
/** /**
* Add a list of statement, as in {@link addStatement}. * Add a list of statement, as in {@link addStatement}.
* *
* @param statements * @param statements
* the statements to add * the statements to add
*/ */
public ItemUpdateBuilder addStatements(Set<Statement> statements) { public ItemUpdateBuilder addStatements(Set<Statement> statements) {
Validate.isTrue(!built, "ItemUpdate has already been built"); Validate.isTrue(!built, "ItemUpdate has already been built");
addedStatements.addAll(statements); addedStatements.addAll(statements);
return this; return this;
} }
/** /**
* Delete a list of statements, as in {@link deleteStatement}. * Delete a list of statements, as in {@link deleteStatement}.
* *
* @param statements * @param statements
* the statements to delete * the statements to delete
*/ */
public ItemUpdateBuilder deleteStatements(Set<Statement> statements) { public ItemUpdateBuilder deleteStatements(Set<Statement> statements) {
Validate.isTrue(!built, "ItemUpdate has already been built"); Validate.isTrue(!built, "ItemUpdate has already been built");
@ -94,24 +118,24 @@ public class ItemUpdateBuilder {
} }
/** /**
* Adds a label to the item. It will override any * Adds a label to the item. It will override any existing label in this
* existing label in this language. * language.
* *
* @param label * @param label
* the label to add * the label to add
*/ */
public ItemUpdateBuilder addLabel(MonolingualTextValue label) { public ItemUpdateBuilder addLabel(MonolingualTextValue label) {
Validate.isTrue(!built, "ItemUpdate has already been built"); Validate.isTrue(!built, "ItemUpdate has already been built");
labels.add(label); labels.add(label);
return this; return this;
} }
/** /**
* Adds a list of labels to the item. It will override any * Adds a list of labels to the item. It will override any existing label in
* existing label in each language. * each language.
* *
* @param labels * @param labels
* the labels to add * the labels to add
*/ */
public ItemUpdateBuilder addLabels(Set<MonolingualTextValue> labels) { public ItemUpdateBuilder addLabels(Set<MonolingualTextValue> labels) {
Validate.isTrue(!built, "ItemUpdate has already been built"); Validate.isTrue(!built, "ItemUpdate has already been built");
@ -120,24 +144,24 @@ public class ItemUpdateBuilder {
} }
/** /**
* Adds a description to the item. It will override any existing * Adds a description to the item. It will override any existing description in
* description in this language. * this language.
* *
* @param description * @param description
* the description to add * the description to add
*/ */
public ItemUpdateBuilder addDescription(MonolingualTextValue description) { public ItemUpdateBuilder addDescription(MonolingualTextValue description) {
Validate.isTrue(!built, "ItemUpdate has already been built"); Validate.isTrue(!built, "ItemUpdate has already been built");
descriptions.add(description); descriptions.add(description);
return this; return this;
} }
/** /**
* Adds a list of descriptions to the item. It will override any * Adds a list of descriptions to the item. It will override any existing
* existing description in each language. * description in each language.
* *
* @param descriptions * @param descriptions
* the descriptions to add * the descriptions to add
*/ */
public ItemUpdateBuilder addDescriptions(Set<MonolingualTextValue> descriptions) { public ItemUpdateBuilder addDescriptions(Set<MonolingualTextValue> descriptions) {
Validate.isTrue(!built, "ItemUpdate has already been built"); Validate.isTrue(!built, "ItemUpdate has already been built");
@ -146,39 +170,39 @@ public class ItemUpdateBuilder {
} }
/** /**
* Adds an alias to the item. It will be added to any existing * Adds an alias to the item. It will be added to any existing aliases in that
* aliases in that language. * language.
* *
* @param alias * @param alias
* the alias to add * the alias to add
*/ */
public ItemUpdateBuilder addAlias(MonolingualTextValue alias) { public ItemUpdateBuilder addAlias(MonolingualTextValue alias) {
Validate.isTrue(!built, "ItemUpdate has already been built"); Validate.isTrue(!built, "ItemUpdate has already been built");
aliases.add(alias); aliases.add(alias);
return this; return this;
} }
/** /**
* Adds a list of aliases to the item. They will be added to any * Adds a list of aliases to the item. They will be added to any existing
* existing aliases in each language. * aliases in each language.
* *
* @param aliases * @param aliases
* the aliases to add * the aliases to add
*/ */
public ItemUpdateBuilder addAliases(Set<MonolingualTextValue> aliases) { public ItemUpdateBuilder addAliases(Set<MonolingualTextValue> aliases) {
Validate.isTrue(!built, "ItemUpdate has already been built"); Validate.isTrue(!built, "ItemUpdate has already been built");
this.aliases.addAll(aliases); this.aliases.addAll(aliases);
return this; return this;
} }
/** /**
* Constructs the {@link ItemUpdate}. * Constructs the {@link ItemUpdate}.
*
* @return * @return
*/ */
public ItemUpdate build() { public ItemUpdate build() {
built = true; built = true;
return new ItemUpdate(qid, addedStatements, deletedStatements, return new ItemUpdate(qid, addedStatements, deletedStatements, labels, descriptions, aliases);
labels, descriptions, aliases);
} }
} }

View File

@ -1,6 +1,28 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.updates.scheduler; package org.openrefine.wikidata.updates.scheduler;
public class ImpossibleSchedulingException extends Exception { public class ImpossibleSchedulingException extends Exception {
private static final long serialVersionUID = 6621563898380564148L; private static final long serialVersionUID = 6621563898380564148L;

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.updates.scheduler; package org.openrefine.wikidata.updates.scheduler;
import java.util.Collections; import java.util.Collections;
@ -5,7 +28,6 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.openrefine.wikidata.schema.entityvalues.ReconEntityIdValue;
import org.openrefine.wikidata.schema.entityvalues.ReconItemIdValue; import org.openrefine.wikidata.schema.entityvalues.ReconItemIdValue;
import org.wikidata.wdtk.datamodel.interfaces.DatatypeIdValue; import org.wikidata.wdtk.datamodel.interfaces.DatatypeIdValue;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
@ -21,33 +43,29 @@ import org.wikidata.wdtk.datamodel.interfaces.Value;
import org.wikidata.wdtk.datamodel.interfaces.ValueVisitor; import org.wikidata.wdtk.datamodel.interfaces.ValueVisitor;
/** /**
* A class that extracts the new entity ids referred to * A class that extracts the new entity ids referred to in a statement.
* in a statement.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public class PointerExtractor implements ValueVisitor<Set<ReconItemIdValue>> { public class PointerExtractor implements ValueVisitor<Set<ReconItemIdValue>> {
/** /**
* Extracts all the new entities mentioned by this statement. This * Extracts all the new entities mentioned by this statement. This does not
* does not include the subject of the statement. * include the subject of the statement.
* *
* @param statement * @param statement
* the statement to inspect * the statement to inspect
* @return * @return the set of all new entities mentioned by the statement
* the set of all new entities mentioned by the statement
*/ */
public Set<ReconItemIdValue> extractPointers(Statement statement) { public Set<ReconItemIdValue> extractPointers(Statement statement) {
Set<ReconItemIdValue> result = new HashSet<>(); Set<ReconItemIdValue> result = new HashSet<>();
result.addAll(extractPointers(statement.getClaim().getMainSnak())); result.addAll(extractPointers(statement.getClaim().getMainSnak()));
result.addAll(extractPointers(statement.getClaim().getQualifiers())); result.addAll(extractPointers(statement.getClaim().getQualifiers()));
statement.getReferences().stream() statement.getReferences().stream().map(l -> extractPointers(l.getSnakGroups())).forEach(s -> result.addAll(s));
.map(l -> extractPointers(l.getSnakGroups()))
.forEach(s -> result.addAll(s));
return result; return result;
} }
/** /**
* Extracts all the new entities mentioned by this list of snak groups. * Extracts all the new entities mentioned by this list of snak groups.
* *
@ -56,12 +74,10 @@ public class PointerExtractor implements ValueVisitor<Set<ReconItemIdValue>> {
*/ */
public Set<ReconItemIdValue> extractPointers(List<SnakGroup> snakGroups) { public Set<ReconItemIdValue> extractPointers(List<SnakGroup> snakGroups) {
Set<ReconItemIdValue> result = new HashSet<>(); Set<ReconItemIdValue> result = new HashSet<>();
snakGroups.stream() snakGroups.stream().map(s -> extractPointers(s)).forEach(s -> result.addAll(s));
.map(s -> extractPointers(s)) return result;
.forEach(s -> result.addAll(s));
return result;
} }
/*** /***
* Extracts all the new entities mentioned by this snak group. * Extracts all the new entities mentioned by this snak group.
* *
@ -70,16 +86,14 @@ public class PointerExtractor implements ValueVisitor<Set<ReconItemIdValue>> {
*/ */
public Set<ReconItemIdValue> extractPointers(SnakGroup snakGroup) { public Set<ReconItemIdValue> extractPointers(SnakGroup snakGroup) {
Set<ReconItemIdValue> result = new HashSet<>(); Set<ReconItemIdValue> result = new HashSet<>();
snakGroup.getSnaks().stream() snakGroup.getSnaks().stream().map(s -> extractPointers(s)).forEach(s -> result.addAll(s));
.map(s -> extractPointers(s)) return result;
.forEach(s -> result.addAll(s));
return result;
} }
/** /**
* Extracts all new entities mentioned by this snak group. * Extracts all new entities mentioned by this snak group. Currently there will
* Currently there will be at most one: the target of the snak * be at most one: the target of the snak (as property ids cannot be new for
* (as property ids cannot be new for now). * now).
* *
* @param snak * @param snak
* @return * @return
@ -90,7 +104,7 @@ public class PointerExtractor implements ValueVisitor<Set<ReconItemIdValue>> {
result.addAll(extractPointers(snak.getValue())); result.addAll(extractPointers(snak.getValue()));
return result; return result;
} }
/** /**
* Extracts any new entity from the value. * Extracts any new entity from the value.
* *
@ -115,9 +129,9 @@ public class PointerExtractor implements ValueVisitor<Set<ReconItemIdValue>> {
@Override @Override
public Set<ReconItemIdValue> visit(EntityIdValue value) { public Set<ReconItemIdValue> visit(EntityIdValue value) {
if(ReconItemIdValue.class.isInstance(value)) { if (ReconItemIdValue.class.isInstance(value)) {
ReconItemIdValue recon = (ReconItemIdValue)value; ReconItemIdValue recon = (ReconItemIdValue) value;
if(recon.isNew()) { if (recon.isNew()) {
return Collections.singleton(recon); return Collections.singleton(recon);
} }
} }
@ -146,7 +160,7 @@ public class PointerExtractor implements ValueVisitor<Set<ReconItemIdValue>> {
} }
@Override @Override
public Set<ReconItemIdValue> visit(TimeValue value) { public Set<ReconItemIdValue> visit(TimeValue value) {
return null; return null;
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.updates.scheduler; package org.openrefine.wikidata.updates.scheduler;
import java.util.ArrayList; import java.util.ArrayList;
@ -8,49 +31,45 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import org.openrefine.wikidata.schema.entityvalues.ReconEntityIdValue;
import org.openrefine.wikidata.schema.entityvalues.ReconItemIdValue; import org.openrefine.wikidata.schema.entityvalues.ReconItemIdValue;
import org.openrefine.wikidata.updates.ItemUpdate; import org.openrefine.wikidata.updates.ItemUpdate;
import org.openrefine.wikidata.updates.ItemUpdateBuilder; import org.openrefine.wikidata.updates.ItemUpdateBuilder;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue; import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Statement; import org.wikidata.wdtk.datamodel.interfaces.Statement;
public class QuickStatementsUpdateScheduler implements UpdateScheduler { public class QuickStatementsUpdateScheduler implements UpdateScheduler {
private PointerExtractor extractor = new PointerExtractor(); private PointerExtractor extractor = new PointerExtractor();
/** /**
* This map holds for each new entity id value a list of updates * This map holds for each new entity id value a list of updates that refer to
* that refer to this id (and should hence be scheduled right after * this id (and should hence be scheduled right after creation of that entity).
* creation of that entity).
*/ */
private Map<ItemIdValue, UpdateSequence> pointerUpdates; private Map<ItemIdValue, UpdateSequence> pointerUpdates;
/** /**
* This contains all updates which do not refer to any new entity * This contains all updates which do not refer to any new entity apart from
* apart from possibly the subject, in the order that they were supplied to us. * possibly the subject, in the order that they were supplied to us.
*/ */
private UpdateSequence pointerFreeUpdates; private UpdateSequence pointerFreeUpdates;
/** /**
* Separates out the statements which refer to new items from the rest * Separates out the statements which refer to new items from the rest of the
* of the update. The resulting updates are stored in {@link referencingUpdates} * update. The resulting updates are stored in {@link referencingUpdates} and
* and {@link updatesWithoutReferences}. * {@link updatesWithoutReferences}.
* *
* @param update * @param update
* @throws ImpossibleSchedulingException * @throws ImpossibleSchedulingException
* if two new item ids are referred to in the same statement * if two new item ids are referred to in the same statement
*/ */
protected void splitUpdate(ItemUpdate update) throws ImpossibleSchedulingException { protected void splitUpdate(ItemUpdate update)
throws ImpossibleSchedulingException {
ItemUpdateBuilder remainingUpdateBuilder = new ItemUpdateBuilder(update.getItemId()) ItemUpdateBuilder remainingUpdateBuilder = new ItemUpdateBuilder(update.getItemId())
.addLabels(update.getLabels()) .addLabels(update.getLabels()).addDescriptions(update.getDescriptions()).addAliases(update.getAliases())
.addDescriptions(update.getDescriptions())
.addAliases(update.getAliases())
.deleteStatements(update.getDeletedStatements()); .deleteStatements(update.getDeletedStatements());
Map<ItemIdValue, ItemUpdateBuilder> referencingUpdates = new HashMap<>(); Map<ItemIdValue, ItemUpdateBuilder> referencingUpdates = new HashMap<>();
for(Statement statement : update.getAddedStatements()) { for (Statement statement : update.getAddedStatements()) {
Set<ReconItemIdValue> pointers = extractor.extractPointers(statement); Set<ReconItemIdValue> pointers = extractor.extractPointers(statement);
if (pointers.isEmpty()) { if (pointers.isEmpty()) {
remainingUpdateBuilder.addStatement(statement); remainingUpdateBuilder.addStatement(statement);
@ -68,14 +87,14 @@ public class QuickStatementsUpdateScheduler implements UpdateScheduler {
throw new ImpossibleSchedulingException(); throw new ImpossibleSchedulingException();
} }
} }
// Add the update that is not referring to anything to the schedule // Add the update that is not referring to anything to the schedule
ItemUpdate pointerFree = remainingUpdateBuilder.build(); ItemUpdate pointerFree = remainingUpdateBuilder.build();
if (!pointerFree.isNull()) { if (!pointerFree.isNull()) {
pointerFreeUpdates.add(pointerFree); pointerFreeUpdates.add(pointerFree);
} }
// Add the other updates to the map // Add the other updates to the map
for(Entry<ItemIdValue, ItemUpdateBuilder> entry : referencingUpdates.entrySet()) { for (Entry<ItemIdValue, ItemUpdateBuilder> entry : referencingUpdates.entrySet()) {
ItemUpdate pointerUpdate = entry.getValue().build(); ItemUpdate pointerUpdate = entry.getValue().build();
UpdateSequence pointerUpdatesForKey = pointerUpdates.get(entry.getKey()); UpdateSequence pointerUpdatesForKey = pointerUpdates.get(entry.getKey());
if (pointerUpdatesForKey == null) { if (pointerUpdatesForKey == null) {
@ -87,18 +106,19 @@ public class QuickStatementsUpdateScheduler implements UpdateScheduler {
} }
@Override @Override
public List<ItemUpdate> schedule(List<ItemUpdate> updates) throws ImpossibleSchedulingException { public List<ItemUpdate> schedule(List<ItemUpdate> updates)
throws ImpossibleSchedulingException {
pointerUpdates = new HashMap<>(); pointerUpdates = new HashMap<>();
pointerFreeUpdates = new UpdateSequence(); pointerFreeUpdates = new UpdateSequence();
for(ItemUpdate update : updates) { for (ItemUpdate update : updates) {
splitUpdate(update); splitUpdate(update);
} }
// Reconstruct // Reconstruct
List<ItemUpdate> fullSchedule = new ArrayList<>(); List<ItemUpdate> fullSchedule = new ArrayList<>();
Set<ItemIdValue> mentionedNewEntities = new HashSet<>(pointerUpdates.keySet()); Set<ItemIdValue> mentionedNewEntities = new HashSet<>(pointerUpdates.keySet());
for(ItemUpdate update : pointerFreeUpdates.getUpdates()) { for (ItemUpdate update : pointerFreeUpdates.getUpdates()) {
fullSchedule.add(update); fullSchedule.add(update);
UpdateSequence backPointers = pointerUpdates.get(update.getItemId()); UpdateSequence backPointers = pointerUpdates.get(update.getItemId());
if (backPointers != null) { if (backPointers != null) {
@ -106,11 +126,11 @@ public class QuickStatementsUpdateScheduler implements UpdateScheduler {
} }
mentionedNewEntities.remove(update.getItemId()); mentionedNewEntities.remove(update.getItemId());
} }
// Create any item that was referred to but untouched // Create any item that was referred to but untouched
// (this is just for the sake of correctness, it would be bad to do that // (this is just for the sake of correctness, it would be bad to do that
// as the items would remain blank in this batch). // as the items would remain blank in this batch).
for(ItemIdValue missingId : mentionedNewEntities) { for (ItemIdValue missingId : mentionedNewEntities) {
fullSchedule.add(new ItemUpdateBuilder(missingId).build()); fullSchedule.add(new ItemUpdateBuilder(missingId).build());
fullSchedule.addAll(pointerUpdates.get(missingId).getUpdates()); fullSchedule.addAll(pointerUpdates.get(missingId).getUpdates());
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.updates.scheduler; package org.openrefine.wikidata.updates.scheduler;
import java.util.List; import java.util.List;
@ -5,28 +28,26 @@ import java.util.List;
import org.openrefine.wikidata.updates.ItemUpdate; import org.openrefine.wikidata.updates.ItemUpdate;
/** /**
* A scheduling strategy for item updates. * A scheduling strategy for item updates. Given a list of initial updates, the
* Given a list of initial updates, the scheduler * scheduler reorganizes these updates (possibly splitting them or merging them)
* reorganizes these updates (possibly splitting them * to create a sequence that is suitable for a particular import process.
* or merging them) to create a sequence that is suitable
* for a particular import process.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public interface UpdateScheduler { public interface UpdateScheduler {
/** /**
* Performs the scheduling. The initial updates are provided * Performs the scheduling. The initial updates are provided as a list so that
* as a list so that the scheduler can attempt to respect the * the scheduler can attempt to respect the initial order (but no guarantee is
* initial order (but no guarantee is made for that in general). * made for that in general).
* *
* @param updates * @param updates
* the updates to schedule * the updates to schedule
* @return * @return the reorganized updates
* the reorganized updates * @throws ImpossibleSchedulingException
* @throws ImpossibleSchedulingException * when the scheduler cannot cope with a particular edit plan.
* when the scheduler cannot cope with a particular edit plan.
*/ */
public List<ItemUpdate> schedule(List<ItemUpdate> updates) throws ImpossibleSchedulingException; public List<ItemUpdate> schedule(List<ItemUpdate> updates)
throws ImpossibleSchedulingException;
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.updates.scheduler; package org.openrefine.wikidata.updates.scheduler;
import java.util.ArrayList; import java.util.ArrayList;
@ -10,12 +33,13 @@ import org.openrefine.wikidata.updates.ItemUpdate;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue; import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
/** /**
* Helper class to store a list of updates where each subject * Helper class to store a list of updates where each subject appears at most
* appears at most once. It preserves order of insertion. * once. It preserves order of insertion.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
*/ */
public class UpdateSequence { public class UpdateSequence {
/** /**
* The list of updates stored by this container * The list of updates stored by this container
*/ */
@ -24,16 +48,16 @@ public class UpdateSequence {
* An index to keep track of where each item is touched in the sequence * An index to keep track of where each item is touched in the sequence
*/ */
private Map<ItemIdValue, Integer> index = new HashMap<>(); private Map<ItemIdValue, Integer> index = new HashMap<>();
/** /**
* Adds a new update to the list, merging it with any existing * Adds a new update to the list, merging it with any existing one with the same
* one with the same subject. * subject.
* *
* @param update * @param update
*/ */
public void add(ItemUpdate update) { public void add(ItemUpdate update) {
ItemIdValue subject = update.getItemId(); ItemIdValue subject = update.getItemId();
if(index.containsKey(subject)) { if (index.containsKey(subject)) {
int i = index.get(subject); int i = index.get(subject);
ItemUpdate oldUpdate = updates.get(i); ItemUpdate oldUpdate = updates.get(i);
updates.set(i, oldUpdate.merge(update)); updates.set(i, oldUpdate.merge(update));
@ -42,18 +66,18 @@ public class UpdateSequence {
updates.add(update); updates.add(update);
} }
} }
/** /**
* @return the list of merged updates * @return the list of merged updates
*/ */
public List<ItemUpdate> getUpdates() { public List<ItemUpdate> getUpdates() {
return updates; return updates;
} }
/** /**
* @return the set of touched subjects * @return the set of touched subjects
*/ */
public Set<ItemIdValue> getSubjects() { public Set<ItemIdValue> getSubjects() {
return index.keySet(); return index.keySet();
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.updates.scheduler; package org.openrefine.wikidata.updates.scheduler;
import java.util.ArrayList; import java.util.ArrayList;
@ -15,33 +38,32 @@ import org.wikidata.wdtk.datamodel.interfaces.Statement;
/** /**
* A simple scheduler for batches commited via the Wikibase API. * A simple scheduler for batches commited via the Wikibase API.
* *
* The strategy is quite simple and makes at most two edits * The strategy is quite simple and makes at most two edits per touched item
* per touched item (which is not minimal though). Each update * (which is not minimal though). Each update is split between statements making
* is split between statements making references to new items, * references to new items, and statements not making these references. All
* and statements not making these references. All updates with no * updates with no references to new items are done first (which creates all new
* references to new items are done first (which creates all new
* items), then all other updates are done. * items), then all other updates are done.
* *
* @author Antonin Delpeuch * @author Antonin Delpeuch
* *
*/ */
public class WikibaseAPIUpdateScheduler implements UpdateScheduler { public class WikibaseAPIUpdateScheduler implements UpdateScheduler {
/** /**
* The first part of updates: the ones which create new items * The first part of updates: the ones which create new items without referring
* without referring to any other new item. * to any other new item.
*/ */
private UpdateSequence pointerFreeUpdates; private UpdateSequence pointerFreeUpdates;
/** /**
* The second part of the updates: all existing items, plus * The second part of the updates: all existing items, plus all parts of new
* all parts of new items that refer to other new items. * items that refer to other new items.
*/ */
private UpdateSequence pointerFullUpdates; private UpdateSequence pointerFullUpdates;
/** /**
* The set of all new items referred to in the whole batch. * The set of all new items referred to in the whole batch.
*/ */
private Set<ItemIdValue> allPointers; private Set<ItemIdValue> allPointers;
private PointerExtractor extractor = new PointerExtractor(); private PointerExtractor extractor = new PointerExtractor();
@Override @Override
@ -50,22 +72,20 @@ public class WikibaseAPIUpdateScheduler implements UpdateScheduler {
pointerFreeUpdates = new UpdateSequence(); pointerFreeUpdates = new UpdateSequence();
pointerFullUpdates = new UpdateSequence(); pointerFullUpdates = new UpdateSequence();
allPointers = new HashSet<>(); allPointers = new HashSet<>();
for(ItemUpdate update : updates) { for (ItemUpdate update : updates) {
splitUpdate(update); splitUpdate(update);
} }
// Part 1: add all the pointer free updates // Part 1: add all the pointer free updates
result.addAll(pointerFreeUpdates.getUpdates()); result.addAll(pointerFreeUpdates.getUpdates());
// Part 1': add the remaining new items that have not been touched // Part 1': add the remaining new items that have not been touched
Set<ItemIdValue> unseenPointers = new HashSet<>(allPointers); Set<ItemIdValue> unseenPointers = new HashSet<>(allPointers);
unseenPointers.removeAll(pointerFreeUpdates.getSubjects()); unseenPointers.removeAll(pointerFreeUpdates.getSubjects());
result.addAll(unseenPointers.stream() result.addAll(unseenPointers.stream().map(e -> new ItemUpdateBuilder(e).build()).collect(Collectors.toList()));
.map(e -> new ItemUpdateBuilder(e).build())
.collect(Collectors.toList()));
// Part 2: add all the pointer full updates // Part 2: add all the pointer full updates
result.addAll(pointerFullUpdates.getUpdates()); result.addAll(pointerFullUpdates.getUpdates());
@ -74,17 +94,16 @@ public class WikibaseAPIUpdateScheduler implements UpdateScheduler {
/** /**
* Splits an update into two parts * Splits an update into two parts
*
* @param update * @param update
*/ */
protected void splitUpdate(ItemUpdate update) { protected void splitUpdate(ItemUpdate update) {
ItemUpdateBuilder pointerFreeBuilder = new ItemUpdateBuilder(update.getItemId()) ItemUpdateBuilder pointerFreeBuilder = new ItemUpdateBuilder(update.getItemId()).addLabels(update.getLabels())
.addLabels(update.getLabels()) .addDescriptions(update.getDescriptions()).addAliases(update.getAliases())
.addDescriptions(update.getDescriptions())
.addAliases(update.getAliases())
.deleteStatements(update.getDeletedStatements()); .deleteStatements(update.getDeletedStatements());
ItemUpdateBuilder pointerFullBuilder = new ItemUpdateBuilder(update.getItemId()); ItemUpdateBuilder pointerFullBuilder = new ItemUpdateBuilder(update.getItemId());
for(Statement statement : update.getAddedStatements()) { for (Statement statement : update.getAddedStatements()) {
Set<ReconItemIdValue> pointers = extractor.extractPointers(statement); Set<ReconItemIdValue> pointers = extractor.extractPointers(statement);
if (pointers.isEmpty()) { if (pointers.isEmpty()) {
pointerFreeBuilder.addStatement(statement); pointerFreeBuilder.addStatement(statement);
@ -93,8 +112,8 @@ public class WikibaseAPIUpdateScheduler implements UpdateScheduler {
} }
allPointers.addAll(pointers); allPointers.addAll(pointers);
} }
if(update.isNew()) { if (update.isNew()) {
// If the update is new, we might need to split it // If the update is new, we might need to split it
// in two (if it refers to any other new entity). // in two (if it refers to any other new entity).
ItemUpdate pointerFree = pointerFreeBuilder.build(); ItemUpdate pointerFree = pointerFreeBuilder.build();
@ -111,5 +130,5 @@ public class WikibaseAPIUpdateScheduler implements UpdateScheduler {
pointerFullUpdates.add(update); pointerFullUpdates.add(update);
} }
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.utils; package org.openrefine.wikidata.utils;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -14,42 +37,42 @@ import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
public class EntityCache { public class EntityCache {
private static EntityCache _entityCache = new EntityCache(); private static EntityCache _entityCache = new EntityCache();
private LoadingCache<String, EntityDocument> _cache = null; private LoadingCache<String, EntityDocument> _cache = null;
private WikibaseDataFetcher _fetcher; private WikibaseDataFetcher _fetcher;
private EntityCache() { private EntityCache() {
ApiConnection connection = ApiConnection.getWikidataApiConnection(); ApiConnection connection = ApiConnection.getWikidataApiConnection();
_fetcher = new WikibaseDataFetcher(connection, Datamodel.SITE_WIKIDATA); _fetcher = new WikibaseDataFetcher(connection, Datamodel.SITE_WIKIDATA);
_cache = CacheBuilder.newBuilder() _cache = CacheBuilder.newBuilder().maximumSize(4096).expireAfterWrite(1, TimeUnit.HOURS)
.maximumSize(4096) .build(new CacheLoader<String, EntityDocument>() {
.expireAfterWrite(1, TimeUnit.HOURS)
.build( public EntityDocument load(String entityId)
new CacheLoader<String, EntityDocument>() { throws Exception {
public EntityDocument load(String entityId) throws Exception { EntityDocument doc = _fetcher.getEntityDocument(entityId);
EntityDocument doc = _fetcher.getEntityDocument(entityId); if (doc != null) {
if (doc != null) { return doc;
return doc; } else {
} else { throw new MediaWikiApiErrorException("400", "Unknown entity id \"" + entityId + "\"");
throw new MediaWikiApiErrorException("400", "Unknown entity id \""+entityId+"\""); }
} }
} });
});
} }
public EntityDocument get(EntityIdValue id) { public EntityDocument get(EntityIdValue id) {
return _cache.apply(id.getId()); return _cache.apply(id.getId());
} }
public static EntityCache getEntityCache() { public static EntityCache getEntityCache() {
if (_entityCache == null) { if (_entityCache == null) {
_entityCache = new EntityCache(); _entityCache = new EntityCache();
} }
return _entityCache; return _entityCache;
} }
public static EntityDocument getEntityDocument(EntityIdValue id) { public static EntityDocument getEntityDocument(EntityIdValue id) {
return getEntityCache().get(id); return getEntityCache().get(id);
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.utils; package org.openrefine.wikidata.utils;
import java.io.IOException; import java.io.IOException;
@ -6,25 +29,27 @@ import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
public class FirstLinesExtractor { public class FirstLinesExtractor {
/** /**
* Returns the first n lines of a given string * Returns the first n lines of a given string
*
* @param content * @param content
* the content, where lines are separated by '\n' * the content, where lines are separated by '\n'
* @param nbLines * @param nbLines
* the number of lines to extract * the number of lines to extract
* @return * @return the first lines of the string
* the first lines of the string * @throws IOException
* @throws IOException
*/ */
public static String extractFirstLines(String content, int nbLines) throws IOException { public static String extractFirstLines(String content, int nbLines)
throws IOException {
StringWriter stringWriter = new StringWriter(); StringWriter stringWriter = new StringWriter();
LineNumberReader reader = new LineNumberReader(new StringReader(content)); LineNumberReader reader = new LineNumberReader(new StringReader(content));
// Only keep the first 50 lines // Only keep the first 50 lines
reader.setLineNumber(0); reader.setLineNumber(0);
String line = reader.readLine(); String line = reader.readLine();
for(int i = 1; i != nbLines && line != null; i++) { for (int i = 1; i != nbLines && line != null; i++) {
stringWriter.write(line+"\n"); stringWriter.write(line + "\n");
line = reader.readLine(); line = reader.readLine();
} }
if (reader.getLineNumber() == nbLines) { if (reader.getLineNumber() == nbLines) {

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.utils; package org.openrefine.wikidata.utils;
import java.io.IOException; import java.io.IOException;
@ -16,13 +39,12 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.refine.Jsonizable; import com.google.refine.Jsonizable;
/** /**
* This class is inefficient because it serializes the * This class is inefficient because it serializes the object to string and then
* object to string and then deserializes it back. Unfortunately, * deserializes it back. Unfortunately, this is the only simple way to bridge
* this is the only simple way to bridge Jackson to org.json. * Jackson to org.json. This conversion should be removed when (if ?) we migrate
* This conversion should be removed when (if ?) we migrate OpenRefine * OpenRefine a better JSON library.
* a better JSON library.
* *
* @author antonin * @author Antonin Delpeuch
* *
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@ -38,8 +60,9 @@ public abstract class JacksonJsonizable implements Jsonizable {
throw new JSONException(e.toString()); throw new JSONException(e.toString());
} }
} }
public static <T> T fromJSONClass(JSONObject obj, Class<T> klass) throws JSONException { public static <T> T fromJSONClass(JSONObject obj, Class<T> klass)
throws JSONException {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String json = obj.toString(); String json = obj.toString();
try { try {
@ -50,7 +73,7 @@ public abstract class JacksonJsonizable implements Jsonizable {
throw new JSONException(e.toString()); throw new JSONException(e.toString());
} catch (IOException e) { } catch (IOException e) {
throw new JSONException(e.toString()); throw new JSONException(e.toString());
} }
} }
} }

View File

@ -1,57 +1,73 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.commands; package org.openrefine.wikidata.commands;
import java.io.StringWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONException;
import org.json.JSONObject;
import org.openrefine.wikidata.testing.TestingData;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.times;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONException;
import org.openrefine.wikidata.testing.TestingData;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import com.google.refine.commands.Command; import com.google.refine.commands.Command;
import com.google.refine.model.Project; import com.google.refine.model.Project;
import com.google.refine.tests.RefineTest; import com.google.refine.tests.RefineTest;
import com.google.refine.util.ParsingUtilities;
public abstract class CommandTest extends RefineTest { public abstract class CommandTest extends RefineTest {
protected Project project = null; protected Project project = null;
protected HttpServletRequest request = null; protected HttpServletRequest request = null;
protected HttpServletResponse response = null; protected HttpServletResponse response = null;
protected StringWriter writer = null; protected StringWriter writer = null;
protected Command command = null; protected Command command = null;
@BeforeMethod(alwaysRun = true) @BeforeMethod(alwaysRun = true)
public void setUpProject() throws JSONException { public void setUpProject()
throws JSONException {
project = createCSVProject(TestingData.inceptionWithNewCsv); project = createCSVProject(TestingData.inceptionWithNewCsv);
TestingData.reconcileInceptionCells(project); TestingData.reconcileInceptionCells(project);
request = mock(HttpServletRequest.class); request = mock(HttpServletRequest.class);
response = mock(HttpServletResponse.class); response = mock(HttpServletResponse.class);
writer = new StringWriter(); writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer); PrintWriter printWriter = new PrintWriter(writer);
when(request.getParameter("project")).thenReturn(String.valueOf(project.id)); when(request.getParameter("project")).thenReturn(String.valueOf(project.id));
try { try {
when(response.getWriter()).thenReturn(printWriter); when(response.getWriter()).thenReturn(printWriter);
} catch (IOException e1) { } catch (IOException e1) {
Assert.fail(); Assert.fail();
} }
} }
} }

View File

@ -1,5 +1,34 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.commands; package org.openrefine.wikidata.commands;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import static org.openrefine.wikidata.testing.TestingData.jsonFromFile;
import java.io.IOException;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import org.json.JSONException; import org.json.JSONException;
@ -8,29 +37,24 @@ import org.openrefine.wikidata.testing.TestingData;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import java.io.IOException;
import com.google.refine.util.ParsingUtilities; import com.google.refine.util.ParsingUtilities;
import static org.openrefine.wikidata.testing.TestingData.jsonFromFile;
public class PreviewWikibaseSchemaCommandTest extends SchemaCommandTest { public class PreviewWikibaseSchemaCommandTest extends SchemaCommandTest {
@BeforeMethod @BeforeMethod
public void SetUp() throws JSONException { public void SetUp()
throws JSONException {
command = new PreviewWikibaseSchemaCommand(); command = new PreviewWikibaseSchemaCommand();
} }
@Test @Test
public void testValidSchema() throws JSONException, IOException, ServletException { public void testValidSchema()
throws JSONException, IOException, ServletException {
String schemaJson = jsonFromFile("data/schema/inception.json").toString(); String schemaJson = jsonFromFile("data/schema/inception.json").toString();
when(request.getParameter("schema")).thenReturn(schemaJson); when(request.getParameter("schema")).thenReturn(schemaJson);
command.doPost(request, response); command.doPost(request, response);
JSONObject response = ParsingUtilities.evaluateJsonStringToObject(writer.toString()); JSONObject response = ParsingUtilities.evaluateJsonStringToObject(writer.toString());
assertEquals(TestingData.inceptionWithNewQS, response.getString("quickstatements")); assertEquals(TestingData.inceptionWithNewQS, response.getString("quickstatements"));
} }

View File

@ -1,31 +1,54 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.commands; package org.openrefine.wikidata.commands;
import org.testng.annotations.BeforeMethod; import static org.junit.Assert.assertTrue;
import org.testng.annotations.Test; import static org.mockito.Mockito.when;
import static org.openrefine.wikidata.testing.TestingData.jsonFromFile; import static org.openrefine.wikidata.testing.TestingData.jsonFromFile;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import static org.junit.Assert.assertTrue; import org.testng.annotations.BeforeMethod;
import static org.mockito.Mockito.when; import org.testng.annotations.Test;
public class SaveWikibaseSchemaCommandTest extends SchemaCommandTest { public class SaveWikibaseSchemaCommandTest extends SchemaCommandTest {
@BeforeMethod @BeforeMethod
public void setUp() { public void setUp() {
this.command = new SaveWikibaseSchemaCommand(); this.command = new SaveWikibaseSchemaCommand();
} }
@Test @Test
public void testValidSchema() throws ServletException, IOException { public void testValidSchema()
throws ServletException, IOException {
String schemaJson = jsonFromFile("data/schema/inception.json").toString(); String schemaJson = jsonFromFile("data/schema/inception.json").toString();
when(request.getParameter("schema")).thenReturn(schemaJson); when(request.getParameter("schema")).thenReturn(schemaJson);
command.doPost(request, response); command.doPost(request, response);
assertTrue(writer.toString().contains("\"ok\"")); assertTrue(writer.toString().contains("\"ok\""));
} }
} }

View File

@ -1,8 +1,28 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.commands; package org.openrefine.wikidata.commands;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -10,20 +30,24 @@ import java.io.IOException;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import org.testng.annotations.Test;
public abstract class SchemaCommandTest extends CommandTest { public abstract class SchemaCommandTest extends CommandTest {
@Test @Test
public void testNoSchema() throws ServletException, IOException { public void testNoSchema()
throws ServletException, IOException {
command.doPost(request, response); command.doPost(request, response);
assertEquals("{\"status\":\"error\",\"message\":\"No Wikibase schema provided.\"}", writer.toString()); assertEquals("{\"status\":\"error\",\"message\":\"No Wikibase schema provided.\"}", writer.toString());
} }
@Test @Test
public void testInvalidSchema() throws ServletException, IOException { public void testInvalidSchema()
throws ServletException, IOException {
when(request.getParameter("schema")).thenReturn("{bogus json"); when(request.getParameter("schema")).thenReturn("{bogus json");
command.doPost(request, response); command.doPost(request, response);
assertEquals("{\"status\":\"error\",\"message\":\"Wikibase schema could not be parsed.\"}", writer.toString()); assertEquals("{\"status\":\"error\",\"message\":\"Wikibase schema could not be parsed.\"}", writer.toString());
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.editing; package org.openrefine.wikidata.editing;
import org.openrefine.wikidata.testing.TestingData; import org.openrefine.wikidata.testing.TestingData;
@ -31,14 +54,13 @@ import java.util.stream.Collectors;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
public class EditBatchProcessorTest extends RefineTest { public class EditBatchProcessorTest extends RefineTest {
private WikibaseDataFetcher fetcher = null; private WikibaseDataFetcher fetcher = null;
private WikibaseDataEditor editor = null; private WikibaseDataEditor editor = null;
private NewItemLibrary library = null; private NewItemLibrary library = null;
private String summary = "my fantastic edits"; private String summary = "my fantastic edits";
@BeforeMethod @BeforeMethod
public void setUp() { public void setUp() {
fetcher = mock(WikibaseDataFetcher.class); fetcher = mock(WikibaseDataFetcher.class);
@ -46,33 +68,29 @@ public class EditBatchProcessorTest extends RefineTest {
editor.disableEditing(); // just in case we got mocking wrong editor.disableEditing(); // just in case we got mocking wrong
library = new NewItemLibrary(); library = new NewItemLibrary();
} }
@Test @Test
public void testNewItem() throws InterruptedException, MediaWikiApiErrorException, IOException { public void testNewItem()
throws InterruptedException, MediaWikiApiErrorException, IOException {
List<ItemUpdate> batch = new ArrayList<>(); List<ItemUpdate> batch = new ArrayList<>();
batch.add(new ItemUpdateBuilder(TestingData.existingId) batch.add(new ItemUpdateBuilder(TestingData.existingId)
.addAlias(Datamodel.makeMonolingualTextValue("my new alias", "en")) .addAlias(Datamodel.makeMonolingualTextValue("my new alias", "en"))
.addStatement(TestingData.generateStatement(TestingData.existingId, TestingData.newIdA)) .addStatement(TestingData.generateStatement(TestingData.existingId, TestingData.newIdA)).build());
.build());
MonolingualTextValue label = Datamodel.makeMonolingualTextValue("better label", "en"); MonolingualTextValue label = Datamodel.makeMonolingualTextValue("better label", "en");
batch.add(new ItemUpdateBuilder(TestingData.newIdA) batch.add(new ItemUpdateBuilder(TestingData.newIdA).addAlias(label).build());
.addAlias(label)
.build());
// Plan expected edits // Plan expected edits
ItemDocument existingItem = ItemDocumentBuilder.forItemId(TestingData.existingId) ItemDocument existingItem = ItemDocumentBuilder.forItemId(TestingData.existingId)
.withLabel(Datamodel.makeMonolingualTextValue("pomme", "fr")) .withLabel(Datamodel.makeMonolingualTextValue("pomme", "fr"))
.withDescription(Datamodel.makeMonolingualTextValue("fruit délicieux", "fr")) .withDescription(Datamodel.makeMonolingualTextValue("fruit délicieux", "fr")).build();
.build();
when(fetcher.getEntityDocuments(Collections.singletonList(TestingData.existingId.getId()))) when(fetcher.getEntityDocuments(Collections.singletonList(TestingData.existingId.getId())))
.thenReturn(Collections.singletonMap(TestingData.existingId.getId(), existingItem)); .thenReturn(Collections.singletonMap(TestingData.existingId.getId(), existingItem));
ItemDocument expectedNewItem = ItemDocumentBuilder.forItemId(TestingData.newIdA) ItemDocument expectedNewItem = ItemDocumentBuilder.forItemId(TestingData.newIdA).withLabel(label).build();
.withLabel(label).build();
ItemDocument createdNewItem = ItemDocumentBuilder.forItemId(Datamodel.makeWikidataItemIdValue("Q1234")) ItemDocument createdNewItem = ItemDocumentBuilder.forItemId(Datamodel.makeWikidataItemIdValue("Q1234"))
.withLabel(label).withRevisionId(37828L).build(); .withLabel(label).withRevisionId(37828L).build();
when(editor.createItemDocument(expectedNewItem, summary)).thenReturn(createdNewItem); when(editor.createItemDocument(expectedNewItem, summary)).thenReturn(createdNewItem);
EditBatchProcessor processor = new EditBatchProcessor(fetcher, editor, batch, library, summary, 50); EditBatchProcessor processor = new EditBatchProcessor(fetcher, editor, batch, library, summary, 50);
assertEquals(2, processor.remainingEdits()); assertEquals(2, processor.remainingEdits());
assertEquals(0, processor.progress()); assertEquals(0, processor.progress());
@ -85,67 +103,63 @@ public class EditBatchProcessorTest extends RefineTest {
processor.performEdit(); // does not do anything processor.performEdit(); // does not do anything
assertEquals(0, processor.remainingEdits()); assertEquals(0, processor.remainingEdits());
assertEquals(100, processor.progress()); assertEquals(100, processor.progress());
NewItemLibrary expectedLibrary = new NewItemLibrary(); NewItemLibrary expectedLibrary = new NewItemLibrary();
expectedLibrary.setQid(1234L, "Q1234"); expectedLibrary.setQid(1234L, "Q1234");
assertEquals(expectedLibrary, library); assertEquals(expectedLibrary, library);
} }
@Test @Test
public void testMultipleBatches() throws MediaWikiApiErrorException, InterruptedException, IOException { public void testMultipleBatches()
throws MediaWikiApiErrorException, InterruptedException, IOException {
// Prepare test data // Prepare test data
MonolingualTextValue description = Datamodel.makeMonolingualTextValue("village in Nepal", "en"); MonolingualTextValue description = Datamodel.makeMonolingualTextValue("village in Nepal", "en");
List<String> ids = new ArrayList<>(); List<String> ids = new ArrayList<>();
for(int i = 124; i < 190; i++) { for (int i = 124; i < 190; i++) {
ids.add("Q"+String.valueOf(i)); ids.add("Q" + String.valueOf(i));
} }
List<ItemIdValue> qids = ids.stream() List<ItemIdValue> qids = ids.stream().map(e -> Datamodel.makeWikidataItemIdValue(e))
.map(e -> Datamodel.makeWikidataItemIdValue(e))
.collect(Collectors.toList()); .collect(Collectors.toList());
List<ItemUpdate> batch = qids.stream() List<ItemUpdate> batch = qids.stream()
.map(qid -> new ItemUpdateBuilder(qid) .map(qid -> new ItemUpdateBuilder(qid).addDescription(description).build())
.addDescription(description)
.build())
.collect(Collectors.toList()); .collect(Collectors.toList());
int batchSize = 50; int batchSize = 50;
List<ItemDocument> fullBatch = qids.stream() List<ItemDocument> fullBatch = qids.stream()
.map(qid -> ItemDocumentBuilder.forItemId(qid) .map(qid -> ItemDocumentBuilder.forItemId(qid)
.withStatement(TestingData.generateStatement(qid, TestingData.existingId)) .withStatement(TestingData.generateStatement(qid, TestingData.existingId)).build())
.build())
.collect(Collectors.toList()); .collect(Collectors.toList());
List<ItemDocument> firstBatch = fullBatch.subList(0, batchSize); List<ItemDocument> firstBatch = fullBatch.subList(0, batchSize);
List<ItemDocument> secondBatch = fullBatch.subList(batchSize, fullBatch.size()); List<ItemDocument> secondBatch = fullBatch.subList(batchSize, fullBatch.size());
when(fetcher.getEntityDocuments(toQids(firstBatch))).thenReturn(toMap(firstBatch)); when(fetcher.getEntityDocuments(toQids(firstBatch))).thenReturn(toMap(firstBatch));
when(fetcher.getEntityDocuments(toQids(secondBatch))).thenReturn(toMap(secondBatch)); when(fetcher.getEntityDocuments(toQids(secondBatch))).thenReturn(toMap(secondBatch));
// Run edits // Run edits
EditBatchProcessor processor = new EditBatchProcessor(fetcher, editor, batch, library, summary, batchSize); EditBatchProcessor processor = new EditBatchProcessor(fetcher, editor, batch, library, summary, batchSize);
assertEquals(0, processor.progress()); assertEquals(0, processor.progress());
for(int i = 124; i < 190; i++) { for (int i = 124; i < 190; i++) {
assertEquals(processor.remainingEdits(), 190-i); assertEquals(processor.remainingEdits(), 190 - i);
processor.performEdit(); processor.performEdit();
} }
assertEquals(0, processor.remainingEdits()); assertEquals(0, processor.remainingEdits());
assertEquals(100, processor.progress()); assertEquals(100, processor.progress());
// Check result // Check result
assertEquals(new NewItemLibrary(), library); assertEquals(new NewItemLibrary(), library);
verify(fetcher, times(1)).getEntityDocuments(toQids(firstBatch)); verify(fetcher, times(1)).getEntityDocuments(toQids(firstBatch));
verify(fetcher, times(1)).getEntityDocuments(toQids(secondBatch)); verify(fetcher, times(1)).getEntityDocuments(toQids(secondBatch));
for(ItemDocument doc : fullBatch) { for (ItemDocument doc : fullBatch) {
verify(editor, times(1)).updateTermsStatements(doc, Collections.emptyList(), verify(editor, times(1)).updateTermsStatements(doc, Collections.emptyList(),
Collections.singletonList(description), Collections.emptyList(), Collections.emptyList(), Collections.singletonList(description), Collections.emptyList(), Collections.emptyList(),
Collections.emptyList(), Collections.emptyList(), summary); Collections.emptyList(), Collections.emptyList(), summary);
} }
} }
private Map<String, EntityDocument> toMap(List<ItemDocument> docs) { private Map<String, EntityDocument> toMap(List<ItemDocument> docs) {
return docs.stream() return docs.stream().collect(Collectors.toMap(doc -> doc.getItemId().getId(), doc -> doc));
.collect(Collectors.toMap(doc -> doc.getItemId().getId(), doc -> doc));
} }
private List<String> toQids(List<ItemDocument> docs) { private List<String> toQids(List<ItemDocument> docs) {
return docs.stream().map(doc -> doc.getItemId().getId()).collect(Collectors.toList()); return docs.stream().map(doc -> doc.getItemId().getId()).collect(Collectors.toList());
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.editing; package org.openrefine.wikidata.editing;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -13,20 +36,21 @@ import com.google.refine.model.Recon;
import com.google.refine.tests.RefineTest; import com.google.refine.tests.RefineTest;
public class NewItemLibraryTest extends RefineTest { public class NewItemLibraryTest extends RefineTest {
private NewItemLibrary library; private NewItemLibrary library;
@BeforeMethod @BeforeMethod
public void setUp() { public void setUp() {
library = new NewItemLibrary(); library = new NewItemLibrary();
library.setQid(1234L, "Q345"); library.setQid(1234L, "Q345");
library.setQid(3289L, "Q384"); library.setQid(3289L, "Q384");
} }
@Test @Test
public void testRetrieveItem() { public void testRetrieveItem() {
assertEquals("Q345", library.getQid(1234L)); assertEquals("Q345", library.getQid(1234L));
} }
@Test @Test
public void testUpdateReconciledCells() { public void testUpdateReconciledCells() {
Project project = createCSVProject(TestingData.inceptionWithNewCsv); Project project = createCSVProject(TestingData.inceptionWithNewCsv);
@ -45,18 +69,18 @@ public class NewItemLibraryTest extends RefineTest {
isMatchedTo("Q865528", project.rows.get(1).cells.get(0)); isMatchedTo("Q865528", project.rows.get(1).cells.get(0));
isNewTo(1234L, project.rows.get(2).cells.get(0)); isNewTo(1234L, project.rows.get(2).cells.get(0));
} }
@Test @Test
public void testSerialize() { public void testSerialize() {
JacksonSerializationTest.canonicalSerialization(NewItemLibrary.class, library, JacksonSerializationTest.canonicalSerialization(NewItemLibrary.class, library,
"{\"qidMap\":{\"1234\":\"Q345\",\"3289\":\"Q384\"}}"); "{\"qidMap\":{\"1234\":\"Q345\",\"3289\":\"Q384\"}}");
} }
private void isMatchedTo(String qid, Cell cell) { private void isMatchedTo(String qid, Cell cell) {
assertEquals(Recon.Judgment.Matched, cell.recon.judgment); assertEquals(Recon.Judgment.Matched, cell.recon.judgment);
assertEquals(qid, cell.recon.match.id); assertEquals(qid, cell.recon.match.id);
} }
private void isNewTo(long id, Cell cell) { private void isNewTo(long id, Cell cell) {
assertEquals(Recon.Judgment.New, cell.recon.judgment); assertEquals(Recon.Judgment.New, cell.recon.judgment);
assertEquals(id, cell.recon.judgmentHistoryEntry); assertEquals(id, cell.recon.judgmentHistoryEntry);

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.editing; package org.openrefine.wikidata.editing;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -11,39 +34,39 @@ import org.wikidata.wdtk.datamodel.helpers.Datamodel;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue; import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
public class ReconEntityRewriterTest { public class ReconEntityRewriterTest {
NewItemLibrary library = null; NewItemLibrary library = null;
ReconEntityRewriter rewriter = null; ReconEntityRewriter rewriter = null;
ItemIdValue subject = TestingData.newIdA; ItemIdValue subject = TestingData.newIdA;
ItemIdValue newlyCreated = Datamodel.makeWikidataItemIdValue("Q1234"); ItemIdValue newlyCreated = Datamodel.makeWikidataItemIdValue("Q1234");
@BeforeMethod @BeforeMethod
public void setUp() { public void setUp() {
library = new NewItemLibrary(); library = new NewItemLibrary();
rewriter = new ReconEntityRewriter(library, subject); rewriter = new ReconEntityRewriter(library, subject);
} }
@Test(expectedExceptions=IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testNotCreatedYet() { public void testNotCreatedYet() {
rewriter.copy(TestingData.newIdB); rewriter.copy(TestingData.newIdB);
} }
@Test @Test
public void testSuccessfulRewrite() { public void testSuccessfulRewrite() {
library.setQid(4567L, "Q1234"); library.setQid(4567L, "Q1234");
assertEquals(newlyCreated, rewriter.copy(TestingData.newIdB)); assertEquals(newlyCreated, rewriter.copy(TestingData.newIdB));
} }
@Test @Test
public void testSubjectNotRewriten() { public void testSubjectNotRewriten() {
assertEquals(subject, rewriter.copy(subject)); assertEquals(subject, rewriter.copy(subject));
} }
@Test @Test
public void testMatched() { public void testMatched() {
assertEquals(TestingData.matchedId, rewriter.copy(TestingData.matchedId)); assertEquals(TestingData.matchedId, rewriter.copy(TestingData.matchedId));
} }
@Test @Test
public void testRewriteUpdate() { public void testRewriteUpdate() {
library.setQid(4567L, "Q1234"); library.setQid(4567L, "Q1234");
@ -52,16 +75,14 @@ public class ReconEntityRewriterTest {
.deleteStatement(TestingData.generateStatement(subject, TestingData.existingId)) .deleteStatement(TestingData.generateStatement(subject, TestingData.existingId))
.addLabel(Datamodel.makeMonolingualTextValue("label", "de")) .addLabel(Datamodel.makeMonolingualTextValue("label", "de"))
.addDescription(Datamodel.makeMonolingualTextValue("beschreibung", "de")) .addDescription(Datamodel.makeMonolingualTextValue("beschreibung", "de"))
.addAlias(Datamodel.makeMonolingualTextValue("darstellung", "de")) .addAlias(Datamodel.makeMonolingualTextValue("darstellung", "de")).build();
.build();
ItemUpdate rewritten = rewriter.rewrite(update); ItemUpdate rewritten = rewriter.rewrite(update);
ItemUpdate expected = new ItemUpdateBuilder(subject) ItemUpdate expected = new ItemUpdateBuilder(subject)
.addStatement(TestingData.generateStatement(subject, newlyCreated)) .addStatement(TestingData.generateStatement(subject, newlyCreated))
.deleteStatement(TestingData.generateStatement(subject, TestingData.existingId)) .deleteStatement(TestingData.generateStatement(subject, TestingData.existingId))
.addLabel(Datamodel.makeMonolingualTextValue("label", "de")) .addLabel(Datamodel.makeMonolingualTextValue("label", "de"))
.addDescription(Datamodel.makeMonolingualTextValue("beschreibung", "de")) .addDescription(Datamodel.makeMonolingualTextValue("beschreibung", "de"))
.addAlias(Datamodel.makeMonolingualTextValue("darstellung", "de")) .addAlias(Datamodel.makeMonolingualTextValue("darstellung", "de")).build();
.build();
assertEquals(expected, rewritten); assertEquals(expected, rewritten);
} }
} }

View File

@ -1,7 +1,29 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.exporters; package org.openrefine.wikidata.exporters;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -14,100 +36,101 @@ import org.wikidata.wdtk.datamodel.interfaces.TimeValue;
import org.wikidata.wdtk.datamodel.interfaces.Value; import org.wikidata.wdtk.datamodel.interfaces.Value;
public class QSValuePrinterTest { public class QSValuePrinterTest {
private QSValuePrinter printer; private QSValuePrinter printer;
public QSValuePrinterTest() { public QSValuePrinterTest() {
printer = new QSValuePrinter(); printer = new QSValuePrinter();
} }
void assertPrints(String expectedFormat, Value datavalue) { void assertPrints(String expectedFormat, Value datavalue) {
assertEquals(expectedFormat, datavalue.accept(printer)); assertEquals(expectedFormat, datavalue.accept(printer));
} }
// Entity id values // Entity id values
@Test @Test
public void printItemId() { public void printItemId() {
assertPrints("Q42", Datamodel.makeWikidataItemIdValue("Q42")); assertPrints("Q42", Datamodel.makeWikidataItemIdValue("Q42"));
} }
@Test @Test
public void printPropertyId() { public void printPropertyId() {
assertPrints("P42", Datamodel.makeWikidataPropertyIdValue("P42")); assertPrints("P42", Datamodel.makeWikidataPropertyIdValue("P42"));
} }
@Test @Test
public void printNewItemId() { public void printNewItemId() {
ReconEntityIdValue id = TestingData.makeNewItemIdValue(12345L, "my new item"); ReconEntityIdValue id = TestingData.makeNewItemIdValue(12345L, "my new item");
assertEquals("LAST", id.accept(printer)); assertEquals("LAST", id.accept(printer));
// because no entity was previously created // because no entity was previously created
ReconEntityIdValue differentId = TestingData.makeMatchedItemIdValue("Q78", "my existing item"); ReconEntityIdValue differentId = TestingData.makeMatchedItemIdValue("Q78", "my existing item");
assertEquals("Q78", differentId.accept(printer)); assertEquals("Q78", differentId.accept(printer));
} }
// Globe coordinates // Globe coordinates
@Test @Test
public void printGlobeCoordinate() { public void printGlobeCoordinate() {
// I don't see how to avoid the trailing zeros - in any case it's not a big deal because // I don't see how to avoid the trailing zeros - in any case it's not a big deal
// the precision is governed by a different parameter that QuickStatements does not support. // because
// the precision is governed by a different parameter that QuickStatements does
// not support.
assertPrints("@43.261930/10.927080", Datamodel.makeGlobeCoordinatesValue(43.26193, 10.92708, assertPrints("@43.261930/10.927080", Datamodel.makeGlobeCoordinatesValue(43.26193, 10.92708,
GlobeCoordinatesValue.PREC_DEGREE, GlobeCoordinatesValue.GLOBE_EARTH)); GlobeCoordinatesValue.PREC_DEGREE, GlobeCoordinatesValue.GLOBE_EARTH));
} }
// Monolingual text values // Monolingual text values
@Test @Test
public void printMonolingualTextValue() { public void printMonolingualTextValue() {
assertPrints("pl:\"Krzyżacy\"", Datamodel.makeMonolingualTextValue("Krzyżacy", "pl")); assertPrints("pl:\"Krzyżacy\"", Datamodel.makeMonolingualTextValue("Krzyżacy", "pl"));
} }
// Quantity values // Quantity values
@Test @Test
public void printSimpleQuantityValue() { public void printSimpleQuantityValue() {
assertPrints("10.00", Datamodel.makeQuantityValue(new BigDecimal("10.00"), assertPrints("10.00", Datamodel.makeQuantityValue(new BigDecimal("10.00"), null, null, "1"));
null, null, "1"));
} }
@Test @Test
public void printQuantityValueWithUnit() { public void printQuantityValueWithUnit() {
assertPrints("10.00U11573", Datamodel.makeQuantityValue(new BigDecimal("10.00"), assertPrints("10.00U11573", Datamodel.makeQuantityValue(new BigDecimal("10.00"), null, null,
null, null, "http://www.wikidata.org/entity/Q11573")); "http://www.wikidata.org/entity/Q11573"));
} }
@Test @Test
public void printQuantityValueWithBounds() { public void printQuantityValueWithBounds() {
assertPrints("10.00[9.0,11.05]", Datamodel.makeQuantityValue(new BigDecimal("10.00"), assertPrints("10.00[9.0,11.05]", Datamodel.makeQuantityValue(new BigDecimal("10.00"), new BigDecimal("9.0"),
new BigDecimal("9.0"), new BigDecimal("11.05"), "1")); new BigDecimal("11.05"), "1"));
} }
@Test @Test
public void printFullQuantity() { public void printFullQuantity() {
assertPrints("10.00[9.0,11.05]U11573", Datamodel.makeQuantityValue(new BigDecimal("10.00"), assertPrints("10.00[9.0,11.05]U11573", Datamodel.makeQuantityValue(new BigDecimal("10.00"),
new BigDecimal("9.0"), new BigDecimal("11.05"), "http://www.wikidata.org/entity/Q11573")); new BigDecimal("9.0"), new BigDecimal("11.05"), "http://www.wikidata.org/entity/Q11573"));
} }
// String values // String values
@Test @Test
public void printString() { public void printString() {
assertPrints("\"hello\"", Datamodel.makeStringValue("hello")); assertPrints("\"hello\"", Datamodel.makeStringValue("hello"));
} }
// Time values // Time values
@Test @Test
public void printYear() { public void printYear() {
assertPrints("+1586-00-00T00:00:00Z/9", Datamodel.makeTimeValue(1586L, (byte)0, (byte)0, (byte)0, assertPrints("+1586-00-00T00:00:00Z/9", Datamodel.makeTimeValue(1586L, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
(byte)0, (byte)0, (byte)9, 0, 0, 0, TimeValue.CM_GREGORIAN_PRO)); (byte) 0, (byte) 9, 0, 0, 0, TimeValue.CM_GREGORIAN_PRO));
} }
@Test @Test
public void printDay() { public void printDay() {
assertPrints("+1586-03-09T00:00:00Z/11", Datamodel.makeTimeValue(1586L, (byte)3, (byte)9, (byte)0, assertPrints("+1586-03-09T00:00:00Z/11", Datamodel.makeTimeValue(1586L, (byte) 3, (byte) 9, (byte) 0, (byte) 0,
(byte)0, (byte)0, (byte)11, 0, 0, 0, TimeValue.CM_GREGORIAN_PRO)); (byte) 0, (byte) 11, 0, 0, 0, TimeValue.CM_GREGORIAN_PRO));
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.exporters; package org.openrefine.wikidata.exporters;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -14,7 +37,6 @@ import org.openrefine.wikidata.schema.WikibaseSchema;
import org.openrefine.wikidata.testing.TestingData; import org.openrefine.wikidata.testing.TestingData;
import org.openrefine.wikidata.updates.ItemUpdate; import org.openrefine.wikidata.updates.ItemUpdate;
import org.openrefine.wikidata.updates.ItemUpdateBuilder; import org.openrefine.wikidata.updates.ItemUpdateBuilder;
import org.openrefine.wikidata.updates.scheduler.UpdateSchedulerTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import org.wikidata.wdtk.datamodel.helpers.Datamodel; import org.wikidata.wdtk.datamodel.helpers.Datamodel;
import org.wikidata.wdtk.datamodel.interfaces.Claim; import org.wikidata.wdtk.datamodel.interfaces.Claim;
@ -30,95 +52,93 @@ import com.google.refine.tests.RefineTest;
public class QuickStatementsExporterTest extends RefineTest { public class QuickStatementsExporterTest extends RefineTest {
private QuickStatementsExporter exporter = new QuickStatementsExporter(); private QuickStatementsExporter exporter = new QuickStatementsExporter();
private ItemIdValue newIdA = TestingData.makeNewItemIdValue(1234L, "new item A"); private ItemIdValue newIdA = TestingData.makeNewItemIdValue(1234L, "new item A");
private ItemIdValue newIdB = TestingData.makeNewItemIdValue(5678L, "new item B"); private ItemIdValue newIdB = TestingData.makeNewItemIdValue(5678L, "new item B");
private ItemIdValue qid1 = Datamodel.makeWikidataItemIdValue("Q1377"); private ItemIdValue qid1 = Datamodel.makeWikidataItemIdValue("Q1377");
private ItemIdValue qid2 = Datamodel.makeWikidataItemIdValue("Q865528"); private ItemIdValue qid2 = Datamodel.makeWikidataItemIdValue("Q865528");
private String export(ItemUpdate... itemUpdates) throws IOException { private String export(ItemUpdate... itemUpdates)
StringWriter writer = new StringWriter(); throws IOException {
exporter.translateItemList(Arrays.asList(itemUpdates), writer); StringWriter writer = new StringWriter();
return writer.toString(); exporter.translateItemList(Arrays.asList(itemUpdates), writer);
} return writer.toString();
}
@Test
public void testSimpleProject() throws JSONException, IOException { @Test
Project project = this.createCSVProject( public void testSimpleProject()
TestingData.inceptionWithNewCsv); throws JSONException, IOException {
TestingData.reconcileInceptionCells(project); Project project = this.createCSVProject(TestingData.inceptionWithNewCsv);
JSONObject serialized = TestingData.jsonFromFile("data/schema/inception.json"); TestingData.reconcileInceptionCells(project);
WikibaseSchema schema = WikibaseSchema.reconstruct(serialized); JSONObject serialized = TestingData.jsonFromFile("data/schema/inception.json");
project.overlayModels.put("wikibaseSchema", schema); WikibaseSchema schema = WikibaseSchema.reconstruct(serialized);
Engine engine = new Engine(project); project.overlayModels.put("wikibaseSchema", schema);
Engine engine = new Engine(project);
StringWriter writer = new StringWriter();
Properties properties = new Properties(); StringWriter writer = new StringWriter();
exporter.export(project, properties, engine, writer); Properties properties = new Properties();
assertEquals(TestingData.inceptionWithNewQS, writer.toString()); exporter.export(project, properties, engine, writer);
} assertEquals(TestingData.inceptionWithNewQS, writer.toString());
}
@Test
public void testImpossibleScheduling() throws IOException { @Test
Statement sNewAtoNewB = TestingData.generateStatement(newIdA, newIdB); public void testImpossibleScheduling()
ItemUpdate update = new ItemUpdateBuilder(newIdA).addStatement(sNewAtoNewB).build(); throws IOException {
Statement sNewAtoNewB = TestingData.generateStatement(newIdA, newIdB);
assertEquals(QuickStatementsExporter.impossibleSchedulingErrorMessage, ItemUpdate update = new ItemUpdateBuilder(newIdA).addStatement(sNewAtoNewB).build();
export(update));
} assertEquals(QuickStatementsExporter.impossibleSchedulingErrorMessage, export(update));
}
@Test
public void testNameDesc() throws IOException { @Test
ItemUpdate update = new ItemUpdateBuilder(newIdA) public void testNameDesc()
.addLabel(Datamodel.makeMonolingualTextValue("my new item", "en")) throws IOException {
.addDescription(Datamodel.makeMonolingualTextValue("isn't it awesome?", "en")) ItemUpdate update = new ItemUpdateBuilder(newIdA)
.addAlias(Datamodel.makeMonolingualTextValue("fabitem", "en")) .addLabel(Datamodel.makeMonolingualTextValue("my new item", "en"))
.build(); .addDescription(Datamodel.makeMonolingualTextValue("isn't it awesome?", "en"))
.addAlias(Datamodel.makeMonolingualTextValue("fabitem", "en")).build();
assertEquals("CREATE\n"+
"LAST\tLen\t\"my new item\"\n"+ assertEquals("CREATE\n" + "LAST\tLen\t\"my new item\"\n" + "LAST\tDen\t\"isn't it awesome?\"\n"
"LAST\tDen\t\"isn't it awesome?\"\n"+ + "LAST\tAen\t\"fabitem\"\n", export(update));
"LAST\tAen\t\"fabitem\"\n", }
export(update));
} @Test
public void testDeleteStatement()
@Test throws IOException {
public void testDeleteStatement() throws IOException { ItemUpdate update = new ItemUpdateBuilder(qid1).deleteStatement(TestingData.generateStatement(qid1, qid2))
ItemUpdate update = new ItemUpdateBuilder(qid1) .build();
.deleteStatement(TestingData.generateStatement(qid1, qid2))
.build(); assertEquals("- Q1377\tP38\tQ865528\n", export(update));
}
assertEquals("- Q1377\tP38\tQ865528\n", export(update));
} @Test
public void testQualifier()
@Test throws IOException {
public void testQualifier() throws IOException { Statement baseStatement = TestingData.generateStatement(qid1, qid2);
Statement baseStatement = TestingData.generateStatement(qid1, qid2); Statement otherStatement = TestingData.generateStatement(qid2, qid1);
Statement otherStatement = TestingData.generateStatement(qid2, qid1); Snak qualifierSnak = otherStatement.getClaim().getMainSnak();
Snak qualifierSnak = otherStatement.getClaim().getMainSnak(); SnakGroup group = Datamodel.makeSnakGroup(Collections.singletonList(qualifierSnak));
SnakGroup group = Datamodel.makeSnakGroup(Collections.singletonList(qualifierSnak)); Claim claim = Datamodel.makeClaim(qid1, baseStatement.getClaim().getMainSnak(),
Claim claim = Datamodel.makeClaim(qid1, baseStatement.getClaim().getMainSnak(), Collections.singletonList(group));
Collections.singletonList(group)); Statement statement = Datamodel.makeStatement(claim, Collections.emptyList(), StatementRank.NORMAL, "");
Statement statement = Datamodel.makeStatement(claim, Collections.emptyList(), StatementRank.NORMAL, ""); ItemUpdate update = new ItemUpdateBuilder(qid1).addStatement(statement).build();
ItemUpdate update = new ItemUpdateBuilder(qid1)
.addStatement(statement) assertEquals("Q1377\tP38\tQ865528\tP38\tQ1377\n", export(update));
.build(); }
assertEquals("Q1377\tP38\tQ865528\tP38\tQ1377\n", export(update)); @Test
} public void testNoSchema()
throws IOException {
@Test Project project = this.createCSVProject("a,b\nc,d");
public void testNoSchema() throws IOException { Engine engine = new Engine(project);
Project project = this.createCSVProject("a,b\nc,d"); StringWriter writer = new StringWriter();
Engine engine = new Engine(project); Properties properties = new Properties();
StringWriter writer = new StringWriter(); exporter.export(project, properties, engine, writer);
Properties properties = new Properties(); assertEquals(QuickStatementsExporter.noSchemaErrorMessage, writer.toString());
exporter.export(project, properties, engine, writer); }
assertEquals(QuickStatementsExporter.noSchemaErrorMessage, writer.toString());
} @Test
public void testGetContentType() {
@Test assertEquals("text/plain", exporter.getContentType());
public void testGetContentType() { }
assertEquals("text/plain", exporter.getContentType());
}
} }

View File

@ -1,5 +1,31 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.operations; package org.openrefine.wikidata.operations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException; import java.io.IOException;
import java.io.LineNumberReader; import java.io.LineNumberReader;
import java.io.StringReader; import java.io.StringReader;
@ -12,9 +38,6 @@ import org.openrefine.wikidata.testing.JacksonSerializationTest;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.refine.history.Change; import com.google.refine.history.Change;
import com.google.refine.model.AbstractOperation; import com.google.refine.model.AbstractOperation;
import com.google.refine.model.Project; import com.google.refine.model.Project;
@ -25,11 +48,11 @@ import com.google.refine.util.Pool;
import edu.mit.simile.butterfly.ButterflyModule; import edu.mit.simile.butterfly.ButterflyModule;
public abstract class OperationTest extends RefineTest { public abstract class OperationTest extends RefineTest {
protected Project project = null; protected Project project = null;
protected ButterflyModule module = null; protected ButterflyModule module = null;
protected Pool pool = null; protected Pool pool = null;
@BeforeMethod @BeforeMethod
public void setUp() { public void setUp() {
project = createCSVProject("a,b\nc,d"); project = createCSVProject("a,b\nc,d");
@ -37,17 +60,20 @@ public abstract class OperationTest extends RefineTest {
when(module.getName()).thenReturn("wikidata"); when(module.getName()).thenReturn("wikidata");
pool = new Pool(); pool = new Pool();
} }
protected void registerOperation(String name, Class klass) { protected void registerOperation(String name, Class klass) {
OperationRegistry.registerOperation(module, name, klass); OperationRegistry.registerOperation(module, name, klass);
} }
public abstract AbstractOperation reconstruct() throws Exception; public abstract AbstractOperation reconstruct()
throws Exception;
public abstract JSONObject getJson() throws Exception;
public abstract JSONObject getJson()
throws Exception;
@Test @Test
public void testReconstruct() throws Exception { public void testReconstruct()
throws Exception {
JSONObject json = getJson(); JSONObject json = getJson();
AbstractOperation op = reconstruct(); AbstractOperation op = reconstruct();
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
@ -55,13 +81,14 @@ public abstract class OperationTest extends RefineTest {
op.write(jsonWriter, new Properties()); op.write(jsonWriter, new Properties());
JacksonSerializationTest.assertJsonEquals(json.toString(), writer.toString()); JacksonSerializationTest.assertJsonEquals(json.toString(), writer.toString());
} }
protected LineNumberReader makeReader(String input) { protected LineNumberReader makeReader(String input) {
StringReader reader = new StringReader(input); StringReader reader = new StringReader(input);
return new LineNumberReader(reader); return new LineNumberReader(reader);
} }
protected String saveChange(Change change) throws IOException { protected String saveChange(Change change)
throws IOException {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
change.save(writer, new Properties()); change.save(writer, new Properties());
return writer.toString(); return writer.toString();

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.operations; package org.openrefine.wikidata.operations;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -14,41 +37,43 @@ import com.google.refine.model.AbstractOperation;
import com.google.refine.model.Recon; import com.google.refine.model.Recon;
public class PerformWikibaseEditsOperationTest extends OperationTest { public class PerformWikibaseEditsOperationTest extends OperationTest {
@BeforeMethod @BeforeMethod
public void registerOperation() { public void registerOperation() {
registerOperation("perform-wikibase-edits", PerformWikibaseEditsOperation.class); registerOperation("perform-wikibase-edits", PerformWikibaseEditsOperation.class);
} }
@Override @Override
public AbstractOperation reconstruct() throws Exception { public AbstractOperation reconstruct()
throws Exception {
JSONObject json = getJson(); JSONObject json = getJson();
return PerformWikibaseEditsOperation.reconstruct(project, json); return PerformWikibaseEditsOperation.reconstruct(project, json);
} }
@Override @Override
public JSONObject getJson() throws Exception { public JSONObject getJson()
throws Exception {
return TestingData.jsonFromFile("data/operations/perform-edits.json"); return TestingData.jsonFromFile("data/operations/perform-edits.json");
} }
@Test @Test
public void testLoadChange() throws Exception { public void testLoadChange()
String changeString = "newItems={\"qidMap\":{\"1234\":\"Q789\"}}\n" + throws Exception {
"/ec/\n"; String changeString = "newItems={\"qidMap\":{\"1234\":\"Q789\"}}\n" + "/ec/\n";
LineNumberReader reader = makeReader(changeString); LineNumberReader reader = makeReader(changeString);
Change change = PerformWikibaseEditsOperation.PerformWikibaseEditsChange.load(reader, pool); Change change = PerformWikibaseEditsOperation.PerformWikibaseEditsChange.load(reader, pool);
project.rows.get(0).cells.set(0, TestingData.makeNewItemCell(1234L, "my new item")); project.rows.get(0).cells.set(0, TestingData.makeNewItemCell(1234L, "my new item"));
change.apply(project); change.apply(project);
assertEquals(Recon.Judgment.Matched, project.rows.get(0).cells.get(0).recon.judgment); assertEquals(Recon.Judgment.Matched, project.rows.get(0).cells.get(0).recon.judgment);
assertEquals("Q789", project.rows.get(0).cells.get(0).recon.match.id); assertEquals("Q789", project.rows.get(0).cells.get(0).recon.match.id);
change.revert(project); change.revert(project);
assertEquals(Recon.Judgment.New, project.rows.get(0).cells.get(0).recon.judgment); assertEquals(Recon.Judgment.New, project.rows.get(0).cells.get(0).recon.judgment);
assertEquals(changeString, saveChange(change)); assertEquals(changeString, saveChange(change));
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.operations; package org.openrefine.wikidata.operations;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -14,44 +37,44 @@ import org.testng.annotations.Test;
import com.google.refine.history.Change; import com.google.refine.history.Change;
import com.google.refine.model.AbstractOperation; import com.google.refine.model.AbstractOperation;
public class SaveWikibaseSchemaOperationTest extends OperationTest { public class SaveWikibaseSchemaOperationTest extends OperationTest {
@BeforeMethod @BeforeMethod
public void registerOperation() { public void registerOperation() {
registerOperation("save-wikibase-schema", SaveWikibaseSchemaOperation.class); registerOperation("save-wikibase-schema", SaveWikibaseSchemaOperation.class);
} }
@Override @Override
public AbstractOperation reconstruct() throws Exception { public AbstractOperation reconstruct()
throws Exception {
return SaveWikibaseSchemaOperation.reconstruct(project, getJson()); return SaveWikibaseSchemaOperation.reconstruct(project, getJson());
} }
@Override @Override
public JSONObject getJson() throws Exception { public JSONObject getJson()
throws Exception {
return TestingData.jsonFromFile("data/operations/save-schema.json"); return TestingData.jsonFromFile("data/operations/save-schema.json");
} }
@Test @Test
public void testLoadChange() throws Exception { public void testLoadChange()
throws Exception {
JSONObject schemaJson = TestingData.jsonFromFile("data/schema/inception.json"); JSONObject schemaJson = TestingData.jsonFromFile("data/schema/inception.json");
String changeString = String changeString = "newSchema=" + schemaJson.toString() + "\n" + "oldSchema=\n" + "/ec/";
"newSchema="+schemaJson.toString()+"\n" +
"oldSchema=\n" +
"/ec/";
WikibaseSchema schema = WikibaseSchema.reconstruct(schemaJson); WikibaseSchema schema = WikibaseSchema.reconstruct(schemaJson);
LineNumberReader reader = makeReader(changeString); LineNumberReader reader = makeReader(changeString);
Change change = SaveWikibaseSchemaOperation.WikibaseSchemaChange.load(reader, pool); Change change = SaveWikibaseSchemaOperation.WikibaseSchemaChange.load(reader, pool);
change.apply(project); change.apply(project);
assertEquals(schema, project.overlayModels.get(SaveWikibaseSchemaOperation.WikibaseSchemaChange.overlayModelKey)); assertEquals(schema,
project.overlayModels.get(SaveWikibaseSchemaOperation.WikibaseSchemaChange.overlayModelKey));
change.revert(project); change.revert(project);
assertNull(project.overlayModels.get(SaveWikibaseSchemaOperation.WikibaseSchemaChange.overlayModelKey)); assertNull(project.overlayModels.get(SaveWikibaseSchemaOperation.WikibaseSchemaChange.overlayModelKey));
saveChange(change); // not checking for equality because JSON serialization varies saveChange(change); // not checking for equality because JSON serialization varies
} }
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa; package org.openrefine.wikidata.qa;
import java.util.Arrays; import java.util.Arrays;
@ -8,14 +31,13 @@ import java.util.stream.Collectors;
import org.wikidata.wdtk.datamodel.helpers.Datamodel; import org.wikidata.wdtk.datamodel.helpers.Datamodel;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue; import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
public class MockConstraintFetcher implements ConstraintFetcher { public class MockConstraintFetcher implements ConstraintFetcher {
public static PropertyIdValue pidWithInverse = Datamodel.makeWikidataPropertyIdValue("P350"); public static PropertyIdValue pidWithInverse = Datamodel.makeWikidataPropertyIdValue("P350");
public static PropertyIdValue inversePid = Datamodel.makeWikidataPropertyIdValue("P57"); public static PropertyIdValue inversePid = Datamodel.makeWikidataPropertyIdValue("P57");
public static PropertyIdValue allowedQualifierPid = Datamodel.makeWikidataPropertyIdValue("P34"); public static PropertyIdValue allowedQualifierPid = Datamodel.makeWikidataPropertyIdValue("P34");
public static PropertyIdValue mandatoryQualifierPid = Datamodel.makeWikidataPropertyIdValue("P97"); public static PropertyIdValue mandatoryQualifierPid = Datamodel.makeWikidataPropertyIdValue("P97");
public static PropertyIdValue mainSnakPid = Datamodel.makeWikidataPropertyIdValue("P1234"); public static PropertyIdValue mainSnakPid = Datamodel.makeWikidataPropertyIdValue("P1234");
public static PropertyIdValue qualifierPid = Datamodel.makeWikidataPropertyIdValue("P987"); public static PropertyIdValue qualifierPid = Datamodel.makeWikidataPropertyIdValue("P987");
public static PropertyIdValue referencePid = Datamodel.makeWikidataPropertyIdValue("P384"); public static PropertyIdValue referencePid = Datamodel.makeWikidataPropertyIdValue("P384");
@ -26,8 +48,8 @@ public class MockConstraintFetcher implements ConstraintFetcher {
} }
/** /**
* This constraint is purposely left inconsistent (the inverse * This constraint is purposely left inconsistent (the inverse constraint holds
* constraint holds only on one side). * only on one side).
*/ */
@Override @Override
public PropertyIdValue getInversePid(PropertyIdValue pid) { public PropertyIdValue getInversePid(PropertyIdValue pid) {

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa; package org.openrefine.wikidata.qa;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -9,13 +32,13 @@ import org.testng.annotations.Test;
public class QAWarningStoreTest { public class QAWarningStoreTest {
public static String exampleJson = "{\"max_severity\":\"CRITICAL\",\"nb_warnings\":5," public static String exampleJson = "{\"max_severity\":\"CRITICAL\",\"nb_warnings\":5,"
+"\"warnings\":[{\"type\":\"new-item-without-label\",\"bucketId\":null," + "\"warnings\":[{\"type\":\"new-item-without-label\",\"bucketId\":null,"
+"\"severity\":\"CRITICAL\",\"count\":3},{\"type\":\"add-statements-with-invalid-format\"," + "\"severity\":\"CRITICAL\",\"count\":3},{\"type\":\"add-statements-with-invalid-format\","
+"\"bucketId\":\"P2427\",\"severity\":\"IMPORTANT\",\"count\":2}]}"; + "\"bucketId\":\"P2427\",\"severity\":\"IMPORTANT\",\"count\":2}]}";
private QAWarningStore store; private QAWarningStore store;
private QAWarning otherWarning; private QAWarning otherWarning;
@BeforeMethod @BeforeMethod
public void setUp() { public void setUp() {
store = new QAWarningStore(); store = new QAWarningStore();
@ -24,18 +47,18 @@ public class QAWarningStoreTest {
otherWarning = new QAWarning("new-item-without-label", null, QAWarning.Severity.CRITICAL, 3); otherWarning = new QAWarning("new-item-without-label", null, QAWarning.Severity.CRITICAL, 3);
store.addWarning(otherWarning); store.addWarning(otherWarning);
} }
@Test @Test
public void testSerialize() { public void testSerialize() {
JacksonSerializationTest.testSerialize(store, exampleJson); JacksonSerializationTest.testSerialize(store, exampleJson);
} }
@Test @Test
public void testCount() { public void testCount() {
assertEquals(5, store.getNbWarnings()); assertEquals(5, store.getNbWarnings());
assertEquals(2, store.getWarnings().size()); assertEquals(2, store.getWarnings().size());
} }
@Test @Test
public void testMaxSeverity() { public void testMaxSeverity() {
assertEquals(QAWarning.Severity.CRITICAL, store.getMaxSeverity()); assertEquals(QAWarning.Severity.CRITICAL, store.getMaxSeverity());

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa; package org.openrefine.wikidata.qa;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -6,25 +29,20 @@ import org.openrefine.wikidata.testing.JacksonSerializationTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;
public class QAWarningTest { public class QAWarningTest {
public static QAWarning exampleWarning = new QAWarning("add-statements-with-invalid-format", public static QAWarning exampleWarning = new QAWarning("add-statements-with-invalid-format", "P2427",
"P2427", QAWarning.Severity.IMPORTANT, 1);
QAWarning.Severity.IMPORTANT, public static String exampleJson = "{\"severity\":\"IMPORTANT\","
1); + "\"count\":1,\"bucketId\":\"P2427\",\"type\":\"add-statements-with-invalid-format\"}";
public static String exampleJson =
"{\"severity\":\"IMPORTANT\","+
"\"count\":1,\"bucketId\":\"P2427\",\"type\":\"add-statements-with-invalid-format\"}";
@Test @Test
public void testSerialize() { public void testSerialize() {
JacksonSerializationTest.testSerialize(exampleWarning, exampleJson); JacksonSerializationTest.testSerialize(exampleWarning, exampleJson);
} }
@Test @Test
public void testAggregate() { public void testAggregate() {
QAWarning firstWarning = new QAWarning("add-statements-with-invalid-format", QAWarning firstWarning = new QAWarning("add-statements-with-invalid-format", "P2427", QAWarning.Severity.INFO,
"P2427",
QAWarning.Severity.INFO,
1); 1);
firstWarning.setProperty("foo", "bar"); firstWarning.setProperty("foo", "bar");
assertEquals(exampleWarning.getAggregationId(), firstWarning.getAggregationId()); assertEquals(exampleWarning.getAggregationId(), firstWarning.getAggregationId());
@ -35,13 +53,10 @@ public class QAWarningTest {
assertEquals(exampleWarning.getSeverity(), merged.getSeverity()); assertEquals(exampleWarning.getSeverity(), merged.getSeverity());
assertEquals("bar", merged.getProperties().get("foo")); assertEquals("bar", merged.getProperties().get("foo"));
} }
@Test @Test
public void testCompare() { public void testCompare() {
QAWarning otherWarning = new QAWarning("no-reference", QAWarning otherWarning = new QAWarning("no-reference", "no-reference", QAWarning.Severity.WARNING, 1);
"no-reference",
QAWarning.Severity.WARNING,
1);
assertEquals(1, otherWarning.compareTo(exampleWarning)); assertEquals(1, otherWarning.compareTo(exampleWarning));
assertEquals(-1, exampleWarning.compareTo(otherWarning)); assertEquals(-1, exampleWarning.compareTo(otherWarning));
assertEquals(0, exampleWarning.compareTo(exampleWarning)); assertEquals(0, exampleWarning.compareTo(exampleWarning));

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa; package org.openrefine.wikidata.qa;
import org.testng.Assert; import org.testng.Assert;
@ -8,9 +31,9 @@ import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class WikidataConstraintFetcherTests { public class WikidataConstraintFetcherTests {
private ConstraintFetcher fetcher; private ConstraintFetcher fetcher;
private PropertyIdValue headOfGovernment; private PropertyIdValue headOfGovernment;
private PropertyIdValue startTime; private PropertyIdValue startTime;
private PropertyIdValue endTime; private PropertyIdValue endTime;
@ -20,7 +43,7 @@ public class WikidataConstraintFetcherTests {
private PropertyIdValue partOf; private PropertyIdValue partOf;
private PropertyIdValue referenceURL; private PropertyIdValue referenceURL;
private PropertyIdValue reasonForDeprecation; private PropertyIdValue reasonForDeprecation;
public WikidataConstraintFetcherTests() { public WikidataConstraintFetcherTests() {
fetcher = new WikidataConstraintFetcher(); fetcher = new WikidataConstraintFetcher();
headOfGovernment = Datamodel.makeWikidataPropertyIdValue("P6"); headOfGovernment = Datamodel.makeWikidataPropertyIdValue("P6");
@ -33,41 +56,41 @@ public class WikidataConstraintFetcherTests {
referenceURL = Datamodel.makeWikidataPropertyIdValue("P854"); referenceURL = Datamodel.makeWikidataPropertyIdValue("P854");
reasonForDeprecation = Datamodel.makeWikidataPropertyIdValue("P2241"); reasonForDeprecation = Datamodel.makeWikidataPropertyIdValue("P2241");
} }
@Test @Test
public void testGetFormatConstraint() { public void testGetFormatConstraint() {
String regex = fetcher.getFormatRegex(gridId); String regex = fetcher.getFormatRegex(gridId);
Pattern pattern = Pattern.compile(regex); Pattern pattern = Pattern.compile(regex);
Assert.assertTrue(pattern.matcher("grid.470811.b").matches()); Assert.assertTrue(pattern.matcher("grid.470811.b").matches());
Assert.assertFalse(pattern.matcher("501100006367").matches()); Assert.assertFalse(pattern.matcher("501100006367").matches());
Assert.assertNull(fetcher.getFormatRegex(instanceOf)); Assert.assertNull(fetcher.getFormatRegex(instanceOf));
} }
@Test @Test
public void testGetInverseConstraint() { public void testGetInverseConstraint() {
Assert.assertEquals(fetcher.getInversePid(partOf), hasPart); Assert.assertEquals(fetcher.getInversePid(partOf), hasPart);
} }
@Test @Test
public void testOnlyReferences() { public void testOnlyReferences() {
Assert.assertTrue(fetcher.isForReferencesOnly(referenceURL)); Assert.assertTrue(fetcher.isForReferencesOnly(referenceURL));
Assert.assertFalse(fetcher.isForReferencesOnly(reasonForDeprecation)); Assert.assertFalse(fetcher.isForReferencesOnly(reasonForDeprecation));
} }
@Test @Test
public void testOnlyQualifiers() { public void testOnlyQualifiers() {
Assert.assertTrue(fetcher.isForQualifiersOnly(reasonForDeprecation)); Assert.assertTrue(fetcher.isForQualifiersOnly(reasonForDeprecation));
Assert.assertFalse(fetcher.isForQualifiersOnly(headOfGovernment)); Assert.assertFalse(fetcher.isForQualifiersOnly(headOfGovernment));
} }
@Test @Test
public void testOnlyValues() { public void testOnlyValues() {
Assert.assertTrue(fetcher.isForValuesOnly(headOfGovernment)); Assert.assertTrue(fetcher.isForValuesOnly(headOfGovernment));
Assert.assertFalse(fetcher.isForValuesOnly(referenceURL)); Assert.assertFalse(fetcher.isForValuesOnly(referenceURL));
} }
@Test @Test
public void testAllowedQualifiers() { public void testAllowedQualifiers() {
Assert.assertTrue(fetcher.allowedQualifiers(headOfGovernment).contains(startTime)); Assert.assertTrue(fetcher.allowedQualifiers(headOfGovernment).contains(startTime));
@ -75,20 +98,20 @@ public class WikidataConstraintFetcherTests {
Assert.assertFalse(fetcher.allowedQualifiers(headOfGovernment).contains(headOfGovernment)); Assert.assertFalse(fetcher.allowedQualifiers(headOfGovernment).contains(headOfGovernment));
Assert.assertNull(fetcher.allowedQualifiers(startTime)); Assert.assertNull(fetcher.allowedQualifiers(startTime));
} }
@Test @Test
public void testMandatoryQualifiers() { public void testMandatoryQualifiers() {
Assert.assertTrue(fetcher.mandatoryQualifiers(headOfGovernment).contains(startTime)); Assert.assertTrue(fetcher.mandatoryQualifiers(headOfGovernment).contains(startTime));
Assert.assertFalse(fetcher.mandatoryQualifiers(headOfGovernment).contains(endTime)); Assert.assertFalse(fetcher.mandatoryQualifiers(headOfGovernment).contains(endTime));
Assert.assertNull(fetcher.allowedQualifiers(startTime)); Assert.assertNull(fetcher.allowedQualifiers(startTime));
} }
@Test @Test
public void testSingleValue() { public void testSingleValue() {
Assert.assertFalse(fetcher.hasSingleValue(headOfGovernment)); Assert.assertFalse(fetcher.hasSingleValue(headOfGovernment));
Assert.assertTrue(fetcher.hasSingleValue(gridId)); Assert.assertTrue(fetcher.hasSingleValue(gridId));
} }
@Test @Test
public void testDistinctValues() { public void testDistinctValues() {
Assert.assertFalse(fetcher.hasDistinctValues(partOf)); Assert.assertFalse(fetcher.hasDistinctValues(partOf));

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import org.openrefine.wikidata.testing.TestingData; import org.openrefine.wikidata.testing.TestingData;
@ -17,12 +40,8 @@ public class DistinctValuesScrutinizerTest extends StatementScrutinizerTest {
public void testTrigger() { public void testTrigger() {
ItemIdValue idA = TestingData.existingId; ItemIdValue idA = TestingData.existingId;
ItemIdValue idB = TestingData.matchedId; ItemIdValue idB = TestingData.matchedId;
ItemUpdate updateA = new ItemUpdateBuilder(idA) ItemUpdate updateA = new ItemUpdateBuilder(idA).addStatement(TestingData.generateStatement(idA, idB)).build();
.addStatement(TestingData.generateStatement(idA, idB)) ItemUpdate updateB = new ItemUpdateBuilder(idB).addStatement(TestingData.generateStatement(idB, idB)).build();
.build();
ItemUpdate updateB = new ItemUpdateBuilder(idB)
.addStatement(TestingData.generateStatement(idB, idB))
.build();
scrutinize(updateA, updateB); scrutinize(updateA, updateB);
assertWarningsRaised(DistinctValuesScrutinizer.type); assertWarningsRaised(DistinctValuesScrutinizer.type);
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -9,19 +32,19 @@ public class FormatScrutinizerTest extends ValueScrutinizerTest {
public EditScrutinizer getScrutinizer() { public EditScrutinizer getScrutinizer() {
return new FormatScrutinizer(); return new FormatScrutinizer();
} }
@Test @Test
public void testTrigger() { public void testTrigger() {
scrutinize(Datamodel.makeStringValue("not a number")); scrutinize(Datamodel.makeStringValue("not a number"));
assertWarningsRaised(FormatScrutinizer.type); assertWarningsRaised(FormatScrutinizer.type);
} }
@Test @Test
public void testNoIssue() { public void testNoIssue() {
scrutinize(Datamodel.makeStringValue("1234")); scrutinize(Datamodel.makeStringValue("1234"));
assertNoWarningRaised(); assertNoWarningRaised();
} }
@Test @Test
public void testIncompleteMatch() { public void testIncompleteMatch() {
scrutinize(Datamodel.makeStringValue("42 is a number")); scrutinize(Datamodel.makeStringValue("42 is a number"));

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import org.openrefine.wikidata.qa.MockConstraintFetcher; import org.openrefine.wikidata.qa.MockConstraintFetcher;
@ -9,7 +32,7 @@ import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue; import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
public class InverseConstaintScrutinizerTest extends StatementScrutinizerTest { public class InverseConstaintScrutinizerTest extends StatementScrutinizerTest {
private ItemIdValue idA = TestingData.existingId; private ItemIdValue idA = TestingData.existingId;
private ItemIdValue idB = TestingData.newIdB; private ItemIdValue idB = TestingData.newIdB;
private PropertyIdValue pidWithInverse = MockConstraintFetcher.pidWithInverse; private PropertyIdValue pidWithInverse = MockConstraintFetcher.pidWithInverse;
@ -19,20 +42,18 @@ public class InverseConstaintScrutinizerTest extends StatementScrutinizerTest {
public EditScrutinizer getScrutinizer() { public EditScrutinizer getScrutinizer() {
return new InverseConstraintScrutinizer(); return new InverseConstraintScrutinizer();
} }
@Test @Test
public void testTrigger() { public void testTrigger() {
ItemUpdate update = new ItemUpdateBuilder(idA) ItemUpdate update = new ItemUpdateBuilder(idA)
.addStatement(TestingData.generateStatement(idA, pidWithInverse, idB)) .addStatement(TestingData.generateStatement(idA, pidWithInverse, idB)).build();
.build();
scrutinize(update); scrutinize(update);
assertWarningsRaised(InverseConstraintScrutinizer.type); assertWarningsRaised(InverseConstraintScrutinizer.type);
} }
@Test @Test
public void testNoSymmetricClosure() { public void testNoSymmetricClosure() {
ItemUpdate update = new ItemUpdateBuilder(idA) ItemUpdate update = new ItemUpdateBuilder(idA).addStatement(TestingData.generateStatement(idA, inversePid, idB))
.addStatement(TestingData.generateStatement(idA, inversePid, idB))
.build(); .build();
scrutinize(update); scrutinize(update);
assertNoWarningRaised(); assertNoWarningRaised();

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.Collections; import java.util.Collections;
@ -11,58 +34,50 @@ import org.wikidata.wdtk.datamodel.interfaces.Claim;
import org.wikidata.wdtk.datamodel.interfaces.Statement; import org.wikidata.wdtk.datamodel.interfaces.Statement;
import org.wikidata.wdtk.datamodel.interfaces.StatementRank; import org.wikidata.wdtk.datamodel.interfaces.StatementRank;
public class NewItemScrutinizerTest extends ScrutinizerTest { public class NewItemScrutinizerTest extends ScrutinizerTest {
private Claim claim = Datamodel.makeClaim(TestingData.newIdA, private Claim claim = Datamodel.makeClaim(TestingData.newIdA,
Datamodel.makeValueSnak(Datamodel.makeWikidataPropertyIdValue("P31"), TestingData.existingId), Datamodel.makeValueSnak(Datamodel.makeWikidataPropertyIdValue("P31"), TestingData.existingId),
Collections.emptyList()); Collections.emptyList());
private Statement p31Statement = Datamodel.makeStatement(claim, Collections.emptyList(), StatementRank.NORMAL, ""); private Statement p31Statement = Datamodel.makeStatement(claim, Collections.emptyList(), StatementRank.NORMAL, "");
@Override @Override
public EditScrutinizer getScrutinizer() { public EditScrutinizer getScrutinizer() {
return new NewItemScrutinizer(); return new NewItemScrutinizer();
} }
@Test @Test
public void testTrigger() { public void testTrigger() {
ItemUpdate update = new ItemUpdateBuilder(TestingData.newIdA).build(); ItemUpdate update = new ItemUpdateBuilder(TestingData.newIdA).build();
scrutinize(update); scrutinize(update);
assertWarningsRaised( assertWarningsRaised(NewItemScrutinizer.noDescType, NewItemScrutinizer.noLabelType,
NewItemScrutinizer.noDescType, NewItemScrutinizer.noTypeType, NewItemScrutinizer.newItemType);
NewItemScrutinizer.noLabelType,
NewItemScrutinizer.noTypeType,
NewItemScrutinizer.newItemType);
} }
@Test @Test
public void testEmptyItem() { public void testEmptyItem() {
ItemUpdate update = new ItemUpdateBuilder(TestingData.existingId).build(); ItemUpdate update = new ItemUpdateBuilder(TestingData.existingId).build();
scrutinize(update); scrutinize(update);
assertNoWarningRaised(); assertNoWarningRaised();
} }
@Test @Test
public void testGoodNewItem() { public void testGoodNewItem() {
ItemUpdate update = new ItemUpdateBuilder(TestingData.newIdA) ItemUpdate update = new ItemUpdateBuilder(TestingData.newIdA)
.addLabel(Datamodel.makeMonolingualTextValue("bonjour", "fr")) .addLabel(Datamodel.makeMonolingualTextValue("bonjour", "fr"))
.addDescription(Datamodel.makeMonolingualTextValue("interesting item", "en")) .addDescription(Datamodel.makeMonolingualTextValue("interesting item", "en")).addStatement(p31Statement)
.addStatement(p31Statement)
.build(); .build();
scrutinize(update); scrutinize(update);
assertWarningsRaised(NewItemScrutinizer.newItemType); assertWarningsRaised(NewItemScrutinizer.newItemType);
} }
@Test @Test
public void testDeletedStatements() { public void testDeletedStatements() {
ItemUpdate update = new ItemUpdateBuilder(TestingData.newIdA) ItemUpdate update = new ItemUpdateBuilder(TestingData.newIdA)
.addLabel(Datamodel.makeMonolingualTextValue("bonjour", "fr")) .addLabel(Datamodel.makeMonolingualTextValue("bonjour", "fr"))
.addDescription(Datamodel.makeMonolingualTextValue("interesting item", "en")) .addDescription(Datamodel.makeMonolingualTextValue("interesting item", "en")).addStatement(p31Statement)
.addStatement(p31Statement) .deleteStatement(TestingData.generateStatement(TestingData.newIdA, TestingData.matchedId)).build();
.deleteStatement(TestingData.generateStatement(TestingData.newIdA,
TestingData.matchedId))
.build();
scrutinize(update); scrutinize(update);
assertWarningsRaised(NewItemScrutinizer.newItemType, NewItemScrutinizer.deletedStatementsType); assertWarningsRaised(NewItemScrutinizer.newItemType, NewItemScrutinizer.deletedStatementsType);
} }

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import org.openrefine.wikidata.testing.TestingData; import org.openrefine.wikidata.testing.TestingData;
@ -10,19 +33,19 @@ public class NoEditsMadeScrutinizerTest extends ScrutinizerTest {
public EditScrutinizer getScrutinizer() { public EditScrutinizer getScrutinizer() {
return new NoEditsMadeScrutinizer(); return new NoEditsMadeScrutinizer();
} }
@Test @Test
public void testTrigger() { public void testTrigger() {
scrutinize(); scrutinize();
assertWarningsRaised(NoEditsMadeScrutinizer.type); assertWarningsRaised(NoEditsMadeScrutinizer.type);
} }
@Test @Test
public void testNonNull() { public void testNonNull() {
scrutinize(new ItemUpdateBuilder(TestingData.newIdA).build()); scrutinize(new ItemUpdateBuilder(TestingData.newIdA).build());
assertNoWarningRaised(); assertNoWarningRaised();
} }
@Test @Test
public void testNull() { public void testNull() {
scrutinize(new ItemUpdateBuilder(TestingData.existingId).build()); scrutinize(new ItemUpdateBuilder(TestingData.existingId).build());

View File

@ -1,3 +1,26 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.qa.scrutinizers; package org.openrefine.wikidata.qa.scrutinizers;
import java.util.Arrays; import java.util.Arrays;
@ -16,6 +39,7 @@ import org.wikidata.wdtk.datamodel.interfaces.Statement;
import org.wikidata.wdtk.datamodel.interfaces.StatementRank; import org.wikidata.wdtk.datamodel.interfaces.StatementRank;
public class QualifierCompatibilityScrutinizerTest extends StatementScrutinizerTest { public class QualifierCompatibilityScrutinizerTest extends StatementScrutinizerTest {
private Snak disallowedQualifier = Datamodel.makeNoValueSnak(MockConstraintFetcher.qualifierPid); private Snak disallowedQualifier = Datamodel.makeNoValueSnak(MockConstraintFetcher.qualifierPid);
private Snak mandatoryQualifier = Datamodel.makeNoValueSnak(MockConstraintFetcher.mandatoryQualifierPid); private Snak mandatoryQualifier = Datamodel.makeNoValueSnak(MockConstraintFetcher.mandatoryQualifierPid);
private Snak allowedQualifier = Datamodel.makeNoValueSnak(MockConstraintFetcher.allowedQualifierPid); private Snak allowedQualifier = Datamodel.makeNoValueSnak(MockConstraintFetcher.allowedQualifierPid);
@ -24,36 +48,36 @@ public class QualifierCompatibilityScrutinizerTest extends StatementScrutinizerT
public EditScrutinizer getScrutinizer() { public EditScrutinizer getScrutinizer() {
return new QualifierCompatibilityScrutinizer(); return new QualifierCompatibilityScrutinizer();
} }
@Test @Test
public void testDisallowedQualifier() { public void testDisallowedQualifier() {
scrutinize(makeStatement(disallowedQualifier,mandatoryQualifier)); scrutinize(makeStatement(disallowedQualifier, mandatoryQualifier));
assertWarningsRaised(QualifierCompatibilityScrutinizer.disallowedQualifiersType); assertWarningsRaised(QualifierCompatibilityScrutinizer.disallowedQualifiersType);
} }
@Test @Test
public void testMissingQualifier() { public void testMissingQualifier() {
scrutinize(makeStatement()); scrutinize(makeStatement());
assertWarningsRaised(QualifierCompatibilityScrutinizer.missingMandatoryQualifiersType); assertWarningsRaised(QualifierCompatibilityScrutinizer.missingMandatoryQualifiersType);
} }
@Test @Test
public void testGoodEdit() { public void testGoodEdit() {
scrutinize(makeStatement(allowedQualifier,mandatoryQualifier)); scrutinize(makeStatement(allowedQualifier, mandatoryQualifier));
assertNoWarningRaised(); assertNoWarningRaised();
} }
private Statement makeStatement(Snak... qualifiers) { private Statement makeStatement(Snak... qualifiers) {
Claim claim = Datamodel.makeClaim(TestingData.existingId, Claim claim = Datamodel.makeClaim(TestingData.existingId,
Datamodel.makeNoValueSnak(MockConstraintFetcher.mainSnakPid), makeQualifiers(qualifiers)); Datamodel.makeNoValueSnak(MockConstraintFetcher.mainSnakPid), makeQualifiers(qualifiers));
return Datamodel.makeStatement(claim, Collections.emptyList(), StatementRank.NORMAL, ""); return Datamodel.makeStatement(claim, Collections.emptyList(), StatementRank.NORMAL, "");
} }
private List<SnakGroup> makeQualifiers(Snak[] qualifiers) { private List<SnakGroup> makeQualifiers(Snak[] qualifiers) {
List<Snak> snaks = Arrays.asList(qualifiers); List<Snak> snaks = Arrays.asList(qualifiers);
return snaks.stream() return snaks.stream().map((Snak q) -> Datamodel.makeSnakGroup(Collections.<Snak> singletonList(q)))
.map((Snak q) -> Datamodel.makeSnakGroup(Collections.<Snak>singletonList(q))) .collect(Collectors.toList());
.collect(Collectors.toList());
} }
} }

Some files were not shown because too many files have changed in this diff Show More