From 283661956b01249f4834148c060de2867d33c93d Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Tue, 9 Jan 2018 14:13:42 +0000 Subject: [PATCH] Add support for snak location constraints --- .../wikidata/module/langs/translation-en.json | 24 +++++ .../wikidata/qa/ConstraintFetcher.java | 35 ++++++++ .../openrefine/wikidata/qa/EditInspector.java | 2 + .../RestrictedPositionScrutinizer.java | 90 +++++++++++++++++++ .../qa/scrutinizers/SnakScrutinizer.java | 2 +- .../wikidata/qa/ConstraintFetcherTests.java | 18 ++++ 6 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizer.java diff --git a/extensions/wikidata/module/langs/translation-en.json b/extensions/wikidata/module/langs/translation-en.json index 5e3dea44f..4d5da5540 100644 --- a/extensions/wikidata/module/langs/translation-en.json +++ b/extensions/wikidata/module/langs/translation-en.json @@ -53,6 +53,30 @@ "unsourced-statements": { "title": "Statements without references.", "body": "Most statements should have references. You can add them easily in the schema." + }, + "property-restricted-to-reference-found-in-mainsnak": { + "title": "Reference-only property used as statement.", + "body": "You are using in a statement a property that was designed to be used in references only." + }, + "property-restricted-to-reference-found-in-qualifier": { + "title": "Reference-only property used as qualifier.", + "body": "You are using in a qualifier a property that was designed to be used in references only." + }, + "property-restricted-to-qualifier-found-in-mainsnak": { + "title": "Qualifier-only property used as statement.", + "body": "You are using in a statement a property that was designed to be used in qualifiers only." + }, + "property-restricted-to-qualifier-found-in-reference": { + "title": "Qualifier-only property used as reference.", + "body": "You are using in a reference a property that was designed to be used in qualifiers only." + }, + "property-restricted-to-mainsnak-found-in-qualifier": { + "title": "Statement-only property used as qualifier.", + "body": "You are using in a qualifier a property that was designed to be used as a statement only." + }, + "property-restricted-to-mainsnak-found-in-reference": { + "title": "Statement-only property used as reference.", + "body": "You are using in a reference a property that was designed to be used as a statement only." } } } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/ConstraintFetcher.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/ConstraintFetcher.java index 6aae88489..86d84db06 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/ConstraintFetcher.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/ConstraintFetcher.java @@ -34,6 +34,20 @@ public class ConstraintFetcher { public static String INVERSE_CONSTRAINT_QID = "Q21510855"; public static String INVERSE_PROPERTY_PID = "P2306"; + public static String USED_ONLY_AS_VALUES_CONSTRAINT_QID = "Q21528958"; + + public static String USED_ONLY_AS_QUALIFIER_CONSTRAINT_QID = "Q21510863"; + + public static String USED_ONLY_AS_REFERENCE_CONSTRAINT_QID = "Q21528959"; + + // The following constraints still need to be implemented: + + public static String ALLOWED_QUALIFIERS_CONSRAINT_QID = "Q21510851"; + public static String ALLOWED_QUALIFIERS_PID = "P2306"; + + public static String MANDATORY_QUALIFIERS_CONSTRAINT_QID = "Q21510856"; + public static String MANDATORY_QUALIFIERS_CONSTRAINT_PID = "P2306"; + public static String SINGLE_VALUE_CONSRAINT_QID = "Q19474404"; public static String DISTINCT_VALUES_CONSRAINT_QID = "Q21502410"; public static String TYPE_CONSTRAINT_QID = "Q21503250"; @@ -73,6 +87,27 @@ public class ConstraintFetcher { return null; } + /** + * Is this property for values only? + */ + public boolean isForValuesOnly(String pid) { + return getSingleConstraint(pid, USED_ONLY_AS_VALUES_CONSTRAINT_QID) != null; + } + + /** + * Is this property for qualifiers only? + */ + public boolean isForQualifiersOnly(String pid) { + return getSingleConstraint(pid, USED_ONLY_AS_QUALIFIER_CONSTRAINT_QID) != null; + } + + /** + * Is this property for references only? + */ + public boolean isForReferencesOnly(String pid) { + return getSingleConstraint(pid, USED_ONLY_AS_REFERENCE_CONSTRAINT_QID) != null; + } + /** * Returns a single constraint for a particular type and a property, or null * if there is no such constraint diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/EditInspector.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/EditInspector.java index eece89ee9..90d1fd17b 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/EditInspector.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/EditInspector.java @@ -8,6 +8,7 @@ import org.openrefine.wikidata.qa.scrutinizers.EditScrutinizer; import org.openrefine.wikidata.qa.scrutinizers.FormatConstraintScrutinizer; import org.openrefine.wikidata.qa.scrutinizers.InverseConstraintScrutinizer; import org.openrefine.wikidata.qa.scrutinizers.NewItemScrutinizer; +import org.openrefine.wikidata.qa.scrutinizers.RestrictedPositionScrutinizer; import org.openrefine.wikidata.qa.scrutinizers.SelfReferentialScrutinizer; import org.openrefine.wikidata.qa.scrutinizers.UnsourcedScrutinizer; import org.openrefine.wikidata.schema.ItemUpdate; @@ -31,6 +32,7 @@ public class EditInspector { register(new InverseConstraintScrutinizer()); register(new SelfReferentialScrutinizer()); register(new UnsourcedScrutinizer()); + register(new RestrictedPositionScrutinizer()); } /** diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizer.java new file mode 100644 index 000000000..d2a21f856 --- /dev/null +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizer.java @@ -0,0 +1,90 @@ +package org.openrefine.wikidata.qa.scrutinizers; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.openrefine.wikidata.qa.ConstraintFetcher; +import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; +import org.wikidata.wdtk.datamodel.interfaces.Reference; +import org.wikidata.wdtk.datamodel.interfaces.Snak; +import org.wikidata.wdtk.datamodel.interfaces.Statement; + + +public class RestrictedPositionScrutinizer extends StatementScrutinizer { + + protected enum SnakPosition { + MAINSNAK, + QUALIFIER, + REFERENCE + } + + private Map _restrictedPids; + private Set _unrestrictedPids; + private ConstraintFetcher _fetcher; + + public RestrictedPositionScrutinizer() { + _restrictedPids = new HashMap<>(); + _unrestrictedPids = new HashSet<>(); + _fetcher = new ConstraintFetcher(); + } + + SnakPosition positionRestriction(String pid) { + if(_unrestrictedPids.contains(pid)) { + return null; + } + SnakPosition restriction = _restrictedPids.get(pid); + if (restriction != null) { + return restriction; + } else { + if (_fetcher.isForValuesOnly(pid)) { + restriction = SnakPosition.MAINSNAK; + } else if (_fetcher.isForQualifiersOnly(pid)) { + restriction = SnakPosition.QUALIFIER; + } else if (_fetcher.isForReferencesOnly(pid)) { + restriction = SnakPosition.REFERENCE; + } + + // Cache these results: + if (restriction != null) { + _restrictedPids.put(pid, restriction); + } else { + _unrestrictedPids.add(pid); + } + return restriction; + } + } + + @Override + public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) { + // Skip the main snak + scrutinize(statement.getClaim().getMainSnak(), entityId, SnakPosition.MAINSNAK, added); + + // Qualifiers + scrutinizeSnakSet(statement.getClaim().getAllQualifiers(), entityId, SnakPosition.QUALIFIER, added); + + // References + for(Reference ref : statement.getReferences()) { + scrutinizeSnakSet(ref.getAllSnaks(), entityId, SnakPosition.REFERENCE, added); + } + } + + protected void scrutinizeSnakSet(Iterator snaks, EntityIdValue entityId, SnakPosition position, boolean added) { + while(snaks.hasNext()) { + Snak snak = snaks.next(); + scrutinize(snak, entityId, position, added); + } + } + + public void scrutinize(Snak snak, EntityIdValue entityId, SnakPosition position, boolean added) { + SnakPosition restriction = positionRestriction(snak.getPropertyId().getId()); + if (restriction != null && position != restriction) { + String positionStr = position.toString().toLowerCase(); + String restrictionStr = restriction.toString().toLowerCase(); + warning("property-restricted-to-"+restrictionStr+"-found-in-"+positionStr); + } + } + +} diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SnakScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SnakScrutinizer.java index 7cd1cb446..7b56f0f4a 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SnakScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SnakScrutinizer.java @@ -38,7 +38,7 @@ public abstract class SnakScrutinizer extends StatementScrutinizer { } } - private void scrutinizeSnakSet(Iterator snaks, EntityIdValue entityId, boolean added) { + protected void scrutinizeSnakSet(Iterator snaks, EntityIdValue entityId, boolean added) { while(snaks.hasNext()) { Snak snak = snaks.next(); scrutinize(snak, entityId, added); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/ConstraintFetcherTests.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/ConstraintFetcherTests.java index f26094b1a..70c855109 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/ConstraintFetcherTests.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/ConstraintFetcherTests.java @@ -27,4 +27,22 @@ public class ConstraintFetcherTests { public void testGetInverseConstraint() { Assert.assertEquals(fetcher.getInversePid("P361"), "P527"); } + + @Test + public void testOnlyReferences() { + Assert.assertTrue(fetcher.isForReferencesOnly("P854")); + Assert.assertFalse(fetcher.isForReferencesOnly("P2241")); + } + + @Test + public void testOnlyQualifiers() { + Assert.assertTrue(fetcher.isForQualifiersOnly("P2241")); + Assert.assertFalse(fetcher.isForQualifiersOnly("P6")); + } + + @Test + public void testOnlyValues() { + Assert.assertTrue(fetcher.isForValuesOnly("P6")); + Assert.assertFalse(fetcher.isForValuesOnly("P854")); + } }