Add ItemRequires Constraint (#2901)

* Add ItemRequires Constraint

Implemented Item requires constraint as part of #2354

* done with implementation of ItemRequiresScrutinizer Class

* Test class added with suitable test cases
This commit is contained in:
Ekta Mishra 2020-07-14 19:41:25 +05:30 committed by GitHub
parent 233cb95289
commit bf3d7234b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 259 additions and 0 deletions

View File

@ -148,6 +148,14 @@
"warnings-messages/multi-valued-property-is-required-for-new-item/body": "This property is expected to have more than one statement on each item but it has single statement, for instance on {example_entity}.", "warnings-messages/multi-valued-property-is-required-for-new-item/body": "This property is expected to have more than one statement on each item but it has single statement, for instance on {example_entity}.",
"warnings-messages/multi-valued-property-is-required-for-existing-item/title": "{property_entity} should have more than one statement on existing items.", "warnings-messages/multi-valued-property-is-required-for-existing-item/title": "{property_entity} should have more than one statement on existing items.",
"warnings-messages/multi-valued-property-is-required-for-existing-item/body": "This property is expected to have more than one statement on each item but it has single statement, for instance on {example_entity}. If the item already has statements with this property in Wikidata, then this warning can be ignored.", "warnings-messages/multi-valued-property-is-required-for-existing-item/body": "This property is expected to have more than one statement on each item but it has single statement, for instance on {example_entity}. If the item already has statements with this property in Wikidata, then this warning can be ignored.",
"warnings-messages/new-item-requires-property-to-have-certain-values/title": "{property_entity} requires property {added_property_entity} to have certain values.",
"warnings-messages/new-item-requires-property-to-have-certain-values/body": "Property {added_property_entity} doesn't have appropriate values such as on item {example_entity}.",
"warnings-messages/new-item-requires-certain-other-statement/title": "{property_entity} requires statement with property {added_property_entity}.",
"warnings-messages/new-item-requires-certain-other-statement/body": "This property is expected to have another statement with property {added_property_entity} such as on item {example_entity}.",
"warnings-messages/existing-item-requires-property-to-have-certain-values/title": "{property_entity} requires property {added_property_entity} to have certain values.",
"warnings-messages/existing-item-requires-property-to-have-certain-values/body": "Property {added_property_entity} doesn't have appropriate values such as on item {example_entity}. If the item already has statements with property {added_property_entity} and with appropriate values in Wikidata, then this warning can be ignored.",
"warnings-messages/existing-item-requires-certain-other-statement/title": "{property_entity} requires statement with property {added_property_entity}.",
"warnings-messages/existing-item-requires-certain-other-statement/body": "This property is expected to have another statement with property {added_property_entity} such as on item {example_entity}. If the item already has statements with property {added_property_entity} in Wikidata, then this warning can be ignored.",
"warnings-messages/ignored-qualifiers/title": "Some qualifiers were ignored.", "warnings-messages/ignored-qualifiers/title": "Some qualifiers were ignored.",
"warnings-messages/ignored-qualifiers/body": "Qualifier values could not be parsed, so they will not be added to the corresponding statements.", "warnings-messages/ignored-qualifiers/body": "Qualifier values could not be parsed, so they will not be added to the corresponding statements.",
"warnings-messages/ignored-references/title": "Some references were ignored.", "warnings-messages/ignored-references/title": "Some references were ignored.",

View File

@ -72,6 +72,7 @@ public class EditInspector {
register(new MultiValueScrutinizer()); register(new MultiValueScrutinizer());
register(new DifferenceWithinRangeScrutinizer()); register(new DifferenceWithinRangeScrutinizer());
register(new ConflictsWithScrutinizer()); register(new ConflictsWithScrutinizer());
register(new ItemRequiresScrutinizer());
} }
/** /**

View File

@ -0,0 +1,104 @@
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.Snak;
import org.wikidata.wdtk.datamodel.interfaces.SnakGroup;
import org.wikidata.wdtk.datamodel.interfaces.Statement;
import org.wikidata.wdtk.datamodel.interfaces.Value;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ItemRequiresScrutinizer extends EditScrutinizer {
public static final String newItemRequireValuesType = "new-item-requires-property-to-have-certain-values";
public static final String newItemRequirePropertyType = "new-item-requires-certain-other-statement";
public static final String existingItemRequireValuesType = "existing-item-requires-property-to-have-certain-values";
public static final String existingItemRequirePropertyType = "existing-item-requires-certain-other-statement";
public static String ITEM_REQUIRES_CONSTRAINT_QID = "Q21503247";
public static String ITEM_REQUIRES_PROPERTY_PID = "P2306";
public static String ITEM_OF_PROPERTY_CONSTRAINT_PID = "P2305";
class ItemRequiresConstraint {
final PropertyIdValue itemRequiresPid;
final List<Value> itemList;
ItemRequiresConstraint(Statement statement) {
List<SnakGroup> specs = statement.getClaim().getQualifiers();
PropertyIdValue pid = null;
this.itemList = new ArrayList<>();
for(SnakGroup group : specs) {
for (Snak snak : group.getSnaks()) {
if (group.getProperty().getId().equals(ITEM_REQUIRES_PROPERTY_PID)){
pid = (PropertyIdValue) snak.getValue();
}
if (group.getProperty().getId().equals(ITEM_OF_PROPERTY_CONSTRAINT_PID)){
this.itemList.add(snak.getValue());
}
}
}
this.itemRequiresPid = pid;
}
}
@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()) {
List<Statement> constraintDefinitions = _fetcher.getConstraintsByType(propertyId, ITEM_REQUIRES_CONSTRAINT_QID);
for (Statement statement : constraintDefinitions) {
ItemRequiresConstraint constraint = new ItemRequiresConstraint(statement);
PropertyIdValue itemRequiresPid = constraint.itemRequiresPid;
List<Value> itemList = constraint.itemList;
if (!propertyIdValueValueMap.containsKey(itemRequiresPid)) {
QAWarning issue = new QAWarning(update.isNew() ? newItemRequirePropertyType : existingItemRequirePropertyType, propertyId.getId() + itemRequiresPid.getId(), update.isNew() ? QAWarning.Severity.WARNING : QAWarning.Severity.INFO, 1);
issue.setProperty("property_entity", propertyId);
issue.setProperty("added_property_entity", itemRequiresPid);
issue.setProperty("example_entity", update.getItemId());
addIssue(issue);
} else if (raiseWarning(propertyIdValueValueMap, itemRequiresPid, itemList)) {
QAWarning issue = new QAWarning(update.isNew() ? newItemRequireValuesType : existingItemRequireValuesType, propertyId.getId() + itemRequiresPid.getId(), update.isNew() ? QAWarning.Severity.WARNING : QAWarning.Severity.INFO, 1);
issue.setProperty("property_entity", propertyId);
issue.setProperty("added_property_entity", itemRequiresPid);
issue.setProperty("example_entity", update.getItemId());
addIssue(issue);
}
}
}
}
private boolean raiseWarning(Map<PropertyIdValue, Set<Value>> propertyIdValueValueMap, PropertyIdValue itemRequiresPid, List<Value> itemList) {
if (itemList.isEmpty()){
return false;
}
for (Value value : itemList) {
if (propertyIdValueValueMap.get(itemRequiresPid).contains(value)){
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,146 @@
package org.openrefine.wikidata.qa.scrutinizers;
import org.openrefine.wikidata.qa.ConstraintFetcher;
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.ItemIdValue;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Snak;
import org.wikidata.wdtk.datamodel.interfaces.SnakGroup;
import org.wikidata.wdtk.datamodel.interfaces.Statement;
import org.wikidata.wdtk.datamodel.interfaces.Value;
import org.wikidata.wdtk.datamodel.interfaces.ValueSnak;
import java.util.List;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.openrefine.wikidata.qa.scrutinizers.ItemRequiresScrutinizer.ITEM_OF_PROPERTY_CONSTRAINT_PID;
import static org.openrefine.wikidata.qa.scrutinizers.ItemRequiresScrutinizer.ITEM_REQUIRES_CONSTRAINT_QID;
import static org.openrefine.wikidata.qa.scrutinizers.ItemRequiresScrutinizer.ITEM_REQUIRES_PROPERTY_PID;
public class ItemRequiresScrutinizerTest extends ScrutinizerTest {
public static PropertyIdValue propertyIdValue = Datamodel.makeWikidataPropertyIdValue("P157");
public static ItemIdValue itemValue = Datamodel.makeWikidataItemIdValue("Q3187975");
public static ItemIdValue entityIdValue = Datamodel.makeWikidataItemIdValue(ITEM_REQUIRES_CONSTRAINT_QID);
public static PropertyIdValue propertyParameterPID = Datamodel.makeWikidataPropertyIdValue(ITEM_REQUIRES_PROPERTY_PID);
public static PropertyIdValue propertyParameterValue = Datamodel.makeWikidataPropertyIdValue("P1196");
public static PropertyIdValue itemParameterPID = Datamodel.makeWikidataPropertyIdValue(ITEM_OF_PROPERTY_CONSTRAINT_PID);
public static Value requiredValue = Datamodel.makeWikidataItemIdValue("Q149086");
@Override
public EditScrutinizer getScrutinizer() {
return new ItemRequiresScrutinizer();
}
@Test
public void testExistingItemTrigger() {
ItemIdValue idA = TestingData.existingId;
Snak mainSnak = Datamodel.makeValueSnak(propertyIdValue, itemValue);
Statement statement = new StatementImpl("P157", mainSnak, idA);
ItemUpdate updateA = new ItemUpdateBuilder(idA).addStatement(statement).build();
Snak qualifierSnak1 = Datamodel.makeValueSnak(propertyParameterPID, propertyParameterValue);
Snak qualifierSnak2 = Datamodel.makeValueSnak(itemParameterPID, requiredValue);
List<SnakGroup> constraintQualifiers = makeSnakGroupList(qualifierSnak1, qualifierSnak2);
List<Statement> constraintDefinitions = constraintParameterStatementList(entityIdValue, constraintQualifiers);
ConstraintFetcher fetcher = mock(ConstraintFetcher.class);
when(fetcher.getConstraintsByType(propertyIdValue, ITEM_REQUIRES_CONSTRAINT_QID)).thenReturn(constraintDefinitions);
setFetcher(fetcher);
scrutinize(updateA);
assertWarningsRaised(ItemRequiresScrutinizer.existingItemRequirePropertyType);
}
@Test
public void testWrongValue() {
ItemIdValue idA = TestingData.existingId;
Snak mainSnak = Datamodel.makeValueSnak(propertyIdValue, itemValue);
Statement statement = new StatementImpl("P157", mainSnak, idA);
Snak requiredPropertySnak = Datamodel.makeValueSnak(propertyParameterValue, itemValue);
Statement requiredStatement = new StatementImpl("P1196", requiredPropertySnak, idA);
ItemUpdate updateA = new ItemUpdateBuilder(idA).addStatement(statement).addStatement(requiredStatement).build();
Snak qualifierSnak1 = Datamodel.makeValueSnak(propertyParameterPID, propertyParameterValue);
Snak qualifierSnak2 = Datamodel.makeValueSnak(itemParameterPID, requiredValue);
List<SnakGroup> constraintQualifiers = makeSnakGroupList(qualifierSnak1, qualifierSnak2);
List<Statement> constraintDefinitions = constraintParameterStatementList(entityIdValue, constraintQualifiers);
ConstraintFetcher fetcher = mock(ConstraintFetcher.class);
when(fetcher.getConstraintsByType(propertyIdValue, ITEM_REQUIRES_CONSTRAINT_QID)).thenReturn(constraintDefinitions);
setFetcher(fetcher);
scrutinize(updateA);
assertWarningsRaised(ItemRequiresScrutinizer.existingItemRequireValuesType);
}
@Test
public void testCorrectValue() {
ItemIdValue idA = TestingData.existingId;
Snak mainSnak = Datamodel.makeValueSnak(propertyIdValue, itemValue);
Statement statement = new StatementImpl("P157", mainSnak, idA);
Snak requiredPropertySnak = Datamodel.makeValueSnak(propertyParameterValue, requiredValue);
Statement requiredStatement = new StatementImpl("P1196", requiredPropertySnak, idA);
ItemUpdate updateA = new ItemUpdateBuilder(idA).addStatement(statement).addStatement(requiredStatement).build();
Snak qualifierSnak1 = Datamodel.makeValueSnak(propertyParameterPID, propertyParameterValue);
Snak qualifierSnak2 = Datamodel.makeValueSnak(itemParameterPID, requiredValue);
List<SnakGroup> constraintQualifiers = makeSnakGroupList(qualifierSnak1, qualifierSnak2);
List<Statement> constraintDefinitions = constraintParameterStatementList(entityIdValue, constraintQualifiers);
ConstraintFetcher fetcher = mock(ConstraintFetcher.class);
when(fetcher.getConstraintsByType(propertyIdValue, ITEM_REQUIRES_CONSTRAINT_QID)).thenReturn(constraintDefinitions);
setFetcher(fetcher);
scrutinize(updateA);
assertNoWarningRaised();
}
@Test
public void testNewItemTrigger() {
ItemIdValue idA = TestingData.newIdA;
Snak mainSnak = Datamodel.makeValueSnak(propertyIdValue, itemValue);
Statement statement = new StatementImpl("P157", mainSnak, idA);
ItemUpdate updateA = new ItemUpdateBuilder(idA).addStatement(statement).build();
Snak qualifierSnak1 = Datamodel.makeValueSnak(propertyParameterPID, propertyParameterValue);
Snak qualifierSnak2 = Datamodel.makeValueSnak(itemParameterPID, requiredValue);
List<SnakGroup> constraintQualifiers = makeSnakGroupList(qualifierSnak1, qualifierSnak2);
List<Statement> constraintDefinitions = constraintParameterStatementList(entityIdValue, constraintQualifiers);
ConstraintFetcher fetcher = mock(ConstraintFetcher.class);
when(fetcher.getConstraintsByType(propertyIdValue, ITEM_REQUIRES_CONSTRAINT_QID)).thenReturn(constraintDefinitions);
setFetcher(fetcher);
scrutinize(updateA);
assertWarningsRaised(ItemRequiresScrutinizer.newItemRequirePropertyType);
}
@Test
public void testExistingItemNoIssue() {
ItemIdValue id = TestingData.existingId;
ValueSnak mainSnak1 = Datamodel.makeValueSnak(propertyIdValue, itemValue);
ValueSnak mainSnak2 = Datamodel.makeValueSnak(propertyParameterValue, requiredValue);
Statement statement1 = new StatementImpl("P157", mainSnak1,id);
Statement statement2 = new StatementImpl("P1196", mainSnak2,id);
ItemUpdate update = new ItemUpdateBuilder(id).addStatement(statement1).addStatement(statement2).build();
Snak qualifierSnak1 = Datamodel.makeValueSnak(propertyParameterPID, propertyParameterValue);
Snak qualifierSnak2 = Datamodel.makeValueSnak(itemParameterPID, requiredValue);
List<SnakGroup> constraintQualifiers = makeSnakGroupList(qualifierSnak1, qualifierSnak2);
List<Statement> constraintDefinitions = constraintParameterStatementList(entityIdValue, constraintQualifiers);
ConstraintFetcher fetcher = mock(ConstraintFetcher.class);
when(fetcher.getConstraintsByType(propertyIdValue, ITEM_REQUIRES_CONSTRAINT_QID)).thenReturn(constraintDefinitions);
setFetcher(fetcher);
scrutinize(update);
assertNoWarningRaised();
}
}