Repository: ontodev/robot
Pull request #1018 (2022-06-25 04:47:20)

[#1017] updating missing_label query to support empty labels
Resolves [#1017]

- [ x ] `docs/` have been added/updated
- [ x ] tests have been added/updated
- [ x ] `mvn verify` says all tests pass
- [ x ] `mvn site` says all JavaDocs correct
- [ x ] `CHANGELOG.md` has been updated

Added a condition checking for empty labels (also across languages) "" into the `missing_label.rq`. 

The test code added is more general than needed to allow adding other query tests easily.

I did some ad-hoc performance tests just on my Mac on DOID, EFO and HP - all showing up to 10% performance degradation with the new query. 

@matentzn if you have a more representative benchmark to push it through, would be great!


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2c54f59..dac14439 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+### Added
+- Checking for empty strings in addition to missing ones in `missing_label.rq` [#1017]
+
 ## [1.9.0] - 2022-06-16
 
 ### Added
diff --git a/docs/report_queries/missing_label.md b/docs/report_queries/missing_label.md
index 4fcd36ec..5eeb0d23 100644
--- a/docs/report_queries/missing_label.md
+++ b/docs/report_queries/missing_label.md
@@ -1,10 +1,10 @@
 # Missing Label
 
-**Problem:** An entity does not have a label. This may cause confusion or misuse. Excludes deprecated entities.
+**Problem:** An entity does not have a label or the label is empty. This may cause confusion or misuse. Excludes deprecated entities.
 
 **OBO Foundry Principle:** [12 - Naming Conventions](http://www.obofoundry.org/principles/fp-012-naming-conventions.html)
 
-**Solution:** Add a label.
+**Solution:** Add a label, or make it non-empty.
 
 ```sparql
 PREFIX owl: <http://www.w3.org/2002/07/owl#>
@@ -15,7 +15,10 @@ PREFIX oboInOwl: <http://www.geneontology.org/formats/oboInOwl#>
 SELECT DISTINCT ?entity ?property ?value WHERE {
   VALUES ?property { rdfs:label }
   ?entity ?any ?o .
-  FILTER NOT EXISTS { ?entity ?property ?value }
+  FILTER NOT EXISTS {
+     ?entity ?property ?value .
+     FILTER(str(?value) != "")
+  }
   FILTER NOT EXISTS { ?entity a owl:Ontology }
   FILTER NOT EXISTS { ?entity owl:deprecated true }
   FILTER NOT EXISTS {
diff --git a/robot-core/src/main/java/org/obolibrary/robot/ReportOperation.java b/robot-core/src/main/java/org/obolibrary/robot/ReportOperation.java
index 1c9c4bbd..943f842e 100644
--- a/robot-core/src/main/java/org/obolibrary/robot/ReportOperation.java
+++ b/robot-core/src/main/java/org/obolibrary/robot/ReportOperation.java
@@ -841,7 +841,7 @@ public class ReportOperation {
    * @return List of Violations
    * @throws IOException on issue parsing query
    */
-  private static List<Violation> getViolations(
+  public static List<Violation> getViolations(
       IOHelper ioHelper,
       Dataset dataset,
       String queryName,
diff --git a/robot-core/src/main/resources/report_queries/missing_label.rq b/robot-core/src/main/resources/report_queries/missing_label.rq
index 5bbcb605..e12c37eb 100644
--- a/robot-core/src/main/resources/report_queries/missing_label.rq
+++ b/robot-core/src/main/resources/report_queries/missing_label.rq
@@ -1,10 +1,10 @@
 # # Missing Label
 #
-# **Problem:** An entity does not have a label. This may cause confusion or misuse. Excludes deprecated entities.
+# **Problem:** An entity does not have a label or the label is empty. This may cause confusion or misuse. Excludes deprecated entities.
 #
 # **OBO Foundry Principle:** [12 - Naming Conventions](http://www.obofoundry.org/principles/fp-012-naming-conventions.html)
 #
-# **Solution:** Add a label.
+# **Solution:** Add a label, or make it non-empty.
 
 PREFIX owl: <http://www.w3.org/2002/07/owl#>
 PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
@@ -14,7 +14,10 @@ PREFIX oboInOwl: <http://www.geneontology.org/formats/oboInOwl#>
 SELECT DISTINCT ?entity ?property ?value WHERE {
   VALUES ?property { rdfs:label }
   ?entity ?any ?o .
-  FILTER NOT EXISTS { ?entity ?property ?value }
+  FILTER NOT EXISTS {
+     ?entity ?property ?value .
+     FILTER(str(?value) != "")
+  }
   FILTER NOT EXISTS { ?entity a owl:Ontology }
   FILTER NOT EXISTS { ?entity owl:deprecated true }
   FILTER NOT EXISTS {


Test patch:
diff --git a/robot-core/src/test/java/org/obolibrary/robot/ReportQueriesTest.java b/robot-core/src/test/java/org/obolibrary/robot/ReportQueriesTest.java
new file mode 100644
index 00000000..7d56760c
--- /dev/null
+++ b/robot-core/src/test/java/org/obolibrary/robot/ReportQueriesTest.java
@@ -0,0 +1,62 @@
+package org.obolibrary.robot;
+
+import java.io.InputStream;
+import java.util.*;
+import java.util.stream.Collectors;
+import org.apache.jena.query.Dataset;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.obolibrary.robot.checks.Violation;
+import org.semanticweb.owlapi.model.OWLOntology;
+
+@RunWith(Parameterized.class)
+public class ReportQueriesTest extends CoreTest {
+
+  @Parameterized.Parameters
+  public static Collection<Object[]> data() {
+    return Arrays.asList(
+        new Object[][] {
+          {
+            "missing_label",
+            "/1017-empty-label-input.owl",
+            new String[] {
+              "http://test.org/test#B", "http://test.org/test#C", "http://test.org/test#D"
+            }
+          }
+        });
+  }
+
+  private final String inputFile;
+  private final String queryName;
+  private final String[] failingEntities;
+
+  public ReportQueriesTest(
+      final String queryFile, final String inputFile, final String[] failingEntities) {
+    this.queryName = queryFile;
+    this.inputFile = inputFile;
+    this.failingEntities = failingEntities;
+  }
+
+  /** Test report queries */
+  @Test
+  public void testReportQuery() throws Exception {
+    final OWLOntology ontology = loadOntology(inputFile);
+    final Dataset dataset = QueryOperation.loadOntologyAsDataset(ontology, false);
+
+    final InputStream is = getClass().getResourceAsStream("/report_queries/" + queryName + ".rq");
+    assert is != null;
+    final String query =
+        new Scanner(is, "UTF-8").useDelimiter("" + Character.LINE_SEPARATOR).next();
+    final List<Violation> violations =
+        ReportOperation.getViolations(
+            new IOHelper(), dataset, queryName, query, Collections.emptyMap());
+
+    final Set<String> expected = new HashSet<>(Arrays.asList(failingEntities));
+    assert violations != null;
+    final Set<String> actual =
+        violations.stream().map(v -> v.entity.getIRI().toString()).collect(Collectors.toSet());
+    Assert.assertEquals(expected, actual);
+  }
+}
diff --git a/robot-core/src/test/resources/1017-empty-label-input.owl b/robot-core/src/test/resources/1017-empty-label-input.owl
new file mode 100644
index 00000000..dc94e69d
--- /dev/null
+++ b/robot-core/src/test/resources/1017-empty-label-input.owl
@@ -0,0 +1,27 @@
+<rdf:RDF
+     xmlns:obo="http://purl.obolibrary.org/obo/"
+     xmlns:owl="http://www.w3.org/2002/07/owl#"
+     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+     xmlns:xml="http://www.w3.org/XML/1998/namespace">
+    <owl:Ontology rdf:about="http://test.org/test"/>
+
+    <owl:Class rdf:about="http://test.org/test#A"/>
+
+    <owl:Class rdf:about="http://test.org/test#B">
+      <obo:IAO_0000115>Sample definition</obo:IAO_0000115>
+    </owl:Class>
+
+    <owl:Class rdf:about="http://test.org/test#C">
+      <rdfs:label></rdfs:label>
+    </owl:Class>
+
+    <owl:Class rdf:about="http://test.org/test#D">
+      <rdfs:label xml:lang="en"></rdfs:label>
+    </owl:Class>
+
+    <owl:Class rdf:about="http://test.org/test#E">
+      <rdfs:label>E</rdfs:label>
+    </owl:Class>
+
+</rdf:RDF>
\ No newline at end of file