Skip to content

Commit

Permalink
#4040 - Ability to store preferences from client-side code
Browse files Browse the repository at this point in the history
* Fix dependencies
* Store the other annotation browser sidebar preferences a well
  • Loading branch information
reckart committed May 30, 2023
1 parent a22e0d2 commit cab66e3
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 75 deletions.
4 changes: 0 additions & 4 deletions inception/inception-api-editor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@
<groupId>de.tudarmstadt.ukp.inception.app</groupId>
<artifactId>inception-api-render</artifactId>
</dependency>
<dependency>
<groupId>de.tudarmstadt.ukp.inception.app</groupId>
<artifactId>inception-preferences</artifactId>
</dependency>
<dependency>
<groupId>de.tudarmstadt.ukp.inception.app</groupId>
<artifactId>inception-model</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
"mode": {
"type": "string",
"enum": ["by-label", "by-position"]
},
"sortByScore": {
"type": "boolean"
},
"recommendationsFirst": {
"type": "boolean"
}
},
"required": ["mode"]
"required": ["mode"],
"additionalProperties": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@
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 @@ -28,15 +28,14 @@
import LabelBadge from "./LabelBadge.svelte";
import SpanText from "./SpanText.svelte";
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 @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -17,80 +17,116 @@
* limitations under the License.
*/
import { onMount, onDestroy } from "svelte"
import { get_current_component } from 'svelte/internal'
import { AnnotatedText, unpackCompactAnnotatedTextV2 } from "@inception-project/inception-js-api"
import { factory } from "@inception-project/inception-diam"
import { groupingMode } from "./AnnotationBrowserState"
import AnnotationsByPositionList from "./AnnotationsByPositionList.svelte"
import AnnotationsByLabelList from "./AnnotationsByLabelList.svelte"
export let wsEndpointUrl: string
export let topicChannel: string
export let ajaxEndpointUrl: string
export let pinnedGroups: string[]
export let userPreferencesKey: string
let connected = false
import { onMount, onDestroy } from "svelte";
import { get_current_component } from "svelte/internal";
import {
AnnotatedText,
unpackCompactAnnotatedTextV2,
} from "@inception-project/inception-js-api";
import { factory } from "@inception-project/inception-diam";
import {
groupingMode,
recommendationsFirst,
sortByScore,
} from "./AnnotationBrowserState";
import AnnotationsByPositionList from "./AnnotationsByPositionList.svelte";
import AnnotationsByLabelList from "./AnnotationsByLabelList.svelte";
export let wsEndpointUrl: string;
export let topicChannel: string;
export let ajaxEndpointUrl: string;
export let pinnedGroups: string[];
export let userPreferencesKey: string;
let connected = false;
let element = null;
let self = get_current_component()
let self = get_current_component();
let defaultPreferences = { 'mode': 'by-label' }
let preferences = Object.assign({}, defaultPreferences)
let modes = {
'by-position': 'Group by position',
'by-label': 'Group by label'
}
let defaultPreferences = {
mode: "by-label",
sortByScore: true,
recommendationsFirst: false,
};
let preferences = Object.assign({}, defaultPreferences);
let modes = {
"by-position": "Group by position",
"by-label": "Group by label",
};
let data: AnnotatedText
let data: AnnotatedText;
let wsClient = factory().createWebsocketClient()
wsClient.onConnect = () => wsClient.subscribeToViewport(topicChannel, (d) => messageRecieved(d))
let wsClient = factory().createWebsocketClient();
wsClient.onConnect = () =>
wsClient.subscribeToViewport(topicChannel, (d) => messageRecieved(d));
let ajaxClient = factory().createAjaxClient(ajaxEndpointUrl)
let ajaxClient = factory().createAjaxClient(ajaxEndpointUrl);
ajaxClient.loadPreferences(userPreferencesKey).then((p) => {
preferences = Object.assign(preferences, defaultPreferences, p)
console.log('Loaded preferences', preferences)
groupingMode.set(preferences.mode || defaultPreferences.mode)
groupingMode.subscribe(mode => {
preferences.mode = mode
ajaxClient.savePreferences(userPreferencesKey, preferences)
})
preferences = Object.assign(preferences, defaultPreferences, p);
console.log("Loaded preferences", preferences);
groupingMode.set(preferences.mode || defaultPreferences.mode);
sortByScore.set(
preferences.sortByScore !== undefined
? preferences.sortByScore
: defaultPreferences.sortByScore
);
recommendationsFirst.set(
preferences.recommendationsFirst !== undefined
? preferences.recommendationsFirst
: defaultPreferences.recommendationsFirst
);
groupingMode.subscribe((mode) => {
preferences.mode = mode;
ajaxClient.savePreferences(userPreferencesKey, preferences);
});
sortByScore.subscribe((mode) => {
preferences.sortByScore = mode;
ajaxClient.savePreferences(userPreferencesKey, preferences);
});
recommendationsFirst.subscribe((mode) => {
preferences.recommendationsFirst = mode;
ajaxClient.savePreferences(userPreferencesKey, preferences);
});
});
export function messageRecieved(d) {
if (!document.body.contains(element)) {
console.debug("Element is not part of the DOM anymore. Disconnecting and suiciding.")
self.$destroy()
return
console.debug(
"Element is not part of the DOM anymore. Disconnecting and suiciding."
);
self.$destroy();
return;
}
data = unpackCompactAnnotatedTextV2(d)
data = unpackCompactAnnotatedTextV2(d);
}
export function connect(): void {
if (connected) return
wsClient.connect(wsEndpointUrl)
if (connected) return;
wsClient.connect(wsEndpointUrl);
}
export function disconnect() {
wsClient.unsubscribeFromViewport()
wsClient.disconnect()
connected = false
wsClient.unsubscribeFromViewport();
wsClient.disconnect();
connected = false;
}
onMount(async () => connect())
onMount(async () => connect());
onDestroy(async () => disconnect())
onDestroy(async () => disconnect());
</script>

