Merge pull request #1644 from OpenRefine/more-constraints

Add support for new Wikidata constraints
This commit is contained in:
Antonin Delpeuch 2018-06-22 12:21:29 +02:00 committed by GitHub
commit 35b08bf254
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 570 additions and 6 deletions

View File

@ -216,6 +216,30 @@
"ignored-coordinates": {
"title": "Invalid geographic coordinates.",
"body": "Some coordinates are incorrectly formatted, such as <span class=\"wb-issue-preformat\">{example_value}</span>. See the <a href=\"https://www.wikidata.org/wiki/Wikidata:Tools/OpenRefine/Editing/Schema_alignment#Globe_coordinates\" target=\"_blank\">allowed formats</a>."
},
"forbidden-value": {
"title": "Invalid values for {property_entity}",
"body": "Items such as {example_value_entity} added on {example_subject_entity} are not allowed as values for {property_entity}."
},
"bounds-disallowed": {
"title": "Quantity bounds supplied for {property_entity}",
"body": "Values are not expected to have uncertainty bounds, but <span class=\"wb-issue-preformat\">{example_value}</span> added on {example_item_entity} has some. See the <a href=\"https://www.wikidata.org/wiki/Wikidata:Tools/OpenRefine/Editing/Schema_alignment#Quantities\" target=\"_blank\">manual</a> to learn how to fix their format."
},
"values-should-be-integers": {
"title": "Non-integer values of {property_entity}",
"body": "Values are expected to be integers, but <span class=\"wb-issue-preformat\">{example_value}</span> added on {example_item_entity} has a fractional part. See the <a href=\"https://www.wikidata.org/wiki/Wikidata:Tools/OpenRefine/Editing/Schema_alignment#Quantities\" target=\"_blank\">manual</a> to learn how to fix their format."
},
"invalid-unit": {
"title": "{property_entity} with invalid units",
"body": "Units such as {unit_entity} used on {example_item_entity} are invalid for {property_entity}."
},
"no-unit-provided": {
"title": "Unit missing for {property_entity}",
"body": "Values such as <span class=\"wb-issue-preformat\">{example_value}</span> on {example_item_entity} are expected to have units."
},
"invalid-entity-type": {
"title": "{property_entity} used on items",
"body": "Uses of {property_entity} on items such as {example_entity} are invalid."
}
}
}

View File

@ -25,7 +25,9 @@ package org.openrefine.wikidata.qa;
import java.util.Set;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Value;
/**
* An object that fetches constraints about properties.
@ -54,6 +56,11 @@ public interface ConstraintFetcher {
*/
PropertyIdValue getInversePid(PropertyIdValue pid);
/**
* Is this property supposed to be symmetric (its own inverse)?
*/
boolean isSymmetric(PropertyIdValue pid);
/**
* Is this property for values only?
*/
@ -81,14 +88,50 @@ public interface ConstraintFetcher {
*/
Set<PropertyIdValue> mandatoryQualifiers(PropertyIdValue pid);
/**
* Get the set of allowed values for this property (null if no such constraint).
* This set may contain null if one of the allowed values in novalue or somevalue.
*/
Set<Value> allowedValues(PropertyIdValue pid);
/**
* Get the set of disallowed values for this property (null if no such constraint).
* This set may contain null if one of the allowed values in novalue or somevalue.
*/
Set<Value> disallowedValues(PropertyIdValue pid);
/**
* Is this property expected to have at most one value per item?
*/
boolean hasSingleValue(PropertyIdValue pid);
/**
* Is this property expected to have a single best value only?
*/
boolean hasSingleBestValue(PropertyIdValue pid);
/**
* Is this property expected to have distinct values?
*/
boolean hasDistinctValues(PropertyIdValue pid);
/**
* Can statements using this property have uncertainty bounds?
*/
boolean boundsAllowed(PropertyIdValue pid);
/**
* Is this property expected to have integer values only?
*/
boolean integerValued(PropertyIdValue pid);
/**
* Returns the allowed units for this property. If empty, no unit is allowed. If null, any unit is allowed.
*/
Set<ItemIdValue> allowedUnits(PropertyIdValue pid);
/**
* Can this property be used on items?
*/
boolean usableOnItems(PropertyIdValue pid);
}

View File

