Merge pull request #1644 from OpenRefine/more-constraints
Add support for new Wikidata constraints
This commit is contained in:
commit
35b08bf254
@ -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."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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>();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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>>());
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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))
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user