From 010cfb243c17b6e2a763777938c16fea84ecc2fc Mon Sep 17 00:00:00 2001
From: McModknower
Date: Mon, 1 Jul 2024 06:36:44 +0200
Subject: [PATCH] Feat: Add PyCramNlpPrinter
---
.../knowledge/owl2anything/Owl2Anything.java | 1 +
.../owl2anything/converter/OwlConverter.java | 27 +++++---
.../owl2anything/converter/OwlRecord.java | 20 ++++++
.../owl2anything/output/PyCramNlpPrinter.java | 69 +++++++++++++++++++
4 files changed, 106 insertions(+), 11 deletions(-)
create mode 100644 src/main/java/com/malte3d/suturo/knowledge/owl2anything/output/PyCramNlpPrinter.java
diff --git a/src/main/java/com/malte3d/suturo/knowledge/owl2anything/Owl2Anything.java b/src/main/java/com/malte3d/suturo/knowledge/owl2anything/Owl2Anything.java
index 5f701c4..871c821 100644
--- a/src/main/java/com/malte3d/suturo/knowledge/owl2anything/Owl2Anything.java
+++ b/src/main/java/com/malte3d/suturo/knowledge/owl2anything/Owl2Anything.java
@@ -46,5 +46,6 @@ public static void main(String[] args) {
CramKnowRobPrinter.print(owlRecords, new File(outputDir, "cram_knowrob.txt"));
PyCramKnowRobPrinter.print(owlRecords, new File(outputDir, "pycram_knowrob.py"));
PyCramRoboKudoPrinter.print(owlRecords, new File(outputDir, "pycram_robokudo.py"));
+ PyCramNlpPrinter.print(owlRecords, new File(outputDir, "pycram_nlp.py"));
}
}
diff --git a/src/main/java/com/malte3d/suturo/knowledge/owl2anything/converter/OwlConverter.java b/src/main/java/com/malte3d/suturo/knowledge/owl2anything/converter/OwlConverter.java
index 3496f4e..b9db59d 100644
--- a/src/main/java/com/malte3d/suturo/knowledge/owl2anything/converter/OwlConverter.java
+++ b/src/main/java/com/malte3d/suturo/knowledge/owl2anything/converter/OwlConverter.java
@@ -7,17 +7,7 @@
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.io.FileDocumentSource;
-import org.semanticweb.owlapi.model.IRI;
-import org.semanticweb.owlapi.model.MissingImportHandlingStrategy;
-import org.semanticweb.owlapi.model.OWLAnnotation;
-import org.semanticweb.owlapi.model.OWLAnnotationValue;
-import org.semanticweb.owlapi.model.OWLClass;
-import org.semanticweb.owlapi.model.OWLLiteral;
-import org.semanticweb.owlapi.model.OWLOntology;
-import org.semanticweb.owlapi.model.OWLOntologyCreationException;
-import org.semanticweb.owlapi.model.OWLOntologyIRIMapper;
-import org.semanticweb.owlapi.model.OWLOntologyLoaderConfiguration;
-import org.semanticweb.owlapi.model.OWLOntologyManager;
+import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory;
@@ -30,6 +20,7 @@
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import uk.ac.manchester.cs.owl.owlapi.OWLAnnotationPropertyImpl;
+import uk.ac.manchester.cs.owl.owlapi.OWLDataPropertyImpl;
/**
* The main class for converting an OWL ontology to a custom format.
@@ -93,6 +84,7 @@ public List extractRecords() {
String naturalName = extractNaturalName(owlClass);
String description = extractDescription(owlClass);
List perceptionIds = extractPerceptionId(owlClass);
+ List predefinedNames = extractPredefinedNames(owlClass);
// Skip "Nothing"
if ("http://www.w3.org/2002/07/owl#Nothing".equals(owlClass.getIRI().getIRIString()))
@@ -107,6 +99,7 @@ public List extractRecords() {
.naturalName(naturalName)
.description(description)
.perceptionId(null)
+ .predefinedNames(predefinedNames)
.build();
owlRecords.add(owlRecord);
@@ -122,6 +115,7 @@ public List extractRecords() {
.naturalName(naturalName)
.description(description)
.perceptionId(perceptionId)
+ .predefinedNames(predefinedNames)
.build();
owlRecords.add(owlRecord);
@@ -137,6 +131,17 @@ public List extractRecords() {
return owlRecords;
}
+ private List extractPredefinedNames(OWLClass owlClass) {
+ OWLDataProperty hasPredefinedName = new OWLDataPropertyImpl(IRI.create("http://www.ease-crc.org/ont/SUTURO.owl#hasPredefinedName"));
+ return EntitySearcher.getSuperClasses(owlClass, allOntologies.stream())
+ .filter(cls -> cls.getClassExpressionType() == ClassExpressionType.DATA_HAS_VALUE)
+ .map(cls -> (OWLDataHasValue) cls)
+ .filter(cls -> cls.getProperty().equals(hasPredefinedName))
+ .map(HasFiller::getFiller)
+ .map(OWLLiteral::getLiteral)
+ .toList();
+ }
+
/**
* Extracts the perception id from the annotation "suturo:perceptionId"
*
diff --git a/src/main/java/com/malte3d/suturo/knowledge/owl2anything/converter/OwlRecord.java b/src/main/java/com/malte3d/suturo/knowledge/owl2anything/converter/OwlRecord.java
index d53c26f..4b2c9d2 100644
--- a/src/main/java/com/malte3d/suturo/knowledge/owl2anything/converter/OwlRecord.java
+++ b/src/main/java/com/malte3d/suturo/knowledge/owl2anything/converter/OwlRecord.java
@@ -5,6 +5,8 @@
import lombok.Value;
import lombok.With;
+import java.util.List;
+
/**
* Combines extracted information from an OWL Class.
*/
@@ -53,6 +55,12 @@ public class OwlRecord implements Comparable {
*/
Size defaultSize;
+ /**
+ * SUTURO predefined names, used for nlp names
+ */
+ @NonNull
+ List predefinedNames;
+
/**
* @return The combination of {@link #iriNamespaceShort} and {@link #iriName}
*
@@ -64,6 +72,18 @@ public String getIriShortForm() {
return iriNamespaceShort + ":'" + iriName + "'";
}
+ /**
+ * @return The combination of {@link #iriNamespace} and {@link #iriName}
+ *
+ * Example:
http://www.ease-crc.org/ont/SUTURO.owl#Apple
+ *
+ */
+ @SuppressWarnings("JavadocLinkAsPlainText")
+ @NonNull
+ public String getIriLongForm() {
+ return iriNamespace + iriName;
+ }
+
/**
* Can be used to sort a list of {@link OwlRecord}es by their {@link #iriName}.
*/
diff --git a/src/main/java/com/malte3d/suturo/knowledge/owl2anything/output/PyCramNlpPrinter.java b/src/main/java/com/malte3d/suturo/knowledge/owl2anything/output/PyCramNlpPrinter.java
new file mode 100644
index 0000000..2b5738c
--- /dev/null
+++ b/src/main/java/com/malte3d/suturo/knowledge/owl2anything/output/PyCramNlpPrinter.java
@@ -0,0 +1,69 @@
+package com.malte3d.suturo.knowledge.owl2anything.output;
+
+import com.google.gson.Gson;
+import com.malte3d.suturo.knowledge.owl2anything.converter.OwlRecord;
+import lombok.NonNull;
+import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.TreeMap;
+
+/**
+ * Utility class to create a .py file for the Rasa - PyCRAM - KnowRob interface
+ */
+@Slf4j
+@UtilityClass
+public class PyCramNlpPrinter {
+
+ private static final Gson GSON = new Gson().newBuilder()
+ .enableComplexMapKeySerialization()
+ .disableHtmlEscaping()
+ .setPrettyPrinting()
+ .create();
+
+ public static void print(@NonNull List classes, @NonNull File outputFile) {
+
+ TreeMap nlpNames = new TreeMap<>();
+
+ for(var record : classes) {
+ String iri = record.getIriLongForm();
+
+ List mapping = record.getPredefinedNames();
+
+ if (mapping.isEmpty()) {
+ // the nlp naming script already generates warnings, and excludes some classes
+ // so warning about this would only generate useless noise.
+ // log.warn("Class {} has no nlp mapping", iriName);
+ continue;
+ }
+
+ for(String nlpName : mapping) {
+ // In case of two IRI classes that use the same name, a warning will be given and the lexically smaller class IRI will be used.
+ String currentIri = nlpNames.get(nlpName);
+ if (currentIri == null)
+ nlpNames.put(nlpName, iri);
+ else {
+ String taken = iri.compareTo(currentIri) < 0 ? iri : currentIri;
+ log.warn("'{}' and '{}' both map to '{}', using '{}'", currentIri, iri, nlpName, taken);
+ nlpNames.put(nlpName, taken);
+ }
+ }
+ }
+
+ try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8)) {
+
+ writer.append("mapping = ").append(GSON.toJson(nlpNames));
+
+ log.info("Successfully created {}", outputFile.getName());
+
+ } catch (IOException e) {
+ log.error("Error while writing the Cram Object Names file", e);
+ }
+ }
+}