@ -30,12 +30,15 @@ import java.util.stream.Collectors;
import org.openrefine.wikidata.qa.scrutinizers.DistinctValuesScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.EditScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.EntityTypeScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.FormatScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.InverseConstraintScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.NewItemScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.NoEditsMadeScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.QualifierCompatibilityScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.QuantityScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.RestrictedPositionScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.RestrictedValuesScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.SelfReferentialScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.SingleValueScrutinizer;
import org.openrefine.wikidata.qa.scrutinizers.UnsourcedScrutinizer;
@ -73,6 +76,9 @@ public class EditInspector {
register(new DistinctValuesScrutinizer());
register(new NoEditsMadeScrutinizer());
register(new WhitespaceScrutinizer());
register(new QuantityScrutinizer());
register(new RestrictedValuesScrutinizer());
register(new EntityTypeScrutinizer());
}
/**

View File

@ -30,7 +30,9 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openrefine.wikidata.utils.EntityCache;
import org.wikidata.wdtk.datamodel.helpers.Datamodel;
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.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Snak;
@ -56,6 +58,7 @@ public class WikidataConstraintFetcher implements ConstraintFetcher {
public static String INVERSE_CONSTRAINT_QID = "Q21510855";
public static String INVERSE_PROPERTY_PID = "P2306";
public static String SYMMETRIC_CONSTRAINT_QID = "Q21510862";
public static String USED_ONLY_AS_VALUES_CONSTRAINT_QID = "Q21528958";
@ -69,9 +72,27 @@ public class WikidataConstraintFetcher implements ConstraintFetcher {
public static String MANDATORY_QUALIFIERS_CONSTRAINT_QID = "Q21510856";
public static String MANDATORY_QUALIFIERS_CONSTRAINT_PID = "P2306";
public static String ALLOWED_VALUES_CONSTRAINT_QID = "Q21510859";
public static String ALLOWED_VALUES_CONSTRAINT_PID = "P2305";
public static String DISALLOWED_VALUES_CONSTRAINT_QID = "Q52558054";
public static String DISALLOWED_VALUES_CONSTRAINT_PID = "P2305";
public static String SINGLE_VALUE_CONSTRAINT_QID = "Q19474404";
public static String SINGLE_BEST_VALUE_CONSTRAINT_QID = "Q52060874";
public static String DISTINCT_VALUES_CONSTRAINT_QID = "Q21502410";
public static String NO_BOUNDS_CONSTRAINT_QID = "Q51723761";
public static String INTEGER_VALUED_CONSTRAINT_QID = "Q52848401";
public static String ALLOWED_UNITS_CONSTRAINT_QID = "Q21514353";
public static String ALLOWED_UNITS_CONSTRAINT_PID = "P2305";
public static String ALLOWED_ENTITY_TYPES_QID = "Q52004125";
public static String ALLOWED_ITEM_TYPE_QID = "Q29934200";
public static String ALLOWED_ENTITY_TYPES_PID = "P2305";
// The following constraints still need to be implemented:
public static String TYPE_CONSTRAINT_QID = "Q21503250";
@ -122,7 +143,10 @@ public class WikidataConstraintFetcher implements ConstraintFetcher {
if (specs != null) {
List<Value> properties = findValues(specs, ALLOWED_QUALIFIERS_CONSTRAINT_PID);
return properties.stream().map(e -> (PropertyIdValue) e).collect(Collectors.toSet());
return properties.stream()
.filter(e -> e != null)
.map(e -> (PropertyIdValue) e)
.collect(Collectors.toSet());
}
return null;
}
@ -133,7 +157,10 @@ public class WikidataConstraintFetcher implements ConstraintFetcher {
if (specs != null) {
List<Value> properties = findValues(specs, MANDATORY_QUALIFIERS_CONSTRAINT_PID);
return properties.stream().map(e -> (PropertyIdValue) e).collect(Collectors.toSet());
return properties.stream()
.filter(e -> e != null)
.map(e -> (PropertyIdValue) e)
.collect(Collectors.toSet());
}
return null;
}
@ -143,11 +170,74 @@ public class WikidataConstraintFetcher implements ConstraintFetcher {
return getSingleConstraint(pid, SINGLE_VALUE_CONSTRAINT_QID) != null;
}
@Override
public boolean hasSingleBestValue(PropertyIdValue pid) {
return getSingleConstraint(pid, SINGLE_BEST_VALUE_CONSTRAINT_QID) != null;
}
@Override
public boolean hasDistinctValues(PropertyIdValue pid) {
return getSingleConstraint(pid, DISTINCT_VALUES_CONSTRAINT_QID) != null;
}
@Override
public boolean isSymmetric(PropertyIdValue pid) {
return getSingleConstraint(pid, SYMMETRIC_CONSTRAINT_QID) != null;
}
@Override
public Set<Value> allowedValues(PropertyIdValue pid) {
List<SnakGroup> specs = getSingleConstraint(pid, ALLOWED_VALUES_CONSTRAINT_QID);
if (specs != null) {
List<Value> properties = findValues(specs, ALLOWED_VALUES_CONSTRAINT_PID);
return properties.stream().collect(Collectors.toSet());
}
return null;
}
@Override
public Set<Value> disallowedValues(PropertyIdValue pid) {
List<SnakGroup> specs = getSingleConstraint(pid, DISALLOWED_VALUES_CONSTRAINT_QID);
if (specs != null) {
List<Value> properties = findValues(specs, DISALLOWED_VALUES_CONSTRAINT_PID);
return properties.stream().collect(Collectors.toSet());
}
return null;
}
@Override
public boolean boundsAllowed(PropertyIdValue pid) {
return getSingleConstraint(pid, NO_BOUNDS_CONSTRAINT_QID) == null;
}
@Override
public boolean integerValued(PropertyIdValue pid) {
return getSingleConstraint(pid, INTEGER_VALUED_CONSTRAINT_QID) != null;
}
@Override
public Set<ItemIdValue> allowedUnits(PropertyIdValue pid) {
List<SnakGroup> specs = getSingleConstraint(pid, ALLOWED_UNITS_CONSTRAINT_QID);
if (specs != null) {
List<Value> properties = findValues(specs, ALLOWED_UNITS_CONSTRAINT_PID);
return properties.stream().map(e -> e == null ? null : (ItemIdValue) e).collect(Collectors.toSet());
}
return null;
}
@Override
public boolean usableOnItems(PropertyIdValue pid) {
List<SnakGroup> constraint = getSingleConstraint(pid, ALLOWED_ENTITY_TYPES_QID);
if (constraint != null) {
return findValues(constraint, ALLOWED_ENTITY_TYPES_PID).contains(
Datamodel.makeWikidataItemIdValue(ALLOWED_ITEM_TYPE_QID));
}
return true;
}
/**
* Returns a single constraint for a particular type and a property, or null if
* there is no such constraint
@ -193,7 +283,9 @@ public class WikidataConstraintFetcher implements ConstraintFetcher {
PropertyDocument doc = (PropertyDocument) EntityCache.getEntityDocument(pid);
StatementGroup group = doc.findStatementGroup(WIKIDATA_CONSTRAINT_PID);
if (group != null) {
return group.getStatements();
return group.getStatements().stream()
.filter(s -> s.getValue() != null && s.getValue() instanceof EntityIdValue)
.collect(Collectors.toList());
} else {
return new ArrayList<Statement>();
}

View File

@ -0,0 +1,22 @@
package org.openrefine.wikidata.qa.scrutinizers;
import org.openrefine.wikidata.qa.QAWarning;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Snak;
public class EntityTypeScrutinizer extends SnakScrutinizer {
public final static String type = "invalid-entity-type";
@Override
public void scrutinize(Snak snak, EntityIdValue entityId, boolean added) {
PropertyIdValue pid = snak.getPropertyId();
if(!_fetcher.usableOnItems(pid)) {
QAWarning issue = new QAWarning(type, null, QAWarning.Severity.WARNING, 1);
issue.setProperty("example_entity", entityId);
addIssue(issue);
}
}
}

View File

@ -59,6 +59,9 @@ public class InverseConstraintScrutinizer extends StatementScrutinizer {
return _inverse.get(pid);
} else {
PropertyIdValue inversePid = _fetcher.getInversePid(pid);
if (inversePid == null && _fetcher.isSymmetric(pid)) {
inversePid = pid;
}
_inverse.put(pid, inversePid);
_statements.put(pid, new HashMap<EntityIdValue, Set<EntityIdValue>>());

View File

@ -0,0 +1,70 @@
package org.openrefine.wikidata.qa.scrutinizers;
import java.util.Set;
import java.util.stream.Collectors;
import org.openrefine.wikidata.qa.QAWarning;
import org.wikidata.wdtk.datamodel.helpers.Datamodel;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.QuantityValue;
import org.wikidata.wdtk.datamodel.interfaces.Snak;
/**
* Scrutinizer checking for units and bounds in quantities.
*
* @author Antonin Delpeuch
*
*/
public class QuantityScrutinizer extends SnakScrutinizer {
public static final String boundsDisallowedType = "bounds-disallowed";
public static final String integerConstraintType = "values-should-be-integers";
public static final String invalidUnitType = "invalid-unit";
public static final String noUnitProvidedType = "no-unit-provided";
@Override
public void scrutinize(Snak snak, EntityIdValue entityId, boolean added) {
if (QuantityValue.class.isInstance(snak.getValue()) && added) {
PropertyIdValue pid = snak.getPropertyId();
QuantityValue value = (QuantityValue)snak.getValue();
if(!_fetcher.boundsAllowed(pid) && (value.getUpperBound() != null || value.getLowerBound() != null)) {
QAWarning issue = new QAWarning(boundsDisallowedType, pid.getId(), QAWarning.Severity.IMPORTANT, 1);
issue.setProperty("property_entity", pid);
issue.setProperty("example_value", value.getNumericValue().toString());
issue.setProperty("example_item_entity", entityId);
addIssue(issue);
}
if(_fetcher.integerValued(pid) && value.getNumericValue().scale() > 0) {
QAWarning issue = new QAWarning(integerConstraintType, pid.getId(), QAWarning.Severity.IMPORTANT, 1);
issue.setProperty("property_entity", pid);
issue.setProperty("example_value", value.getNumericValue().toString());
issue.setProperty("example_item_entity", entityId);
addIssue(issue);
}
Set<ItemIdValue> allowedUnits = _fetcher.allowedUnits(pid);
String currentUnit = null;
if (value.getUnit() != null && !value.getUnit().equals("")) {
currentUnit = value.getUnit();
}
if(allowedUnits != null &&
!allowedUnits.stream().map(u -> u != null ? u.getIri() : null)
.collect(Collectors.toSet()).contains(currentUnit)) {
String issueType = currentUnit == null ? noUnitProvidedType : invalidUnitType;
QAWarning issue = new QAWarning(issueType, pid.getId(), QAWarning.Severity.IMPORTANT, 1);
issue.setProperty("property_entity", pid);
issue.setProperty("example_value", value.getNumericValue().toString());
issue.setProperty("example_item_entity", entityId);
if (currentUnit != null) {
issue.setProperty("unit_entity",
// this is a hack but it will not be needed anymore in the upcoming version of Wikidata-Toolkit
Datamodel.makeWikidataItemIdValue(currentUnit.substring(currentUnit.indexOf("Q"))));
}
addIssue(issue);
}
}
}
}

