Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion admin-ui/src/main/java/eu/knowledge/engine/admin/MetadataKB.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,23 @@ public class MetadataKB extends KnowledgeBaseImpl {

private static final Logger LOG = LoggerFactory.getLogger(MetadataKB.class);

private static final String META_GRAPH_PATTERN_STR = "?kb rdf:type ke:KnowledgeBase . ?kb ke:hasName ?name . ?kb ke:hasDescription ?description . ?kb ke:hasKnowledgeInteraction ?ki . ?ki rdf:type ?kiType . ?ki ke:isMeta ?isMeta . ?ki ke:hasCommunicativeAct ?act . ?act rdf:type ke:CommunicativeAct . ?act ke:hasRequirement ?req . ?act ke:hasSatisfaction ?sat . ?req rdf:type ?reqType . ?sat rdf:type ?satType . ?ki ke:hasGraphPattern ?gp . ?gp rdf:type ?patternType . ?gp ke:hasPattern ?pattern .";
private static final String META_GRAPH_PATTERN_STR = """
?kb rdf:type ke:KnowledgeBase .
?kb ke:hasName ?name .
?kb ke:hasDescription ?description .
?kb ke:hasKnowledgeInteraction ?ki .
?ki rdf:type ?kiType .
?ki ke:isMeta ?isMeta .
?ki ke:hasCommunicativeAct ?act .
?act rdf:type ke:CommunicativeAct .
?act ke:hasRequirement ?req .
?act ke:hasSatisfaction ?sat .
?req rdf:type ?reqType .
?sat rdf:type ?satType .
?ki ke:hasGraphPattern ?gp .
?gp rdf:type ?patternType .
?gp ke:hasPattern ?pattern .
""";

private final PrefixMapping prefixes;

Expand Down
1 change: 0 additions & 1 deletion examples/common/answering_kb/.python-version

This file was deleted.

1 change: 0 additions & 1 deletion examples/common/asking_kb/.python-version

This file was deleted.

51 changes: 51 additions & 0 deletions examples/dcat/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# DCAT example
This example demonstrates how the Knowledge Bases (KBs) within a Knowledge Network can be exposed as a [DataService Catalog](https://www.w3.org/TR/vocab-dcat-3/) (DCAT). We reuse the docker compose file from the [multiple runtimes example](../multiple-runtimes/) and add a KB that gathers the metadata about the KBs using DCAT terminology. With specific domain knowledge the KE reasoner is able to automatically transform KE metadata into DCAT catalogue data. The following table shows how Knowledge Engine concepts are mapped to DCAT concepts.

| KE | DCAT |
|----|------|
| ke:KnowledgeBase | dcat:DataService |
| ke:hasName | dcterms:title |
| ke:hasDescription | dcterms:description |

## meta-kb
We use a modified [asking_kb](../common/asking_kb/asking_kb.py) to enable ASKing and printing for DCAT metadata. We use DCAT specific rules derived from the [Knowledge Engine Ontology]{https://github.com/TNO/knowledge-engine/blob/master/smart-connector/src/main/resources/knowledge-engine-ontology.ttl}. We could also use the following (more generic) RFDS rules, but these currently are too slow and require too much memory:

```
// DCAT facts
-> (ke:KnowledgeBase rdfs:subClassOf dcat:DataService ) .
-> (ke:hasName skos:exactMatch dcterms:title ) .
-> (ke:hasDescription skos:exactMatch dcterms:description ) .

// RDFS rules
[exactMatch: (?i ?p1 ?v) (?p1 skos:exactMatch ?p2) -> (?i ?p2 ?v)]
[subClass: (?i rdf:type ?t1) (?t1 rdfs:subClassOf ?t2) -> (?i rdf:type ?t2)]
```

After building & starting the example with `docker compose build` and `docker compose up -d`, you can see the `meta-kb` in action by watching its log: `docker compose logs -f meta-kb`

After a while you should see the following valid DCAT RDF appear in the log:

```
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix ex: <http://example.org/> .

ex:ke_catalog rdf:type dcat:Catalog .
ex:ke_catalog dcterms:title "Knowledge Engine DCAT Catalog" .

<http://example.org/kb2> rdf:type dcat:DataService .
<http://example.org/kb2> dcterms:title "kb2" .
<http://example.org/kb2> dcterms:description "kb2" .
ex:ke_catalog dcat:service <http://example.org/kb2> .

<http://example.org/kb1> rdf:type dcat:DataService .
<http://example.org/kb1> dcterms:title "kb1" .
<http://example.org/kb1> dcterms:description "kb1" .
ex:ke_catalog dcat:service <http://example.org/kb1> .

<http://example.org/kb3> rdf:type dcat:DataService .
<http://example.org/kb3> dcterms:title "kb3" .
<http://example.org/kb3> dcterms:description "kb3" .
ex:ke_catalog dcat:service <http://example.org/kb3> .
```
40 changes: 40 additions & 0 deletions examples/dcat/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
include:
- ../multiple-runtimes/docker-compose.yml

services:
runtime-4:
image: ghcr.io/tno/knowledge-engine/smart-connector:1.4.1-SNAPSHOT
environment:
KE_RUNTIME_PORT: 8081
KE_RUNTIME_EXPOSED_URL: http://runtime-4:8081
KD_URL: http://knowledge-directory:8282
KE_REASONER_LEVEL: 5
JAVA_TOOL_OPTIONS: "-Dorg.slf4j.simpleLogger.log.eu.knowledge.engine=info"
meta-kb:
build: meta-kb
environment:
KE_URL: http://runtime-4:8280/rest
KB_ID: http://example.org/meta-kb
PREFIXES: |
{
"dcat": "http://www.w3.org/ns/dcat#",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"dcterms": "http://purl.org/dc/terms/"
}
GRAPH_PATTERN: |
?ds rdf:type dcat:DataService .
?ds dcterms:title ?title .
?ds dcterms:description ?description .
DOMAIN_KNOWLEDGE: |
@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix ke: <https://w3id.org/knowledge-engine/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .

// KE DCAT rules
[DCATDataService: (?i rdf:type ke:KnowledgeBase) -> (?i rdf:type dcat:DataService)]
[DCATTitle: (?i ke:hasName ?v) -> (?i dcterms:title ?v)]
[DCATDesc: (?i ke:hasDescription ?v) -> (?i dcterms:description ?v)]

16 changes: 16 additions & 0 deletions examples/dcat/meta-kb/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.10.6-alpine

# Create and enable venv
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

RUN pip install --upgrade pip

WORKDIR /app/

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY ./asking_kb.py .

ENTRYPOINT [ "python", "asking_kb.py" ]
134 changes: 134 additions & 0 deletions examples/dcat/meta-kb/asking_kb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import os
import logging
import time
import json
import signal
import requests

from knowledge_mapper.tke_client import TkeClient
from knowledge_mapper.knowledge_base import KnowledgeBaseRegistrationRequest
from knowledge_mapper.knowledge_interaction import (
AskKnowledgeInteraction,
AskKnowledgeInteractionRegistrationRequest,
)

KE_URL = os.getenv("KE_URL")
KB_ID = os.getenv("KB_ID")
KB_NAME = KB_ID.split("/")[-1]
if "PREFIXES" in os.environ:
PREFIXES = json.loads(os.getenv("PREFIXES"))
else:
PREFIXES = None
GRAPH_PATTERN = os.getenv("GRAPH_PATTERN")
DOMAIN_KNOWLEDGE= os.getenv("DOMAIN_KNOWLEDGE")

log = logging.getLogger(KB_NAME)
log.setLevel(logging.INFO)

"""
Make sure that this Python program handles SIGTERM by raising a
KeyboardInterrupt, to end the program.
"""
def handle_sigterm(*args):
raise KeyboardInterrupt()

signal.signal(signal.SIGTERM, handle_sigterm)

def register_domain_knowledge(domain_knowledge):
resp = requests.post(url = KE_URL + "/sc/knowledge", headers = {"Knowledge-Base-Id":KB_ID}, data = DOMAIN_KNOWLEDGE);
if resp.status_code != 200:
log.error(f"Our domain knowledge register should return 200 and not {resp.status_code} with message: " + resp.text);

def register_ask_knowledge_interaction(graph_pattern, prefixes) -> dict:
resp = requests.post(url = KE_URL + "/sc/ki", headers = {"Knowledge-Base-Id":KB_ID}, json = { "knowledgeInteractionType": "AskKnowledgeInteraction", "graphPattern": graph_pattern, "prefixes": prefixes, "includeMetaKIs": "true"});
if resp.status_code != 200:
log.error(f"Our ask KI register should return 200 and not {resp.status_code} with message: " + resp.text);
else:
log.info(f"Registering Ask succesfull!")
return resp.json();

def ask(kiId, bindingSet: dict ) -> dict:
resp = requests.post(
KE_URL + '/sc/ask',
json=bindingSet,
headers={
'Knowledge-Base-Id': KB_ID,
'Knowledge-Interaction-Id': kiId,
}
)

if resp.status_code != 200:
log.error(f"Our ask should return 200 and not {resp.status_code} with message: " + resp.text);
else:
log.info(f"Ask activated succesfully!")

return resp.json()

def bindingset_to_dcatrdf(aGraphPattern, bindingSet: list[dict]):
someRDF = ""

for binding in bindingSet:
partRDF = aGraphPattern
id = binding["ds"]
for aVar, aValue in binding.items():
partRDF = partRDF.replace("?" + aVar, aValue)

partRDF += f"ex:ke_catalog dcat:service {id} .\n"
someRDF += partRDF + "\n"
return someRDF;

def kb_1():
client = TkeClient(KE_URL)
client.connect()
log.info(f"registering KB...")
kb = client.register(
KnowledgeBaseRegistrationRequest(
id=f"{KB_ID}",
name=f"{KB_NAME}",
description=f"{KB_NAME}",
)
)

# register domain knowledge
if DOMAIN_KNOWLEDGE is not None:
register_domain_knowledge(DOMAIN_KNOWLEDGE)
log.info("Domain knowledge registered!")
else:
log.debug(f"No domain knowledge found!")

log.info(f"KB registered!")
log.info(f"registering ASK KI...")

kiId = register_ask_knowledge_interaction(GRAPH_PATTERN, PREFIXES)["knowledgeInteractionId"]

log.info(f"ASK KI ({kiId}) registered!")
result = []
try:
while True:
log.info(f"asking...")
result = ask(kiId, [{}])

bs = result["bindingSet"]

if len(bs) == 0:
log.info(f"asking gave no results; will sleep for 2s...")
else:
rdf = bindingset_to_dcatrdf(GRAPH_PATTERN, bs)
rdf = """@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix ex: <http://example.org/> .

ex:ke_catalog rdf:type dcat:Catalog .
ex:ke_catalog dcterms:title \"Knowledge Engine DCAT Catalog\" .

""" + rdf
log.info(f"Knowledge Network DCAT info: \n{rdf}")
time.sleep(2)
finally:
log.info(f"unregistering...")
kb.unregister()


if __name__ == "__main__":
kb_1()
9 changes: 9 additions & 0 deletions examples/dcat/meta-kb/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
knowledge-mapper==0.0.23
# certifi==2022.9.24 # Installed as dependency for requests
# charset-normalizer==2.1.1 # Installed as dependency for requests
# idna==3.4 # Installed as dependency for requests
# json5==0.9.10 # Installed as dependency for knowledge-mapper
# mysql-connector-python==8.0.30 # Installed as dependency for knowledge-mapper
# protobuf==3.20.1 # Installed as dependency for mysql-connector-python
# requests==2.28.1 # Installed as dependency for knowledge-mapper
# urllib3==1.26.12 # Installed as dependency for requests
5 changes: 5 additions & 0 deletions reasoner/src/main/java/eu/knowledge/engine/reasoner/Rule.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ public CompletableFuture<BindingSet> handle(BindingSet bs) {
}
}

public Rule(String aName, Set<TriplePattern> anAntecedent, Set<TriplePattern> aConsequent) {
this(anAntecedent, aConsequent);
this.setName(aName);
}

public Rule(Set<TriplePattern> anAntecedent, Set<TriplePattern> aConsequent,
TransformBindingSetHandler aBindingSetHandler) {
super(anAntecedent, aConsequent);
Expand Down
Loading
Loading