<div class="flex-content flex-v-container" bind:this={element}>
<select bind:value={$groupingMode} class="form-select">
{#each Object.keys(modes) as value}<option {value}>{modes[value]}</option>{/each}
{#each Object.keys(modes) as value}<option {value}
>{modes[value]}</option
>{/each}
</select>
{#if $groupingMode=='by-position'}
{#if $groupingMode == "by-position"}
<AnnotationsByPositionList {ajaxClient} {data} />
{:else}
<AnnotationsByLabelList {ajaxClient} {data} {pinnedGroups} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@
package de.tudarmstadt.ukp.inception.diam.editor.actions;

import static de.tudarmstadt.ukp.clarin.webanno.support.JSONUtil.fromValidatedJsonString;
import static org.apache.commons.lang3.StringUtils.isBlank;

import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.request.Request;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.core.annotation.Order;

import de.tudarmstadt.ukp.clarin.webanno.security.UserDao;
Expand Down Expand Up @@ -71,22 +69,22 @@ public String getCommand()
public AjaxResponse handle(AjaxRequestTarget aTarget, Request aRequest)
{
try {
var userPreferencesContext = aRequest.getRequestParameters()
.getParameterValue(PARAM_KEY).toOptionalString();
if (StringUtils.isBlank(userPreferencesContext)) {
var keyParameter = aRequest.getRequestParameters().getParameterValue(PARAM_KEY)
.toOptionalString();
if (isBlank(keyParameter)) {
throw new IllegalArgumentException("No key specified");
}

var bean = clientSiderUserPreferencesProviderRegistry
.getProviderForClientSideKey(userPreferencesContext);
var prefProvider = clientSiderUserPreferencesProviderRegistry
.getProviderForClientSideKey(keyParameter);

if (bean.isEmpty()) {
if (prefProvider.isEmpty()) {
throw new IllegalStateException(
"Client-side user preferences not allowed for given key");
}

var key = bean.get().getUserPreferencesKey();
var schema = bean.get().getUserPreferencesSchema();
var key = prefProvider.get().getUserPreferencesKey();
var schema = prefProvider.get().getUserPreferencesSchema();

if (key.isEmpty() || schema.isEmpty()) {
throw new IllegalStateException(
Expand All @@ -100,9 +98,6 @@ public AjaxResponse handle(AjaxRequestTarget aTarget, Request aRequest)
preferencesService.saveTraitsForUserAndProject(key.get(), sessionOwner, project, data);
return new DefaultAjaxResponse();
}
catch (BeanNotOfRequiredTypeException | NoSuchBeanDefinitionException e) {
return handleError("Client-side user preferences not supported", e);
}
catch (Exception e) {
return handleError("Unable to save client-side user preferences", e);
}
Expand Down
4 changes: 4 additions & 0 deletions inception/inception-preferences/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
Expand Down

0 comments on commit cab66e3

Please sign in to comment.