Skip to content

Commit

Permalink
Merge pull request #4504 from inception-project/feature/4503-SPARQL-q…
Browse files Browse the repository at this point in the history
…uery-UI-for-developers

#4503 - SPARQL query UI for developers
  • Loading branch information
reckart authored Feb 11, 2024
2 parents ebe43a4 + edce131 commit 142518f
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,5 @@ <h5 wicket:id="name" class="card-title"/>
The chosen template is only a starting point for your new project. Can you fully customize the project
after it has been created.
</div>

</wicket:panel>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
<html xmlns:wicket="http://wicket.apache.org">
<body>
<wicket:panel>
<div wicket:id="dialog"/>

<div class="flex-v-container flex-gutter flex-sidebar" style="overflow: auto;">
<div>
<div class="d-flex">
<input wicket:id="searchBar" class="form-control"/>
<button wicket:id="query" class="ms-3 btn btn-outline-secondary">SPARQL</button>
</div>

<div wicket:enclosure="knowledgebases">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
*/
package de.tudarmstadt.ukp.inception.ui.kb;

import static de.tudarmstadt.ukp.inception.support.lambda.LambdaBehavior.visibleWhen;
import static de.tudarmstadt.ukp.inception.ui.kb.KnowledgeBasePage.PAGE_PARAM_KB_NAME;
import static org.apache.wicket.RuntimeConfigurationType.DEVELOPMENT;

import java.util.Arrays;
import java.util.List;
Expand All @@ -27,6 +29,7 @@
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.event.Broadcast;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalDialog;
import org.apache.wicket.feedback.IFeedback;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
Expand All @@ -46,6 +49,7 @@

import de.tudarmstadt.ukp.clarin.webanno.model.Project;
import de.tudarmstadt.ukp.clarin.webanno.ui.core.page.ProjectPageBase;
import de.tudarmstadt.ukp.inception.bootstrap.BootstrapModalDialog;
import de.tudarmstadt.ukp.inception.conceptlinking.config.EntityLinkingProperties;
import de.tudarmstadt.ukp.inception.conceptlinking.service.ConceptLinkingService;
import de.tudarmstadt.ukp.inception.kb.KnowledgeBaseService;
Expand All @@ -57,6 +61,7 @@
import de.tudarmstadt.ukp.inception.kb.graph.KBStatement;
import de.tudarmstadt.ukp.inception.kb.model.KnowledgeBase;
import de.tudarmstadt.ukp.inception.support.lambda.LambdaAjaxFormComponentUpdatingBehavior;
import de.tudarmstadt.ukp.inception.support.lambda.LambdaAjaxLink;
import de.tudarmstadt.ukp.inception.ui.kb.event.AjaxConceptSelectionEvent;
import de.tudarmstadt.ukp.inception.ui.kb.event.AjaxInstanceSelectionEvent;
import de.tudarmstadt.ukp.inception.ui.kb.event.AjaxNewConceptEvent;
Expand Down Expand Up @@ -95,6 +100,7 @@ public class KnowledgeBasePanel
private Model<KBProperty> selectedPropertyHandle = Model.of();
private Model<KBHandle> searchHandleModel = Model.of();

