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 f04c731
Show file tree
Hide file tree
Showing 25 changed files with 475 additions and 62 deletions.
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 @@ -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;
}
}
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 RecommendersSuspendedEvent
extends ApplicationEvent
implements HybridApplicationUIEvent
{
private static final long serialVersionUID = -4736560772442881663L;

private final Project project;
private final String sessionOwner;

public RecommendersSuspendedEvent(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 f04c731

Please sign in to comment.