View File

@ -0,0 +1,32 @@
package org.openrefine.wikidata.qa.scrutinizers;
import java.util.Set;
import org.openrefine.wikidata.qa.QAWarning;
import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Snak;
import org.wikidata.wdtk.datamodel.interfaces.Value;
public class RestrictedValuesScrutinizer extends SnakScrutinizer {
public static String type = "forbidden-value";
@Override
public void scrutinize(Snak snak, EntityIdValue entityId, boolean added) {
PropertyIdValue pid = snak.getPropertyId();
Value value = snak.getValue();
Set<Value> allowedValues = _fetcher.allowedValues(pid);
Set<Value> disallowedValues = _fetcher.disallowedValues(pid);
if((allowedValues != null && !allowedValues.contains(value)) ||
(disallowedValues != null && disallowedValues.contains(value))) {
QAWarning issue = new QAWarning(type, pid.getId(), QAWarning.Severity.IMPORTANT, 1);
issue.setProperty("property_entity", pid);
issue.setProperty("example_value_entity", value);
issue.setProperty("example_subject_entity", entityId);
addIssue(issue);
}
}
}

View File

@ -35,6 +35,9 @@ import org.wikidata.wdtk.datamodel.interfaces.Statement;
* For now this scrutinizer only checks for uniqueness at the item level (it
* ignores qualifiers and references).
*
* Given that all ranks are currently set to Normal, this also checks for
* single best values.
*
* @author Antonin Delpeuch
*
*/
@ -54,7 +57,7 @@ public class SingleValueScrutinizer extends EditScrutinizer {
issue.setProperty("property_entity", pid);
issue.setProperty("example_entity", update.getItemId());
addIssue(issue);
} else if (_fetcher.hasSingleValue(pid)) {
} else if (_fetcher.hasSingleValue(pid) || _fetcher.hasSingleBestValue(pid)) {
seenSingleProperties.add(pid);
}
}

