Skip to content

Commit

Permalink
#4616 - Option to disable displaying suggestions in sidebar curation …
Browse files Browse the repository at this point in the history
…mode

- Added options in the project settings to enable/disable displaying suggestions when in curation mode or when looking at annotations from other users
- Added a status badge to the icon of the recommender sidebar
- Switch icons of the recommedner sidebar and active learning sidebar
- Disable recommender and active learning sidebar when recommenders are disabled in curation mode etc.
  • Loading branch information
reckart committed Mar 10, 2024
1 parent 7da064b commit 2671e87
Show file tree
Hide file tree
Showing 28 changed files with 514 additions and 75 deletions.
4 changes: 4 additions & 0 deletions inception/inception-active-learning/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@
<groupId>de.tudarmstadt.ukp.inception.app</groupId>
<artifactId>inception-support</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-support-bootstrap</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import de.tudarmstadt.ukp.inception.active.learning.log.ActiveLearningSuggestionOfferedAdapter;
import de.tudarmstadt.ukp.inception.active.learning.sidebar.ActiveLearningSidebarFactory;
import de.tudarmstadt.ukp.inception.documents.api.DocumentService;
import de.tudarmstadt.ukp.inception.preferences.PreferencesService;
import de.tudarmstadt.ukp.inception.recommendation.api.LearningRecordService;
import de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService;
import de.tudarmstadt.ukp.inception.recommendation.config.RecommenderServiceAutoConfiguration;
Expand Down Expand Up @@ -73,7 +74,8 @@ public ActiveLearningSuggestionOfferedAdapter activeLearningSuggestionOfferedAda