private ModalDialog dialog;
private WebMarkupContainer detailContainer;
private ConceptTreePanel conceptTreePanel;
private PropertyListPanel propertyListPanel;
Expand All @@ -113,17 +119,22 @@ public KnowledgeBasePanel(String id, IModel<Project> aProjectModel,

setOutputMarkupId(true);

dialog = new BootstrapModalDialog("dialog");
add(dialog);

add(new LambdaAjaxLink("query", this::actionQuery).add(
visibleWhen(() -> DEVELOPMENT.equals(getApplication().getConfigurationType()))));

kbModel = aKbModel;

// add the selector for the knowledge bases
DropDownChoice<KnowledgeBase> ddc = new DropDownChoice<KnowledgeBase>("knowledgebases",
LoadableDetachableModel
.of(() -> kbService.getEnabledKnowledgeBases(aProjectModel.getObject())));
var ddc = new DropDownChoice<KnowledgeBase>("knowledgebases", LoadableDetachableModel
.of(() -> kbService.getEnabledKnowledgeBases(aProjectModel.getObject())));

ddc.add(new LambdaAjaxFormComponentUpdatingBehavior("change", t -> {
String kbName = aKbModel.getObject().getName();

PageParameters pageParameters = new PageParameters();
var pageParameters = new PageParameters();
ProjectPageBase.setProjectPageParameter(pageParameters, aProjectModel.getObject());
pageParameters.set(PAGE_PARAM_KB_NAME, kbName);
setResponsePage(KnowledgeBasePage.class, pageParameters);
Expand All @@ -147,6 +158,12 @@ public KnowledgeBasePanel(String id, IModel<Project> aProjectModel,
detailContainer.add(details);
}

private void actionQuery(AjaxRequestTarget aTarget)
{
var content = new SparqlPanel(ModalDialog.CONTENT_ID, kbModel);
dialog.open(content, aTarget);
}

private KnowledgeBaseItemAutoCompleteField createSearchField(String aId,
IModel<KBHandle> aHandleModel, IModel<Project> aProjectModel)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<!--
Licensed to the Technische Universität Darmstadt under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The Technische Universität Darmstadt
licenses this file to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<html xmlns:wicket="http://wicket.apache.org">
<wicket:head>
<style>
.results-table tr:hover {
background-color: #f5f5f5;
}
</style>
</wicket:head>
<body>
<wicket:panel>
<div class="modal-header">
<h5 class="modal-title">
SPARQL Query
</h5>
<button wicket:id="closeDialog" type="button" class="btn-close" aria-label="Close"></button>
</div>
<div class="modal-body overflow-auto" style="width: 79vw;">
<form wicket:id="form" class="container">
<div>
<textarea wicket:id=query class="w-100" rows="10">
</textarea>
</div>
<button wicket:id="runQuery">Query</button>
</form>
<div wicket:id="resultsContainer">
<table wicket:id="results" class="results-table w-100"/>
</div>
</div>
<div class="modal-footer flex-column small">
Great power brings great responsibility.
</div>
</wicket:panel>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Licensed to the Technische Universität Darmstadt under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The Technische Universität Darmstadt
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.tudarmstadt.ukp.inception.ui.kb;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalDialog;
import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackHeadersToolbar;
import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxNavigationToolbar;
import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.LambdaColumn;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.panel.GenericPanel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.spring.injection.annot.SpringBean;

import de.tudarmstadt.ukp.inception.kb.KnowledgeBaseService;
import de.tudarmstadt.ukp.inception.kb.model.KnowledgeBase;
import de.tudarmstadt.ukp.inception.support.lambda.LambdaAjaxLink;
import de.tudarmstadt.ukp.inception.support.lambda.LambdaAjaxSubmitLink;

public class SparqlPanel
extends GenericPanel<KnowledgeBase>
{
private static final long serialVersionUID = -8435410869046010021L;

private @SpringBean KnowledgeBaseService knowledgeBaseService;

private WebMarkupContainer resultsContainer;
private DataTable<Map<String, String>, Void> results;

public SparqlPanel(String aId, IModel<KnowledgeBase> aModel)
{
super(aId, aModel);

queue(new Form<QueryModel>("form", new CompoundPropertyModel<>(new QueryModel())));
queue(new TextArea<>("query"));
queue(new LambdaAjaxSubmitLink<QueryModel>("runQuery", this::actionQuery));
queue(new LambdaAjaxLink("closeDialog", this::actionCancel));
resultsContainer = new WebMarkupContainer("resultsContainer");
resultsContainer.setOutputMarkupId(true);
queue(resultsContainer);
results = new DataTable<>("results", Collections.emptyList(),
new TupleQueryResultDataProvider(), 10);
queue(results);
}

private void actionQuery(AjaxRequestTarget aTarget, Form<QueryModel> aForm)
{
var model = aForm.getModelObject();

var columns = new ArrayList<IColumn<Map<String, String>, Void>>();

var dataProvider = knowledgeBaseService.read(getModelObject(), conn -> {
var tupleQuery = conn.prepareTupleQuery(model.query);

var tupleResult = tupleQuery.evaluate();

for (var bindingName : tupleResult.getBindingNames()) {
columns.add(new LambdaColumn<>(Model.of(bindingName), $ -> $.get(bindingName)));
}

return new TupleQueryResultDataProvider(tupleResult);
});

var table = new DataTable<>("results", columns, dataProvider, 10);
table.setOutputMarkupId(true);
table.addTopToolbar(new AjaxNavigationToolbar(table));
table.addTopToolbar(new AjaxFallbackHeadersToolbar<Void>(table, dataProvider));
results = (DataTable<Map<String, String>, Void>) results.replaceWith(table);

aTarget.add(resultsContainer);
}

protected void actionCancel(AjaxRequestTarget aTarget)
{
findParent(ModalDialog.class).close(aTarget);
}

private static class QueryModel
implements Serializable
{
private static final long serialVersionUID = -1407131006099282400L;

String query;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Licensed to the Technische Universität Darmstadt under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The Technische Universität Darmstadt
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.tudarmstadt.ukp.inception.ui.kb;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.eclipse.rdf4j.query.TupleQueryResult;

public class TupleQueryResultDataProvider
extends SortableDataProvider<Map<String, String>, Void>
implements Serializable
{
private static final long serialVersionUID = -4546564722894830886L;

private List<Map<String, String>> data;

public TupleQueryResultDataProvider()
{
data = Collections.emptyList();
}

public TupleQueryResultDataProvider(TupleQueryResult aTupleResult)
{
data = new ArrayList<Map<String, String>>();

for (var bindingSet : aTupleResult) {
var map = new LinkedHashMap<String, String>();

for (var binding : bindingSet) {
map.put(binding.getName(), String.valueOf(binding.getValue()));
}

data.add(map);
}
}

@Override
public Iterator<? extends Map<String, String>> iterator(long aFirst, long aCount)
{
return data.subList((int) aFirst, (int) (aFirst + aCount)).iterator();
}

@Override
public long size()
{
return data.size();
}

@Override
public IModel<Map<String, String>> model(Map<String, String> aObject)
{
return Model.ofMap(aObject);
}
}

0 comments on commit 142518f

Please sign in to comment.