View File

@ -63,7 +63,7 @@ public class WbItemVariable extends WbVariableExpr<ItemIdValue> {
throws SkipSchemaExpressionException {
if (cell.recon != null
&& (Judgment.Matched.equals(cell.recon.judgment) || Judgment.New.equals(cell.recon.judgment))) {
if (!cell.recon.identifierSpace.equals(Datamodel.SITE_WIKIDATA)) {
if (cell.recon.identifierSpace == null || !cell.recon.identifierSpace.equals(Datamodel.SITE_WIKIDATA)) {
QAWarning warning = new QAWarning("invalid-identifier-space", null, QAWarning.Severity.INFO, 1);
warning.setProperty("example_cell", cell.value.toString());
ctxt.addWarning(warning);

View File

@ -29,12 +29,15 @@ import java.util.Set;
import java.util.stream.Collectors;
import org.wikidata.wdtk.datamodel.helpers.Datamodel;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Value;
public class MockConstraintFetcher implements ConstraintFetcher {
public static PropertyIdValue pidWithInverse = Datamodel.makeWikidataPropertyIdValue("P350");
public static PropertyIdValue inversePid = Datamodel.makeWikidataPropertyIdValue("P57");
public static PropertyIdValue symmetricPid = Datamodel.makeWikidataPropertyIdValue("P783");
public static PropertyIdValue allowedQualifierPid = Datamodel.makeWikidataPropertyIdValue("P34");
public static PropertyIdValue mandatoryQualifierPid = Datamodel.makeWikidataPropertyIdValue("P97");
@ -42,6 +45,20 @@ public class MockConstraintFetcher implements ConstraintFetcher {
public static PropertyIdValue qualifierPid = Datamodel.makeWikidataPropertyIdValue("P987");
public static PropertyIdValue referencePid = Datamodel.makeWikidataPropertyIdValue("P384");
public static PropertyIdValue allowedValuesPid = Datamodel.makeWikidataPropertyIdValue("P8121");
public static ItemIdValue allowedValueQid = Datamodel.makeWikidataItemIdValue("Q389");
public static PropertyIdValue forbiddenValuesPid = Datamodel.makeWikidataPropertyIdValue("P8141");
public static ItemIdValue forbiddenValueQid = Datamodel.makeWikidataItemIdValue("Q378");
public static PropertyIdValue allowedUnitsPid = Datamodel.makeWikidataPropertyIdValue("P34787");
public static ItemIdValue allowedUnit = Datamodel.makeWikidataItemIdValue("Q7887");
public static PropertyIdValue noUnitsPid = Datamodel.makeWikidataPropertyIdValue("P334211");
public static PropertyIdValue noBoundsPid = Datamodel.makeWikidataPropertyIdValue("P8932");
public static PropertyIdValue integerPid = Datamodel.makeWikidataPropertyIdValue("P389");
public static PropertyIdValue propertyOnlyPid = Datamodel.makeWikidataPropertyIdValue("P372");
@Override
public String getFormatRegex(PropertyIdValue pid) {
return "[1-9]\\d+";
@ -89,9 +106,59 @@ public class MockConstraintFetcher implements ConstraintFetcher {
return true;
}
@Override
public boolean hasSingleBestValue(PropertyIdValue pid) {
return false;
}
@Override
public boolean hasDistinctValues(PropertyIdValue pid) {
return true;
}
@Override
public boolean isSymmetric(PropertyIdValue pid) {
return pid.equals(symmetricPid);
}
@Override
public Set<Value> allowedValues(PropertyIdValue pid) {
if (allowedValuesPid.equals(pid)) {
return Arrays.asList(allowedValueQid, null).stream().collect(Collectors.toSet());
}
return null;
}
@Override
public Set<Value> disallowedValues(PropertyIdValue pid) {
if (forbiddenValuesPid.equals(pid)) {
return Collections.singleton(forbiddenValueQid);
}
return null;
}
@Override
public boolean boundsAllowed(PropertyIdValue pid) {
return !noBoundsPid.equals(pid);
}
@Override
public boolean integerValued(PropertyIdValue pid) {
return integerPid.equals(pid);
}
@Override
public Set<ItemIdValue> allowedUnits(PropertyIdValue pid) {
if(allowedUnitsPid.equals(pid)) {
return Collections.singleton(allowedUnit);
} else if(noUnitsPid.equals(pid)) {
return Collections.singleton(null);
}
return null;
}
@Override
public boolean usableOnItems(PropertyIdValue pid) {
return !propertyOnlyPid.equals(pid);
}
}

View File

@ -0,0 +1,29 @@
package org.openrefine.wikidata.qa.scrutinizers;
import org.openrefine.wikidata.qa.MockConstraintFetcher;
import org.openrefine.wikidata.testing.TestingData;
import org.testng.annotations.Test;
import org.wikidata.wdtk.datamodel.helpers.Datamodel;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
public class EntityTypeScrutinizerTest extends StatementScrutinizerTest {
private static ItemIdValue qid = Datamodel.makeWikidataItemIdValue("Q343");
@Override
public EditScrutinizer getScrutinizer() {
return new EntityTypeScrutinizer();
}
@Test
public void testAllowed() {
scrutinize(TestingData.generateStatement(qid, qid));
assertNoWarningRaised();
}
@Test
public void testDisallowed() {
scrutinize(TestingData.generateStatement(qid, MockConstraintFetcher.propertyOnlyPid, qid));
assertWarningsRaised(EntityTypeScrutinizer.type);
}
}

View File

@ -37,6 +37,7 @@ public class InverseConstaintScrutinizerTest extends StatementScrutinizerTest {
private ItemIdValue idB = TestingData.newIdB;
private PropertyIdValue pidWithInverse = MockConstraintFetcher.pidWithInverse;
private PropertyIdValue inversePid = MockConstraintFetcher.inversePid;
private PropertyIdValue symmetricPid = MockConstraintFetcher.symmetricPid;
@Override
public EditScrutinizer getScrutinizer() {
@ -51,6 +52,14 @@ public class InverseConstaintScrutinizerTest extends StatementScrutinizerTest {
assertWarningsRaised(InverseConstraintScrutinizer.type);
}
@Test
public void testSymmetric() {
ItemUpdate update = new ItemUpdateBuilder(idA)
.addStatement(TestingData.generateStatement(idA, symmetricPid, idB)).build();
scrutinize(update);
assertWarningsRaised(InverseConstraintScrutinizer.type);
}
@Test
public void testNoSymmetricClosure() {
ItemUpdate update = new ItemUpdateBuilder(idA).addStatement(TestingData.generateStatement(idA, inversePid, idB))

View File

@ -0,0 +1,102 @@
package org.openrefine.wikidata.qa.scrutinizers;
import java.math.BigDecimal;
import org.openrefine.wikidata.qa.MockConstraintFetcher;
import org.testng.annotations.Test;
import org.wikidata.wdtk.datamodel.helpers.Datamodel;
import org.wikidata.wdtk.datamodel.interfaces.QuantityValue;
public class QuantityScrutinizerTest extends ValueScrutinizerTest{
private QuantityValue exactValue = Datamodel.makeQuantityValue(
new BigDecimal("1.234"));
private QuantityValue integerValue = Datamodel.makeQuantityValue(
new BigDecimal("132"));
private QuantityValue trailingZeros = Datamodel.makeQuantityValue(
new BigDecimal("132.00"));
private QuantityValue valueWithBounds = Datamodel.makeQuantityValue(
new BigDecimal("1.234"),
new BigDecimal("1.200"),
new BigDecimal("1.545"));
private QuantityValue wrongUnitValue = Datamodel.makeQuantityValue(
new BigDecimal("1.234"), "Q346721");
private QuantityValue goodUnitValue = Datamodel.makeQuantityValue(
new BigDecimal("1.234"), MockConstraintFetcher.allowedUnit.getIri());
@Override
public EditScrutinizer getScrutinizer() {
return new QuantityScrutinizer();
}
@Test
public void testBoundsAllowed() {
scrutinize(valueWithBounds);
assertNoWarningRaised();
}
@Test
public void testBoundsDisallowed() {
scrutinize(MockConstraintFetcher.noBoundsPid, valueWithBounds);
assertWarningsRaised(QuantityScrutinizer.boundsDisallowedType);
}
@Test
public void testFractionalAllowed() {
scrutinize(exactValue);
assertNoWarningRaised();
}
@Test
public void testFractionalDisallowed() {
scrutinize(MockConstraintFetcher.integerPid, exactValue);
assertWarningsRaised(QuantityScrutinizer.integerConstraintType);
}
@Test
public void testTrailingZeros() {
scrutinize(MockConstraintFetcher.integerPid, trailingZeros);
assertWarningsRaised(QuantityScrutinizer.integerConstraintType);
}
@Test
public void testInteger() {
scrutinize(MockConstraintFetcher.integerPid, integerValue);
assertNoWarningRaised();
}
@Test
public void testUnitReqired() {
scrutinize(MockConstraintFetcher.allowedUnitsPid, integerValue);
assertWarningsRaised(QuantityScrutinizer.noUnitProvidedType);
}
@Test
public void testWrongUnit() {
scrutinize(MockConstraintFetcher.allowedUnitsPid, wrongUnitValue);
assertWarningsRaised(QuantityScrutinizer.invalidUnitType);
}
@Test
public void testGoodUnit() {
scrutinize(MockConstraintFetcher.allowedUnitsPid, goodUnitValue);
assertNoWarningRaised();
}
@Test
public void testUnitForbidden() {
scrutinize(MockConstraintFetcher.noUnitsPid, goodUnitValue);
assertWarningsRaised(QuantityScrutinizer.invalidUnitType);
}
@Test
public void testNoUnit() {
scrutinize(MockConstraintFetcher.noUnitsPid, integerValue);
assertNoWarningRaised();
}
}

View File

@ -0,0 +1,58 @@
package org.openrefine.wikidata.qa.scrutinizers;
import org.openrefine.wikidata.qa.MockConstraintFetcher;
import org.openrefine.wikidata.testing.TestingData;
import org.testng.annotations.Test;
import org.wikidata.wdtk.datamodel.helpers.Datamodel;
import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
public class RestrictedValuesScrutinizerTest extends SnakScrutinizerTest {
private ItemIdValue qid = Datamodel.makeWikidataItemIdValue("Q3487");
@Override
public EditScrutinizer getScrutinizer() {
return new RestrictedValuesScrutinizer();
}
@Test
public void testNoConstraint() {
scrutinize(TestingData.generateStatement(qid,
Datamodel.makeWikidataPropertyIdValue("P28732"),
qid));
assertNoWarningRaised();
}
@Test
public void testAllowedValue() {
scrutinize(TestingData.generateStatement(qid,
MockConstraintFetcher.allowedValuesPid,
MockConstraintFetcher.allowedValueQid));
assertNoWarningRaised();
}
@Test
public void testAllowedValueFailing() {
scrutinize(TestingData.generateStatement(qid,
MockConstraintFetcher.allowedValuesPid,
qid));
assertWarningsRaised(RestrictedValuesScrutinizer.type);
}
@Test
public void testDisallowedValue() {
scrutinize(TestingData.generateStatement(qid,
MockConstraintFetcher.forbiddenValuesPid,
qid));
assertNoWarningRaised();
}
@Test
public void testDisallowedValueFailing() {
scrutinize(TestingData.generateStatement(qid,
MockConstraintFetcher.forbiddenValuesPid,
MockConstraintFetcher.forbiddenValueQid));
assertWarningsRaised(RestrictedValuesScrutinizer.type);
}
}

View File

@ -35,7 +35,11 @@ public abstract class ValueScrutinizerTest extends SnakScrutinizerTest {
public static final PropertyIdValue defaultPid = Datamodel.makeWikidataPropertyIdValue("P328");
public void scrutinize(Value value) {
scrutinize(Datamodel.makeValueSnak(defaultPid, value));
scrutinize(defaultPid, value);
}
public void scrutinize(PropertyIdValue pid, Value value) {
scrutinize(Datamodel.makeValueSnak(pid, value));
}
public void scrutinizeLabel(MonolingualTextValue text) {