@Bean
public ActiveLearningSidebarFactory activeLearningSidebarFactory(
RecommendationService aRecommendationService)
RecommendationService aRecommendationService, PreferencesService aPreferencesService,
UserDao aUserService)
{
return new ActiveLearningSidebarFactory(aRecommendationService);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
</div>
<div class="scrolling card-body flex-v-container">

<span wicket:id="notAvailableNotice" class="flex-content no-data-notice">
<wicket:message key="notAvailableNotice"/>
</span>

<form wicket:id="sessionControlForm">
<div class="input-group">
<select class="form-select flex-content" wicket:id="selectLayer" data-container="body"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
import static de.tudarmstadt.ukp.clarin.webanno.api.casstorage.CasAccessMode.SHARED_READ_ONLY_ACCESS;
import static de.tudarmstadt.ukp.clarin.webanno.api.casstorage.CasUpgradeMode.AUTO_CAS_UPGRADE;
import static de.tudarmstadt.ukp.inception.active.learning.sidebar.ActiveLearningUserStateMetaData.CURRENT_AL_USER_STATE;
import static de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService.KEY_RECOMMENDER_GENERAL_SETTINGS;
import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_REJECTED;
import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_SKIPPED;
import static de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordUserAction.ACCEPTED;
import static de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordUserAction.CORRECTED;
import static de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordUserAction.REJECTED;
import static de.tudarmstadt.ukp.inception.support.lambda.LambdaBehavior.visibleWhen;
import static de.tudarmstadt.ukp.inception.support.lambda.LambdaBehavior.visibleWhenNot;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
Expand Down Expand Up @@ -92,6 +94,7 @@
import de.tudarmstadt.ukp.inception.bootstrap.BootstrapModalDialog;
import de.tudarmstadt.ukp.inception.documents.api.DocumentService;
import de.tudarmstadt.ukp.inception.editor.action.AnnotationActionHandler;
import de.tudarmstadt.ukp.inception.preferences.PreferencesService;
import de.tudarmstadt.ukp.inception.recommendation.api.LearningRecordService;
import de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService;
import de.tudarmstadt.ukp.inception.recommendation.api.event.AjaxRecommendationAcceptedEvent;
Expand All @@ -100,7 +103,6 @@
import de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion;
import de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecord;
import de.tudarmstadt.ukp.inception.recommendation.api.model.Offset;
import de.tudarmstadt.ukp.inception.recommendation.api.model.Predictions;
import de.tudarmstadt.ukp.inception.recommendation.api.model.SpanSuggestion;
import de.tudarmstadt.ukp.inception.recommendation.api.model.SuggestionDocumentGroup;
import de.tudarmstadt.ukp.inception.recommendation.api.model.SuggestionGroup;
Expand Down Expand Up @@ -172,7 +174,9 @@ public class ActiveLearningSidebar
private @SpringBean ApplicationEventPublisherHolder applicationEventPublisherHolder;
private @SpringBean UserDao userService;
private @SpringBean FeatureSupportRegistry featureSupportRegistry;
private @SpringBean PreferencesService preferencesService;

private IModel<Boolean> recommendersAvailable;
private IModel<List<LearningRecord>> learningRecords;
private CompoundPropertyModel<ActiveLearningServiceImpl.ActiveLearningUserState> alStateModel;

Expand Down Expand Up @@ -211,17 +215,26 @@ public ActiveLearningSidebar(String aId, IModel<AnnotatorState> aModel,

// Set up the AL state in the page if it is not already there or if for some reason the
// suggestions have completely disappeared (e.g. after a system restart)
AnnotatorState state = getModelObject();
Predictions predictions = state.getProject() != null
var state = getModelObject();
var predictions = state.getProject() != null
? recommendationService.getPredictions(state.getUser(), state.getProject())
: null;
if (aAnnotationPage.getMetaData(CURRENT_AL_USER_STATE) == null || predictions == null) {
ActiveLearningUserState alState = new ActiveLearningUserState();
var alState = new ActiveLearningUserState();
alState.setStrategy(new UncertaintySamplingStrategy());
alStateModel.setObject(alState);
}

add(sessionControlForm = createSessionControlForm());
recommendersAvailable = LoadableDetachableModel.of(this::isRecommendersAvailable);

var notAvailableNotice = new WebMarkupContainer("notAvailableNotice");
notAvailableNotice.add(visibleWhenNot(recommendersAvailable));
add(notAvailableNotice);

sessionControlForm = createSessionControlForm();
sessionControlForm.add(visibleWhen(() -> alStateModel.getObject().isDoExistRecommenders()
&& recommendersAvailable.getObject()));
add(sessionControlForm);

alMainContainer = new WebMarkupContainer(CID_MAIN_CONTAINER);
alMainContainer.setOutputMarkupId(true);
Expand All @@ -230,19 +243,48 @@ public ActiveLearningSidebar(String aId, IModel<AnnotatorState> aModel,
alMainContainer.add(clearSkippedRecommendationForm());
alMainContainer.add(createRecommendationOperationForm());
alMainContainer.add(createLearningHistory());
alMainContainer.add(visibleWhen(recommendersAvailable));
add(alMainContainer);

dialog = new BootstrapModalDialog(CID_CONFIRMATION_DIALOG);
dialog.trapFocus();
add(dialog);
}

@Override
protected void onDetach()
{
super.onDetach();
recommendersAvailable.detach();
}

private boolean isRecommendersAvailable()
{
var state = getModelObject();
var prefs = preferencesService.loadDefaultTraitsForProject(KEY_RECOMMENDER_GENERAL_SETTINGS,
state.getProject());

// Do not show predictions when viewing annotations of another user
if (!prefs.isShowRecommendationsWhenViewingOtherUser()
&& !Objects.equals(state.getUser(), userService.getCurrentUser())) {
return false;
}

// Do not show predictions when viewing annotations of curation user
if (!prefs.isShowRecommendationsWhenViewingCurationUser()
&& Objects.equals(state.getUser(), userService.getCurationUser())) {
return false;
}

return true;
}

private Label createNoRecommendersMessage()
{
if (!alStateModel.getObject().isSessionActive()) {
// Use the currently selected layer from the annotation detail editor panel as the
// default choice in the active learning mode.
List<AnnotationLayer> layersWithRecommenders = listLayersWithRecommenders();
var layersWithRecommenders = listLayersWithRecommenders();
if (layersWithRecommenders.contains(getModelObject().getDefaultAnnotationLayer())) {
alStateModel.getObject().setLayer(getModelObject().getDefaultAnnotationLayer());
}
Expand All @@ -258,7 +300,7 @@ else if (!layersWithRecommenders.isEmpty()) {
}
}

Label noRecommendersMessage = new Label(CID_NO_RECOMMENDERS, "None of the layers have any "
var noRecommendersMessage = new Label(CID_NO_RECOMMENDERS, "None of the layers have any "
+ "recommenders configured. Please set the recommenders first in the Project "
+ "Settings.");
noRecommendersMessage
Expand All @@ -268,9 +310,9 @@ else if (!layersWithRecommenders.isEmpty()) {

private Form<Void> createSessionControlForm()
{
Form<Void> form = new Form<>(CID_SESSION_CONTROL_FORM);
var form = new Form<Void>(CID_SESSION_CONTROL_FORM);

DropDownChoice<AnnotationLayer> layersDropdown = new DropDownChoice<>(CID_SELECT_LAYER);
var layersDropdown = new DropDownChoice<AnnotationLayer>(CID_SELECT_LAYER);
layersDropdown.setModel(alStateModel.bind("layer"));
layersDropdown.setChoices(LoadableDetachableModel.of(this::listLayersWithRecommenders));
layersDropdown.setChoiceRenderer(new LambdaChoiceRenderer<>(AnnotationLayer::getUiName));
Expand All @@ -284,7 +326,6 @@ private Form<Void> createSessionControlForm()
.add(visibleWhen(() -> !alStateModel.getObject().isSessionActive())));
form.add(new LambdaAjaxLink(CID_STOP_SESSION_BUTTON, this::actionStopSession)
.add(visibleWhen(() -> alStateModel.getObject().isSessionActive())));
form.add(visibleWhen(() -> alStateModel.getObject().isDoExistRecommenders()));

return form;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ delta=\u0394
history=History
session=Session
activeLearning=Active Learning
notAvailableNotice=Currently not available
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import org.apache.wicket.Component;
import org.apache.wicket.model.IModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;

import de.tudarmstadt.ukp.clarin.webanno.api.casstorage.CasProvider;
Expand All @@ -44,7 +43,6 @@ public class ActiveLearningSidebarFactory
{
private final RecommendationService recommendationService;

@Autowired
public ActiveLearningSidebarFactory(RecommendationService aRecommendationService)
{
recommendationService = aRecommendationService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public ActiveLearningSidebarIcon(String aId, IModel<AnnotatorState> aState)

setOutputMarkupId(true);

queue(new Icon("icon", FontAwesome5IconType.robot_s));
queue(new Icon("icon", FontAwesome5IconType.font_s));
queue(new Icon("badge", LoadableDetachableModel.of(this::getStateIcon))
.add(new ClassAttributeModifier()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,4 +320,13 @@ void setPredictForAllDocuments(String aSessionOwner, Project aProject,
boolean isSuspended(String aUser, Project aProject);

void setSuspended(String aUser, Project aProject, boolean aState);

/**
* @return if the curation sidebar mode is available.
* @deprecated This obviously shouldn't be here, but this is the easiest way to access this
* information from the recommender settings. Should be removed when the curation
* sidebar leaves experimental mode.
*/
@Deprecated
boolean isCurationSidebarEnabled();
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public class RecommenderGeneralSettings

private boolean waitForRecommendersOnOpenDocument = false;

private boolean showRecommendationsWhenViewingOtherUser = true;

private boolean showRecommendationsWhenViewingCurationUser = true;

public boolean isWaitForRecommendersOnOpenDocument()
{
return waitForRecommendersOnOpenDocument;
Expand All @@ -35,4 +39,26 @@ public void setWaitForRecommendersOnOpenDocument(boolean aWaitForRecommendersOnO
{
waitForRecommendersOnOpenDocument = aWaitForRecommendersOnOpenDocument;
}

public boolean isShowRecommendationsWhenViewingOtherUser()
{
return showRecommendationsWhenViewingOtherUser;
}

public void setShowRecommendationsWhenViewingOtherUser(
boolean aShowRecommendationsWhenViewingOtherUser)
{
showRecommendationsWhenViewingOtherUser = aShowRecommendationsWhenViewingOtherUser;
}

public boolean isShowRecommendationsWhenViewingCurationUser()
{
return showRecommendationsWhenViewingCurationUser;
}

public void setShowRecommendationsWhenViewingCurationUser(
boolean aShowRecommendationsWhenViewingCurationUser)
{
showRecommendationsWhenViewingCurationUser = aShowRecommendationsWhenViewingCurationUser;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ public ProjectRecommendersMenuItem projectRecommendersMenuItem()
havingValue = "true", matchIfMissing = true)
@Bean
public RecommendationSidebarFactory recommendationSidebarFactory(
RecommendationService aRecommendationService)
RecommendationService aRecommendationService, PreferencesService aPreferencesService,
UserDao aUserService)
{
return new RecommendationSidebarFactory(aRecommendationService);
}
Expand Down Expand Up @@ -188,12 +189,13 @@ public RecommendationEventFooterItem recommendationEventFooterItem()
}

@Bean
public RecommendationRenderer recommendationRenderer(AnnotationSchemaService aAnnotationService,
public RecommendationRenderer recommendationRenderer(
RecommendationService aRecommendationService,
SuggestionSupportRegistry aSuggestionSupportRegistry)
SuggestionSupportRegistry aSuggestionSupportRegistry,
PreferencesService aPreferencesService, UserDao aUserService)
{
return new RecommendationRenderer(aAnnotationService, aRecommendationService,
aSuggestionSupportRegistry);
return new RecommendationRenderer(aRecommendationService, aSuggestionSupportRegistry,
aPreferencesService, aUserService);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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.recommendation.event;

import org.springframework.context.ApplicationEvent;

import de.tudarmstadt.ukp.clarin.webanno.model.Project;
import de.tudarmstadt.ukp.inception.support.wicket.event.HybridApplicationUIEvent;

public class RecommendersResumedEvent
extends ApplicationEvent
implements HybridApplicationUIEvent
{
private static final long serialVersionUID = -4736560772442881663L;

private final Project project;
private final String sessionOwner;

public RecommendersResumedEvent(Object source, Project aProject, String aSessionOwner)
{
super(source);
project = aProject;
sessionOwner = aSessionOwner;
}

public Project getProject()
{
return project;
}

public String getUser()
{
return sessionOwner;
}
}
Loading

0 comments on commit 2671e87

Please sign in to comment.