Added conflicts-with constraints (#2641)
Implemented conflicts-with scrutinizer as part of #2354
This commit is contained in:
parent
e960744bab
commit
cf851ee636
@ -104,6 +104,8 @@
|
|||||||
"warnings-messages/remove-statements-with-invalid-format/body": "If these statements currently exist on Wikidata, this will solve constraint violations.",
|
"warnings-messages/remove-statements-with-invalid-format/body": "If these statements currently exist on Wikidata, this will solve constraint violations.",
|
||||||
"warnings-messages/missing-inverse-statements/title": "Inverse statements missing for {added_property_entity}.",
|
"warnings-messages/missing-inverse-statements/title": "Inverse statements missing for {added_property_entity}.",
|
||||||
"warnings-messages/missing-inverse-statements/body": "Any {added_property_entity} statement such as the one from {source_entity} to {target_entity} should also be added in reverse with {inverse_property_entity}: in this case, {target_entity} {inverse_property_entity} {source_entity}.",
|
"warnings-messages/missing-inverse-statements/body": "Any {added_property_entity} statement such as the one from {source_entity} to {target_entity} should also be added in reverse with {inverse_property_entity}: in this case, {target_entity} {inverse_property_entity} {source_entity}.",
|
||||||
|
"warnings-messages/having-conflicts-with-statements/title": "{property_entity} conflicts with statement {added_property_entity}.",
|
||||||
|
"warnings-messages/having-conflicts-with-statements/body": "Remove the conflicting statements with {property_entity} such as item with property {example_entity}.",
|
||||||
"warnings-messages/self-referential-statements/title": "Self-referential statements.",
|
"warnings-messages/self-referential-statements/title": "Self-referential statements.",
|
||||||
"warnings-messages/self-referential-statements/body": "While not forbidden, self-referential statements are generally suspicious. You have some on {example_entity}.",
|
"warnings-messages/self-referential-statements/body": "While not forbidden, self-referential statements are generally suspicious. You have some on {example_entity}.",
|
||||||
"warnings-messages/unsourced-statements/title": "Statements without references.",
|
"warnings-messages/unsourced-statements/title": "Statements without references.",
|
||||||
|
@ -28,6 +28,8 @@ import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
|
|||||||
import org.wikidata.wdtk.datamodel.interfaces.QuantityValue;
|
import org.wikidata.wdtk.datamodel.interfaces.QuantityValue;
|
||||||
import org.wikidata.wdtk.datamodel.interfaces.Value;
|
import org.wikidata.wdtk.datamodel.interfaces.Value;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,6 +144,7 @@ public interface ConstraintFetcher {
|
|||||||
boolean usableOnItems(PropertyIdValue pid);
|
boolean usableOnItems(PropertyIdValue pid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
<<<<<<< HEAD
|
||||||
* Retrieves the lower bound of the range
|
* Retrieves the lower bound of the range
|
||||||
* required in difference-within-range constraint
|
* required in difference-within-range constraint
|
||||||
*
|
*
|
||||||
@ -174,4 +177,14 @@ public interface ConstraintFetcher {
|
|||||||
* with its lower bound property should be in a range?
|
* with its lower bound property should be in a range?
|
||||||
*/
|
*/
|
||||||
boolean hasDiffWithinRange(PropertyIdValue pid);
|
boolean hasDiffWithinRange(PropertyIdValue pid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the Map of all the conflicting pid and their item values
|
||||||
|
*
|
||||||
|
* @param pid:
|
||||||
|
* the property having conflicts-with constraint
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Map<PropertyIdValue, List<Value>> getParamConflictsWith(PropertyIdValue pid);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@ public class EditInspector {
|
|||||||
register(new EnglishDescriptionScrutinizer());
|
register(new EnglishDescriptionScrutinizer());
|
||||||
register(new MultiValueScrutinizer());
|
register(new MultiValueScrutinizer());
|
||||||
register(new DifferenceWithinRangeScrutinizer());
|
register(new DifferenceWithinRangeScrutinizer());
|
||||||
|
register(new ConflictsWithScrutinizer());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,9 +28,7 @@ import org.wikidata.wdtk.datamodel.helpers.Datamodel;
|
|||||||
import org.wikidata.wdtk.datamodel.implementation.QuantityValueImpl;
|
import org.wikidata.wdtk.datamodel.implementation.QuantityValueImpl;
|
||||||
import org.wikidata.wdtk.datamodel.interfaces.*;
|
import org.wikidata.wdtk.datamodel.interfaces.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@ -94,7 +92,10 @@ public class WikidataConstraintFetcher implements ConstraintFetcher {
|
|||||||
public static String ALLOWED_ENTITY_TYPES_QID = "Q52004125";
|
public static String ALLOWED_ENTITY_TYPES_QID = "Q52004125";
|
||||||
public static String ALLOWED_ITEM_TYPE_QID = "Q29934200";
|
public static String ALLOWED_ITEM_TYPE_QID = "Q29934200";
|
||||||
public static String ALLOWED_ENTITY_TYPES_PID = "P2305";
|
public static String ALLOWED_ENTITY_TYPES_PID = "P2305";
|
||||||
|
|
||||||
|
public static String CONFLICTS_WITH_CONSTRAINT_QID = "Q21502838";
|
||||||
|
public static String CONFLICTS_WITH_PROPERTY_PID = "P2306";
|
||||||
|
public static String ITEM_OF_PROPERTY_CONSTRAINT_PID = "P2305";
|
||||||
|
|
||||||
// The following constraints still need to be implemented:
|
// The following constraints still need to be implemented:
|
||||||
|
|
||||||
@ -423,4 +424,36 @@ public class WikidataConstraintFetcher implements ConstraintFetcher {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Map of all the conflicting pid and their item values
|
||||||
|
*
|
||||||
|
* @param pid:
|
||||||
|
* the property having conflicts-with constraint
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<PropertyIdValue, List<Value>> getParamConflictsWith(PropertyIdValue pid) {
|
||||||
|
List<Statement> statementList = getConstraintsByType(pid, CONFLICTS_WITH_CONSTRAINT_QID).collect(Collectors.toList());
|
||||||
|
Map<PropertyIdValue, List<Value>> propertyIdValueListMap = new HashMap<>();
|
||||||
|
for (Statement statement : statementList) {
|
||||||
|
List<SnakGroup> specs = statement.getClaim().getQualifiers();
|
||||||
|
PropertyIdValue conflictingPid = null;
|
||||||
|
List<Value> items = new ArrayList<>();
|
||||||
|
for(SnakGroup group : specs) {
|
||||||
|
for (Snak snak : group.getSnaks()) {
|
||||||
|
if (group.getProperty().getId().equals(CONFLICTS_WITH_PROPERTY_PID)){
|
||||||
|
conflictingPid = (PropertyIdValue) snak.getValue();
|
||||||
|
}
|
||||||
|
if (group.getProperty().getId().equals(ITEM_OF_PROPERTY_CONSTRAINT_PID)){
|
||||||
|
items.add(snak.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (conflictingPid != null) {
|
||||||
|
propertyIdValueListMap.put(conflictingPid, items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return propertyIdValueListMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
package org.openrefine.wikidata.qa.scrutinizers;
|
||||||
|
|
||||||
|
import org.openrefine.wikidata.qa.QAWarning;
|
||||||
|
import org.openrefine.wikidata.updates.ItemUpdate;
|
||||||
|
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
|
||||||
|
import org.wikidata.wdtk.datamodel.interfaces.Statement;
|
||||||
|
import org.wikidata.wdtk.datamodel.interfaces.Value;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ConflictsWithScrutinizer extends EditScrutinizer {
|
||||||
|
|
||||||
|
public static final String type = "having-conflicts-with-statements";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void scrutinize(ItemUpdate update) {
|
||||||
|
Map<PropertyIdValue, Set<Value>> propertyIdValueValueMap = new HashMap<>();
|
||||||
|
for (Statement statement : update.getAddedStatements()){
|
||||||
|
PropertyIdValue pid = statement.getClaim().getMainSnak().getPropertyId();
|
||||||
|
Value value = statement.getClaim().getMainSnak().getValue();
|
||||||
|
Set<Value> values;
|
||||||
|
if (value != null) {
|
||||||
|
if (propertyIdValueValueMap.containsKey(pid)) {
|
||||||
|
values = propertyIdValueValueMap.get(pid);
|
||||||
|
} else {
|
||||||
|
values = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
values.add(value);
|
||||||
|
propertyIdValueValueMap.put(pid, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(PropertyIdValue propertyId : propertyIdValueValueMap.keySet()){
|
||||||
|
Map<PropertyIdValue, List<Value>> conflictingPropertyMap = _fetcher.getParamConflictsWith(propertyId);
|
||||||
|
for (PropertyIdValue conflictingPid : conflictingPropertyMap.keySet()) {
|
||||||
|
if (propertyIdValueValueMap.containsKey(conflictingPid) && raiseWarning(propertyIdValueValueMap, conflictingPid, conflictingPropertyMap)) {
|
||||||
|
QAWarning issue = new QAWarning(type, propertyId.getId()+conflictingPid.getId(), QAWarning.Severity.WARNING, 1);
|
||||||
|
issue.setProperty("property_entity", propertyId);
|
||||||
|
issue.setProperty("added_property_entity", conflictingPid);
|
||||||
|
issue.setProperty("example_entity", update.getItemId());
|
||||||
|
addIssue(issue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean raiseWarning(Map<PropertyIdValue, Set<Value>> propertyIdValueValueMap, PropertyIdValue conflictingPid, Map<PropertyIdValue, List<Value>> conflictingPropertyMap) {
|
||||||
|
if (conflictingPropertyMap.get(conflictingPid).isEmpty()){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Value value : conflictingPropertyMap.get(conflictingPid)) {
|
||||||
|
if (propertyIdValueValueMap.get(conflictingPid).contains(value)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,9 @@ import java.math.BigDecimal;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.wikidata.wdtk.datamodel.interfaces.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class MockConstraintFetcher implements ConstraintFetcher {
|
public class MockConstraintFetcher implements ConstraintFetcher {
|
||||||
@ -66,6 +69,11 @@ public class MockConstraintFetcher implements ConstraintFetcher {
|
|||||||
public static QuantityValue minValuePid = Datamodel.makeQuantityValue(new BigDecimal(0));
|
public static QuantityValue minValuePid = Datamodel.makeQuantityValue(new BigDecimal(0));
|
||||||
public static QuantityValue maxValuePid = Datamodel.makeQuantityValue(new BigDecimal(150));
|
public static QuantityValue maxValuePid = Datamodel.makeQuantityValue(new BigDecimal(150));
|
||||||
|
|
||||||
|
public static PropertyIdValue conflictsWithPid = Datamodel.makeWikidataPropertyIdValue("P50");
|
||||||
|
public static PropertyIdValue pidConflictingStatement = Datamodel.makeWikidataPropertyIdValue("P31");
|
||||||
|
public static Value conflictingStatementValue = Datamodel.makeWikidataItemIdValue("Q5");
|
||||||
|
public static Value conflictsWithValue = Datamodel.makeWikidataItemIdValue("Q36322");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFormatRegex(PropertyIdValue pid) {
|
public String getFormatRegex(PropertyIdValue pid) {
|
||||||
return "[1-9]\\d+";
|
return "[1-9]\\d+";
|
||||||
@ -202,4 +210,11 @@ public class MockConstraintFetcher implements ConstraintFetcher {
|
|||||||
public boolean hasDiffWithinRange(PropertyIdValue pid) {
|
public boolean hasDiffWithinRange(PropertyIdValue pid) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<PropertyIdValue, List<Value>> getParamConflictsWith(PropertyIdValue pid) {
|
||||||
|
Map<PropertyIdValue, List<Value>> propertyIdValueListMap = new HashMap<>();
|
||||||
|
List<Value> items = Arrays.asList(conflictingStatementValue, null);
|
||||||
|
propertyIdValueListMap.put(pidConflictingStatement, items);
|
||||||
|
return propertyIdValueListMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
package org.openrefine.wikidata.qa.scrutinizers;
|
||||||
|
|
||||||
|
import org.openrefine.wikidata.qa.MockConstraintFetcher;
|
||||||
|
import org.openrefine.wikidata.testing.TestingData;
|
||||||
|
import org.openrefine.wikidata.updates.ItemUpdate;
|
||||||
|
import org.openrefine.wikidata.updates.ItemUpdateBuilder;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
import org.wikidata.wdtk.datamodel.helpers.Datamodel;
|
||||||
|
import org.wikidata.wdtk.datamodel.implementation.StatementImpl;
|
||||||
|
import org.wikidata.wdtk.datamodel.interfaces.*;
|
||||||
|
|
||||||
|
public class ConflictsWithScrutinizerTest extends ScrutinizerTest {
|
||||||
|
@Override
|
||||||
|
public EditScrutinizer getScrutinizer() {
|
||||||
|
return new ConflictsWithScrutinizer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTrigger() {
|
||||||
|
ItemIdValue idA = TestingData.existingId;
|
||||||
|
|
||||||
|
PropertyIdValue conflictsWithPid = MockConstraintFetcher.conflictsWithPid;
|
||||||
|
Value conflictsWithValue = MockConstraintFetcher.conflictsWithValue;
|
||||||
|
|
||||||
|
PropertyIdValue propertyWithConflictsPid = MockConstraintFetcher.pidConflictingStatement;
|
||||||
|
Value conflictingValue = MockConstraintFetcher.conflictingStatementValue;
|
||||||
|
|
||||||
|
ValueSnak value1 = Datamodel.makeValueSnak(conflictsWithPid, conflictsWithValue);
|
||||||
|
ValueSnak value2 = Datamodel.makeValueSnak(propertyWithConflictsPid, conflictingValue);
|
||||||
|
|
||||||
|
Statement statement1 = new StatementImpl("P50", value1,idA);
|
||||||
|
Statement statement2 = new StatementImpl("P31", value2,idA);
|
||||||
|
|
||||||
|
ItemUpdate updateA = new ItemUpdateBuilder(idA).addStatement(statement1).addStatement(statement2).build();
|
||||||
|
|
||||||
|
scrutinize(updateA);
|
||||||
|
assertWarningsRaised(ConflictsWithScrutinizer.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoIssue() {
|
||||||
|
ItemIdValue idA = TestingData.existingId;
|
||||||
|
|
||||||
|
PropertyIdValue conflictsWithPid = MockConstraintFetcher.conflictsWithPid;
|
||||||
|
Value conflictsWithValue = MockConstraintFetcher.conflictsWithValue;
|
||||||
|
|
||||||
|
ValueSnak value1 = Datamodel.makeValueSnak(conflictsWithPid, conflictsWithValue);
|
||||||
|
|
||||||
|
Statement statement1 = new StatementImpl("P50", value1,idA);
|
||||||
|
|
||||||
|
ItemUpdate updateA = new ItemUpdateBuilder(idA).addStatement(statement1).build();
|
||||||
|
scrutinize(updateA);
|
||||||
|
assertNoWarningRaised();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoValueSnak() {
|
||||||
|
ItemIdValue idA = TestingData.existingId;
|
||||||
|
|
||||||
|
PropertyIdValue conflictsWithPid = MockConstraintFetcher.conflictsWithPid;
|
||||||
|
Value conflictsWithValue = MockConstraintFetcher.conflictsWithValue;
|
||||||
|
|
||||||
|
PropertyIdValue propertyWithConflictsPid = MockConstraintFetcher.pidConflictingStatement;
|
||||||
|
|
||||||
|
ValueSnak value1 = Datamodel.makeValueSnak(conflictsWithPid, conflictsWithValue);
|
||||||
|
NoValueSnak value2 = Datamodel.makeNoValueSnak(propertyWithConflictsPid);
|
||||||
|
|
||||||
|
Statement statement1 = new StatementImpl("P50", value1,idA);
|
||||||
|
Statement statement2 = new StatementImpl("P31", value2,idA);
|
||||||
|
|
||||||
|
ItemUpdate updateA = new ItemUpdateBuilder(idA).addStatement(statement1).addStatement(statement2).build();
|
||||||
|
|
||||||
|
scrutinize(updateA);
|
||||||
|
assertNoWarningRaised();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user