Skip to content

Commit

Permalink
Semantic handling fixes; handle observers
Browse files Browse the repository at this point in the history
  • Loading branch information
Ferdinando Villa committed Dec 1, 2024
1 parent a4d7cb0 commit 6be8109
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,9 @@ interface ADMIN {
//// */
//// String REGISTER = "/scope/register/{scopeType}/{scopeId}";
//
//// /**
//// * Dispose of a previously created or registered scope.
//// */
//// String DISPOSE = "/scope/dispose/{scopeId}";

/// / /** / * Dispose of a previously created or registered scope. / */ /
/// String DISPOSE = "/scope/dispose/{scopeId}";
//
// }

Expand Down Expand Up @@ -187,20 +186,20 @@ interface REASONER {
String REASONER_BASE = API_BASE;

/**
* Resolve a concept definition, returning a unique ID for the reasoner, the normalized URN form and
* any metadata.
* Resolve a concept definition passed as a request body, returning a unique ID for the reasoner, the
* normalized URN form and any metadata.
*
* @protocol GET
* @protocol POST
* @service
* @produces {@link Concept}
*/
String RESOLVE_CONCEPT = REASONER_BASE + "/resolve/concept/{definition}";
String RESOLVE_CONCEPT = REASONER_BASE + "/resolve/concept";

/**
* @protocol GET for a string definition encoded in the URL
* @protocol POST for a string definition passed as request body
* @produces {@link Observable}
*/
String RESOLVE_OBSERVABLE = REASONER_BASE + "/resolve/observable/{definition}";
String RESOLVE_OBSERVABLE = REASONER_BASE + "/resolve/observable";

/**
* @protocol POST for a map containing the KimObservable definition as "OBSERVABLE" and possibly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ static ObservationImpl createObservation(Scope scope, Object... resolvables) {
if (definition.containsKey("semantics")) {
observable = scope.getService(Reasoner.class).resolveObservable(definition.get(
"semantics").toString());
if (observable == null) {
scope.error("Invalid semantics in observation definition: " + definition.get("semantics"));
return null;
}
}
if (definition.containsKey("space") || definition.containsKey("time")) {
var geometryBuilder = Geometry.builder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.integratedmodelling.klab.api.services.runtime.extension;

import org.integratedmodelling.klab.api.knowledge.Artifact;

import java.lang.annotation.*;

/**
* Use over a method within a @{@link Library}-annotated class or on a class with a single public method to
* provide an export method to a given media type for a given geometry, data type and/or semantics.
* <p>
* TODO document the argument matching for the methods.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DataExport {

/**
* Mandatory media type to be matched with
*
* @return
*/
String mediaType();

/**
* Geometry of the observation we apply to. Should only state the main dimensions but may also provide
* scale constraints.
*
* @return
*/
String geometry();

/**
* Data type to filter the applicable observations. Alternative to {@link #semantics()}, default means
* anything is accepted.
*
* @return
*/
Artifact.Type dataType() default Artifact.Type.VOID;


/**
* Optional semantics to filter the applicable observation. Must be a quality in the current worldview.
* Should be only needed in very specific situations.
*
* @return
*/
String semantics() default "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.integratedmodelling.klab.api.services.runtime.extension;

import org.integratedmodelling.klab.api.knowledge.Artifact;

import java.lang.annotation.*;

/**
* Use on top of a method within a @{@link Library}-annotated class or on a type with a single public method
* to provide an exporter of an image for a given observation.
* <p>
* TODO document the argument matching for the methods.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DataImage {

/**
* Mandatory media type to be matched with
*
* @return
*/
String mediaType();

/**
* Geometry of the observation we apply to. Should only state the main dimensions but may also provide
* scale constraints.
*
* @return
*/
String geometry();

/**
* Data type to filter the applicable observations. Alternative to {@link #semantics()}, default means
* anything is accepted.
*
* @return
*/
Artifact.Type dataType() default Artifact.Type.VOID;

/**
* If this method is particularly costly to run, provide an estimation between 0 (lowest) and 10 (highest)
* so that the exporter can choose, assuming higher cost means higher quality and the cost is compounded
* with the requested size of the image.
*
* @return
*/
int cost() default 0;

/**
* Optional semantics to filter the applicable observation. Must be a quality in the current worldview.
* Should be only needed in very specific situations.
*
* @return
*/
String semantics() default "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,10 @@ public String computeUrn() {
String ret = "";
boolean complex = false;

if (type.contains(SemanticType.NOTHING)) {
return "owl:Nothing";
}

if (semanticModifier != null) {
ret += (ret.isEmpty() ? "" : " ") + semanticModifier.declaration[0];
complex = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,12 @@ public Capabilities capabilities(Scope scope) {

@Override
public Concept resolveConcept(String definition) {
return client.get(ServicesAPI.REASONER.RESOLVE_CONCEPT, Concept.class, "definition", definition);
return client.post(ServicesAPI.REASONER.RESOLVE_CONCEPT, definition, Concept.class);
}

@Override
public Observable resolveObservable(String definition) {
return client.get(ServicesAPI.REASONER.RESOLVE_OBSERVABLE, Observable.class, "definition",
definition);
return client.post(ServicesAPI.REASONER.RESOLVE_OBSERVABLE, definition, Observable.class);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,8 @@ private String substituteTemplateParameters(String request, Map<String,
for (String key : parameters.keySet()) {
var subst = "{" + key + "}";
if (request.contains(subst)) {
ret = ret.replace(subst, Escape.forURL(parameters.get(key).toString()));
ret = ret.replace(subst, UriUtils.encodeQueryParam(parameters.get(key).toString(),
StandardCharsets.UTF_8));
toRemove.add(key);
}
}
Expand Down Expand Up @@ -1547,7 +1548,7 @@ public static Map<String, Object> makeKeyMap(Object[] parameters) {
return ret;
}

public static <K,V> Map<K,V> removeNullValues(Map<K,V> map) {
public static <K, V> Map<K, V> removeNullValues(Map<K, V> map) {
Set<K> toRemove = new HashSet<>();
for (K key : map.keySet()) {
if (map.get(key) == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ public void observe(Object asset, boolean adding) {

if (observation == null) {
currentContext.error("Cannot create an observation out of " + asset + ": aborting");
return;
}

final boolean observering = isObserver;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ public class ReasonerController {
* @param definition
* @return
*/
@GetMapping(ServicesAPI.REASONER.RESOLVE_CONCEPT)
public @ResponseBody Concept resolveConcept(@PathVariable("definition") String definition) {
@PostMapping(ServicesAPI.REASONER.RESOLVE_CONCEPT)
public @ResponseBody Concept resolveConcept(@RequestBody String definition) {
return reasoner.klabService().resolveConcept(definition);
}

Expand All @@ -63,8 +63,8 @@ public class ReasonerController {
* @param definition
* @return
*/
@GetMapping(ServicesAPI.REASONER.RESOLVE_OBSERVABLE)
public @ResponseBody Observable resolveObservable(@PathVariable("definition") String definition) {
@PostMapping(ServicesAPI.REASONER.RESOLVE_OBSERVABLE)
public @ResponseBody Observable resolveObservable(@RequestBody String definition) {
return reasoner.klabService().resolveObservable(definition);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ public class OWL {
private Ontology mergedReasonerOntology;
private OWLReasoner reasoner;

private static String INTERNAL_REASONER_ONTOLOGY_ID = "k";
// this is the ontology that imports all concepts including the worldview, on which the OWL reasoner operates
public static String INTERNAL_REASONER_ONTOLOGY_ID = "k.reasoner";
// concepts that start without a namespace end up here, which eventually will import the entire worldview
public static String INTERNAL_ONTOLOGY_ID = "k.derived";

static EnumSet<SemanticType> emptyType = EnumSet.noneOf(SemanticType.class);

Expand Down Expand Up @@ -191,7 +194,7 @@ public Ontology getTargetOntology(Ontology fallback, Object... targets) {
}

public Ontology requireOntology(String id) {
return requireOntology(id, DEFAULT_ONTOLOGY_PREFIX);
return requireOntology(id == null ? INTERNAL_ONTOLOGY_ID : id, DEFAULT_ONTOLOGY_PREFIX);
}

public Ontology requireOntology(String id, String prefix) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ public String serviceId() {

@Override
public KimObservable resolveObservable(String definition) {
var parsed = this.workspaceManager.resolveObservable(definition);
var parsed = this.workspaceManager.resolveObservable(removeExcessParentheses(definition));
if (parsed != null) {
boolean errors = false;
for (var notification : parsed.getNotifications()) {
Expand All @@ -576,7 +576,7 @@ public KimConcept.Descriptor describeConcept(String conceptUrn) {

@Override
public KimConcept resolveConcept(String definition) {
var parsed = this.workspaceManager.resolveConcept(definition);
var parsed = this.workspaceManager.resolveConcept(removeExcessParentheses(definition));
if (parsed != null) {
boolean errors = false;
for (var notification : parsed.getNotifications()) {
Expand All @@ -592,6 +592,14 @@ public KimConcept resolveConcept(String definition) {
return null;
}

private String removeExcessParentheses(String definition) {
definition = definition.trim();
while (definition.startsWith("(") && definition.endsWith(")")) {
definition = definition.substring(1, definition.length() - 1);
}
return definition;
}

@Override
public List<ResourceSet> projects(Collection<String> projects, Scope scope) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,76 @@ public KimObservable adaptObservable(ObservableSyntax observableSyntax, String n
public KimConcept adaptSemantics(SemanticSyntax semantics, String namespace, String projectName,
KlabAsset.KnowledgeClass documentClass) {

List<KimConceptImpl> tokens = new ArrayList<>();

for (var token : semantics) {
tokens.add(adaptSemanticToken(token, namespace, projectName, documentClass));
}

if (tokens.isEmpty()) {
return null;
} else if (tokens.size() == 1) {
return tokens.getFirst();
}

Set<SemanticType> type = null;

KimConceptImpl ret = null;
List<KimConcept> roles = new ArrayList<>();
List<KimConcept> traits = new ArrayList<>();

for (var token : tokens) {

if (token.getType().contains(SemanticType.OBSERVABLE)) {
ret = token;
} else if (token.getType().contains(SemanticType.ROLE)) {
roles.add(token);
} else if (token.getType().contains(SemanticType.PREDICATE)) {
traits.add(token);
}
}

if (ret == null) {
// no observable
ret = tokens.getFirst();
traits.remove(ret);
roles.remove(ret);
}

roles.sort(new Comparator<KimConcept>() {
@Override
public int compare(KimConcept o1, KimConcept o2) {
return o1.getUrn().compareTo(o2.getUrn());
}
});
traits.sort(new Comparator<KimConcept>() {
@Override
public int compare(KimConcept o1, KimConcept o2) {
return o1.getUrn().compareTo(o2.getUrn());
}
});

// rebuild urn
StringBuilder urn = new StringBuilder();
for (var role : roles) {
urn.append(urn.isEmpty() ? "" : " ").append(role.getUrn());
}
for (var trait : traits) {
urn.append(urn.isEmpty() ? "" : " ").append(trait.getUrn());
}
urn.append(urn.isEmpty() ? "" : " ").append(ret.getUrn());

// ret.setType(tokens.getFirst().getType());
ret.setUrn(urn.toString());
ret.getTraits().addAll(traits);
ret.getRoles().addAll(roles);

return ret;
}

public KimConceptImpl adaptSemanticToken(SemanticSyntax semantics, String namespace, String projectName,
KlabAsset.KnowledgeClass documentClass) {

KimConceptImpl ret = new KimConceptImpl();

ret.setLength(semantics.getCodeLength());
Expand Down Expand Up @@ -341,7 +411,8 @@ private KlabStatement adaptModel(ModelSyntax model, KimNamespace namespace) {
return ret;
}

private Contextualizable adaptContextualizable(ModelSyntax.Contextualization contextualizable, KimNamespace namespace) {
private Contextualizable adaptContextualizable(ModelSyntax.Contextualization contextualizable,
KimNamespace namespace) {

var ret = new ContextualizableImpl();

Expand Down

0 comments on commit 6be8109

Please sign in to comment.