Skip to content

Commit

Permalink
Merge pull request #4041 from inception-project/feature/4040-Ability-…
Browse files Browse the repository at this point in the history
…to-store-preferences-from-client-side-code

#4040 - Ability to store preferences from client-side code
  • Loading branch information
reckart authored May 30, 2023
2 parents ccc9cf6 + 0f5b49e commit ceb2250
Show file tree
Hide file tree
Showing 47 changed files with 932 additions and 157 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@

import static java.lang.Integer.MIN_VALUE;

import java.io.IOException;
import java.util.Optional;

import org.apache.wicket.model.IModel;

import de.tudarmstadt.ukp.clarin.webanno.api.CasProvider;
import de.tudarmstadt.ukp.clarin.webanno.model.Project;
import de.tudarmstadt.ukp.inception.editor.action.AnnotationActionHandler;
import de.tudarmstadt.ukp.inception.rendering.editorstate.AnnotatorState;
import de.tudarmstadt.ukp.inception.support.xml.sanitizer.PolicyCollection;

public interface AnnotationEditorFactory
{
Expand Down Expand Up @@ -58,4 +62,6 @@ AnnotationEditorBase create(String id, IModel<AnnotatorState> aModel,
* the annotator state
*/
void initState(AnnotatorState aState);

Optional<PolicyCollection> getPolicy() throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@
*/
package de.tudarmstadt.ukp.inception.editor;

import java.io.IOException;
import java.util.Optional;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.Ordered;

import de.tudarmstadt.ukp.inception.support.xml.sanitizer.PolicyCollection;

public abstract class AnnotationEditorFactoryImplBase
implements BeanNameAware, Ordered, AnnotationEditorFactory
{
Expand All @@ -42,4 +47,10 @@ public int getOrder()
{
return Ordered.LOWEST_PRECEDENCE;
}

@Override
public Optional<PolicyCollection> getPolicy() throws IOException
{
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,10 @@ public class BratAnnotationEditor
private DifferentialRenderingSupport diffRenderSupport;

public BratAnnotationEditor(String id, IModel<AnnotatorState> aModel,
final AnnotationActionHandler aActionHandler, final CasProvider aCasProvider)
final AnnotationActionHandler aActionHandler, final CasProvider aCasProvider,
String aEditorFactoryId)
{
super(id, aModel, aActionHandler, aCasProvider);
super(id, aModel, aActionHandler, aCasProvider, aEditorFactoryId);

add(visibleWhen(getModel().map(AnnotatorState::getProject).isPresent()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public String getDisplayName()
public AnnotationEditorBase create(String aId, IModel<AnnotatorState> aModel,
AnnotationActionHandler aActionHandler, CasProvider aCasProvider)
{
return new BratAnnotationEditor(aId, aModel, aActionHandler, aCasProvider);
return new BratAnnotationEditor(aId, aModel, aActionHandler, aCasProvider, getBeanName());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public int accepts(Project aProject, String aFormat)
public AnnotationEditorBase create(String aId, IModel<AnnotatorState> aModel,
AnnotationActionHandler aActionHandler, CasProvider aCasProvider)
{
return new BratAnnotationEditor(aId, aModel, aActionHandler, aCasProvider);
return new BratAnnotationEditor(aId, aModel, aActionHandler, aCasProvider, getBeanName());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public String getDisplayName()
public AnnotationEditorBase create(String aId, IModel<AnnotatorState> aModel,
AnnotationActionHandler aActionHandler, CasProvider aCasProvider)
{
return new BratAnnotationEditor(aId, aModel, aActionHandler, aCasProvider);
return new BratAnnotationEditor(aId, aModel, aActionHandler, aCasProvider, getBeanName());
}

@Override
Expand Down
10 changes: 10 additions & 0 deletions inception/inception-diam-editor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@
<artifactId>inception-support</artifactId>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>

<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,14 @@ public class DiamAnnotationBrowser
private @SpringBean ServletContext servletContext;
private @SpringBean PreferencesService userPrefService;

private final String userPreferencesKey;

private DiamAjaxBehavior diamBehavior;

public DiamAnnotationBrowser(String aId)
public DiamAnnotationBrowser(String aId, String aUserPreferencesKey)
{
super(aId);
userPreferencesKey = aUserPreferencesKey;
}

@Override
Expand Down Expand Up @@ -87,7 +90,8 @@ protected void onConfigure()
"ajaxEndpointUrl", diamBehavior.getCallbackUrl(), //
"wsEndpointUrl", constructEndpointUrl(), //
"topicChannel", viewport.getTopic(), //
"pinnedGroups", managerPrefs.getPinnedGroups());
"pinnedGroups", managerPrefs.getPinnedGroups(), "userPreferencesKey",
userPreferencesKey);

// model will be added as props to Svelte component
setDefaultModel(Model.ofMap(properties));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,24 @@ public class DiamSidebar
{
private static final long serialVersionUID = -3062641971181309172L;

private final String userPreferencesKey;

private DiamAnnotationBrowser browser;

public DiamSidebar(String aId, IModel<AnnotatorState> aModel,
AnnotationActionHandler aActionHandler, CasProvider aCasProvider,
AnnotationPage aAnnotationPage)
AnnotationPage aAnnotationPage, String aUserPreferencesKey)
{
super(aId, aModel, aActionHandler, aCasProvider, aAnnotationPage);

add(browser = new DiamAnnotationBrowser("vis"));
userPreferencesKey = aUserPreferencesKey;
add(browser = new DiamAnnotationBrowser("vis", userPreferencesKey));
}

@OnEvent
public void onDocumentOpenedEvent(DocumentOpenedEvent aEvent)
{
browser = (DiamAnnotationBrowser) browser.replaceWith(new DiamAnnotationBrowser("vis"));
browser = (DiamAnnotationBrowser) browser
.replaceWith(new DiamAnnotationBrowser("vis", userPreferencesKey));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,48 @@
*/
package de.tudarmstadt.ukp.inception.diam.sidebar;

import java.io.IOException;
import java.util.Map;
import java.util.Optional;

import org.apache.wicket.model.IModel;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;

import com.networknt.schema.JsonSchema;

import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon;
import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
import de.tudarmstadt.ukp.clarin.webanno.api.CasProvider;
import de.tudarmstadt.ukp.clarin.webanno.support.JSONUtil;
import de.tudarmstadt.ukp.clarin.webanno.support.WatchedResourceFile;
import de.tudarmstadt.ukp.clarin.webanno.ui.annotation.AnnotationPage;
import de.tudarmstadt.ukp.clarin.webanno.ui.annotation.sidebar.AnnotationSidebarFactory_ImplBase;
import de.tudarmstadt.ukp.clarin.webanno.ui.annotation.sidebar.AnnotationSidebar_ImplBase;
import de.tudarmstadt.ukp.inception.diam.sidebar.config.AnnotationBrowserSidebarAutoConfiguration;
import de.tudarmstadt.ukp.inception.editor.action.AnnotationActionHandler;
import de.tudarmstadt.ukp.inception.preferences.ClientSidePreferencesKey;
import de.tudarmstadt.ukp.inception.preferences.ClientSideUserPreferencesProvider;
import de.tudarmstadt.ukp.inception.rendering.editorstate.AnnotatorState;

@ConditionalOnExpression("${websocket.enabled:true}")
@Component
/**
* <p>
* This class is exposed as a Spring Component via
* {@link AnnotationBrowserSidebarAutoConfiguration#annotationBrowserSidebarFactory}.
* </p>
*/
public class DiamSidebarFactory
extends AnnotationSidebarFactory_ImplBase
implements ClientSideUserPreferencesProvider
{
private WatchedResourceFile<JsonSchema> userPreferencesSchema;

public DiamSidebarFactory()
{
var userPreferencesSchemaFile = getClass()
.getResource("DiamSidebarFactoryUserPreferences.schema.json");
userPreferencesSchema = new WatchedResourceFile<>(userPreferencesSchemaFile,
JSONUtil::loadJsonSchema);
}

@Override
public String getDisplayName()
{
Expand All @@ -58,6 +82,21 @@ public AnnotationSidebar_ImplBase create(String aId, IModel<AnnotatorState> aMod
AnnotationActionHandler aActionHandler, CasProvider aCasProvider,
AnnotationPage aAnnotationPage)
{
return new DiamSidebar(aId, aModel, aActionHandler, aCasProvider, aAnnotationPage);
return new DiamSidebar(aId, aModel, aActionHandler, aCasProvider, aAnnotationPage,
getUserPreferencesKey().get().getClientSideKey());
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Optional<ClientSidePreferencesKey<Map>> getUserPreferencesKey()
{
return Optional.of(
new ClientSidePreferencesKey<>(Map.class, "annotation/annotation-browser-sidebar"));
}

@Override
public Optional<JsonSchema> getUserPreferencesSchema() throws IOException
{
return userPreferencesSchema.get();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft/2020-12/schema#",
"type": "object",
"properties": {
"mode": {
"type": "string",
"enum": ["by-label", "by-position"]
},
"sortByScore": {
"type": "boolean"
},
"recommendationsFirst": {
"type": "boolean"
}
},
"required": ["mode"],
"additionalProperties": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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.diam.sidebar.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import de.tudarmstadt.ukp.inception.diam.sidebar.DiamSidebarFactory;

@ConditionalOnWebApplication
@Configuration
public class AnnotationBrowserSidebarAutoConfiguration
{
@ConditionalOnExpression("${websocket.enabled:true}")
@Bean
public DiamSidebarFactory annotationBrowserSidebarFactory()
{
return new DiamSidebarFactory();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
de.tudarmstadt.ukp.inception.diam.sidebar.config.AnnotationBrowserSidebarAutoConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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.
*/

import { writable } from 'svelte/store'

export const groupingMode = writable('by-label')

export const sortByScore = writable(true)

export const recommendationsFirst = writable(false)
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,15 @@
import { compareOffsets } from "@inception-project/inception-js-api/src/model/Offsets";
import LabelBadge from "./LabelBadge.svelte";
import SpanText from "./SpanText.svelte";
import { compareSpanText, groupBy, uniqueLabels } from "./Utils";
import { compareSpanText, groupBy, renderLabel, uniqueLabels } from "./Utils";
import { sortByScore, recommendationsFirst } from "./AnnotationBrowserState"
export let ajaxClient: DiamAjax;
export let data: AnnotatedText;
export let pinnedGroups: string[];
let groupedAnnotations: Record<string, Annotation[]>;
let sortedLabels: string[];
let sortByScore: boolean = true;
let recommendationsFirst: boolean = false;
$: {
sortedLabels = [...pinnedGroups, ...uniqueLabels(data).filter(v => !pinnedGroups.includes(v))]
Expand All @@ -45,7 +44,7 @@
const spans = data?.spans.values() || []
groupedAnnotations = groupBy(
[...spans, ...relations],
(s) => s.label || ""
(s) => renderLabel(s)
)
for (const items of Object.values(groupedAnnotations)) {
Expand All @@ -60,12 +59,12 @@
const aIsRec = a.vid.toString().startsWith("rec:")
const bIsRec = b.vid.toString().startsWith("rec:")
if (sortByScore && aIsRec && !bIsRec) {
return recommendationsFirst ? -1 : 1;
if ($sortByScore && aIsRec && !bIsRec) {
return $recommendationsFirst ? -1 : 1;
}
if (a instanceof Span && b instanceof Span) {
if (sortByScore && aIsRec && bIsRec) {
if ($sortByScore && aIsRec && bIsRec) {
return b.score - a.score;
}
return (
Expand All @@ -75,7 +74,7 @@
}
if (a instanceof Relation && b instanceof Relation) {
if (sortByScore && aIsRec && bIsRec) {
if ($sortByScore && aIsRec && bIsRec) {
return b.score - a.score;
}
return compareOffsets(
Expand Down Expand Up @@ -110,19 +109,19 @@
type="checkbox"
role="switch"
id="sortByScore"
bind:checked={sortByScore}
bind:checked={$sortByScore}
/>
<label class="form-check-label" for="sortByScore"
>Sort by score</label
>
</div>
<div class="form-check form-switch mx-2" class:d-none={!sortByScore}>
<div class="form-check form-switch mx-2" class:d-none={!$sortByScore}>
<input
class="form-check-input"
type="checkbox"
role="switch"
id="recommendationsFirst"
bind:checked={recommendationsFirst}
bind:checked={$recommendationsFirst}
/>
<label class="form-check-label" for="recommendationsFirst"
>Recommendations first</label
Expand Down
Loading

0 comments on commit ceb2250

Please sign in to comment.