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); + } + } +}