Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SLR data is now editable #9131

Merged
merged 15 commits into from
Sep 4, 2022
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve
- We integrated a new three-way merge UI for merging entries in the Entries Merger Dialog, the Duplicate Resolver Dialog, the Entry Importer Dialog, and the External Changes Resolver Dialog. [#8945](https://github.com/JabRef/jabref/pull/8945)
- We added the ability to merge groups, keywords, comments and files when merging entries. [#9022](https://github.com/JabRef/jabref/pull/9022)
- We added a warning message next to the authors field in the merge dialog to warn users when the authors are the same but formatted differently. [#8745](https://github.com/JabRef/jabref/issues/8745)
- The properties of an existing systematic literature review can be edited.

### Changed

Expand Down
8 changes: 6 additions & 2 deletions src/main/java/org/jabref/gui/JabRefFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
import org.jabref.gui.shared.PullChangesFromSharedAction;
import org.jabref.gui.sidepane.SidePane;
import org.jabref.gui.sidepane.SidePaneType;
import org.jabref.gui.slr.EditExistingStudyAction;
import org.jabref.gui.slr.ExistingStudySearchAction;
import org.jabref.gui.slr.StartNewStudyAction;
import org.jabref.gui.specialfields.SpecialFieldMenuItemFactory;
Expand Down Expand Up @@ -541,7 +542,6 @@ private Node createToolbar() {
// Setup Toolbar

ToolBar toolBar = new ToolBar(

new HBox(
factory.createIconButton(StandardActions.NEW_LIBRARY, new NewDatabaseAction(this, prefs)),
factory.createIconButton(StandardActions.OPEN_LIBRARY, new OpenDatabaseAction(this, prefs, dialogService, stateManager, themeManager)),
Expand Down Expand Up @@ -878,9 +878,13 @@ private MenuBar createMenu() {

factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new SendAsEMailAction(dialogService, this.prefs, stateManager)),
pushToApplicationMenuItem,

new SeparatorMenuItem(),

// Systematic Literature Review (SLR)
factory.createMenuItem(StandardActions.START_NEW_STUDY, new StartNewStudyAction(this, Globals.getFileUpdateMonitor(), Globals.TASK_EXECUTOR, prefs, stateManager, themeManager)),
factory.createMenuItem(StandardActions.SEARCH_FOR_EXISTING_STUDY, new ExistingStudySearchAction(this, Globals.getFileUpdateMonitor(), Globals.TASK_EXECUTOR, prefs, stateManager, themeManager)),
factory.createMenuItem(StandardActions.EDIT_EXISTING_STUDY, new EditExistingStudyAction(this.dialogService, this.stateManager)),
factory.createMenuItem(StandardActions.UPDATE_SEARCH_RESULTS_OF_STUDY, new ExistingStudySearchAction(this, this.getOpenDatabaseAction(), this.getDialogService(), Globals.getFileUpdateMonitor(), Globals.TASK_EXECUTOR, prefs, stateManager, themeManager)),

new SeparatorMenuItem(),

Expand Down
8 changes: 7 additions & 1 deletion src/main/java/org/jabref/gui/LibraryTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public class LibraryTab extends Tab {
private final ThemeManager themeManager;
private final BooleanProperty changedProperty = new SimpleBooleanProperty(false);
private final BooleanProperty nonUndoableChangeProperty = new SimpleBooleanProperty(false);

private BibDatabaseContext bibDatabaseContext;
private MainTableDataModel tableModel;
private CitationStyleCache citationStyleCache;
Expand All @@ -98,17 +99,22 @@ public class LibraryTab extends Tab {
private BasePanelMode mode = BasePanelMode.SHOWING_NOTHING;
private SplitPane splitPane;
private DatabaseNotification databaseNotificationPane;

private boolean saving;
private PersonNameSuggestionProvider searchAutoCompleter;

// Used to track whether the base has changed since last save.
private BibEntry showing;

private SuggestionProviders suggestionProviders;

@SuppressWarnings({"FieldCanBeLocal"})
private Subscription dividerPositionSubscription;

// the query the user searches when this BasePanel is active
private Optional<SearchQuery> currentSearchQuery = Optional.empty();

private Optional<DatabaseChangeMonitor> changeMonitor = Optional.empty();

// initializing it so we prevent NullPointerException
private BackgroundTask<ParserResult> dataLoadingTask = BackgroundTask.wrap(() -> null);

Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/jabref/gui/actions/ActionHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import org.jabref.gui.StateManager;
import org.jabref.logic.shared.DatabaseLocation;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.LinkedFile;
import org.jabref.model.entry.field.Field;
Expand All @@ -33,6 +34,11 @@ public static BooleanExpression needsSharedDatabase(StateManager stateManager) {
return BooleanExpression.booleanExpression(binding);
}

public static BooleanExpression needsStudyDatabase(StateManager stateManager) {
EasyBinding<Boolean> binding = EasyBind.map(stateManager.activeDatabaseProperty(), context -> context.filter(BibDatabaseContext::isStudy).isPresent());
return BooleanExpression.booleanExpression(binding);
}

public static BooleanExpression needsEntriesSelected(StateManager stateManager) {
return Bindings.isNotEmpty(stateManager.getSelectedEntries());
}
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/org/jabref/gui/actions/StandardActions.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,11 @@ public enum StandardActions implements Action {
PARSE_LATEX(Localization.lang("Search for citations in LaTeX files..."), IconTheme.JabRefIcons.LATEX_CITATIONS),
NEW_SUB_LIBRARY_FROM_AUX(Localization.lang("New sublibrary based on AUX file") + "...", Localization.lang("New BibTeX sublibrary") + Localization.lang("This feature generates a new library based on which entries are needed in an existing LaTeX document."), IconTheme.JabRefIcons.NEW),
WRITE_METADATA_TO_PDF(Localization.lang("Write metadata to PDF files"), Localization.lang("Will write metadata to the PDFs linked from selected entries."), KeyBinding.WRITE_METADATA_TO_PDF),

START_NEW_STUDY(Localization.lang("Start new systematic literature review")),
SEARCH_FOR_EXISTING_STUDY(Localization.lang("Perform search for existing systematic literature review")),
UPDATE_SEARCH_RESULTS_OF_STUDY(Localization.lang("Perform search for existing systematic literature review")),
EDIT_EXISTING_STUDY(Localization.lang("Manage study definition")),

OPEN_DATABASE_FOLDER(Localization.lang("Reveal in file explorer")),
OPEN_FOLDER(Localization.lang("Open folder"), Localization.lang("Open folder"), IconTheme.JabRefIcons.FOLDER, KeyBinding.OPEN_FOLDER),
OPEN_FILE(Localization.lang("Open file"), Localization.lang("Open file"), IconTheme.JabRefIcons.FILE, KeyBinding.OPEN_FILE),
Expand Down
64 changes: 64 additions & 0 deletions src/main/java/org/jabref/gui/slr/EditExistingStudyAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.jabref.gui.slr;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Objects;
Siedlerchr marked this conversation as resolved.
Show resolved Hide resolved

import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.ActionHelper;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.logic.crawler.StudyRepository;
import org.jabref.logic.crawler.StudyYamlParser;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.study.Study;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EditExistingStudyAction extends SimpleCommand {
private static final Logger LOGGER = LoggerFactory.getLogger(EditExistingStudyAction.class);

private final DialogService dialogService;
private final StateManager stateManager;

public EditExistingStudyAction(DialogService dialogService, StateManager stateManager) {
this.dialogService = dialogService;
this.stateManager = stateManager;
this.executable.bind(ActionHelper.needsStudyDatabase(stateManager));
}

@Override
public void execute() {
// The action works on the current library
// This library has to be determined
if (stateManager.getActiveDatabase().isEmpty() || !stateManager.getActiveDatabase().get().isStudy()) {
return;
}
BibDatabaseContext bibDatabaseContext = stateManager.getActiveDatabase().get();

// The action can only be called on an existing AND saved study library
// The saving is ensured at creation of a study library
// Thus, this check is only existing to check this assumption
if (bibDatabaseContext.getDatabasePath().isEmpty()) {
LOGGER.error("Database path is not available");
Siedlerchr marked this conversation as resolved.
Show resolved Hide resolved
return;
}

Path databasePath = bibDatabaseContext.getDatabasePath().get();

Path studyDirectory = databasePath.getParent();

Study study;
try {
study = new StudyYamlParser().parseStudyYamlFile(studyDirectory.resolve(StudyRepository.STUDY_DEFINITION_FILE_NAME));
} catch (IOException e) {
dialogService.showErrorDialogAndWait(Localization.lang("Error opening file"), e);
return;
}

// When the dialog returns, the study.yml file is updated (or kept unmodified at Cancel)
dialogService.showCustomDialogAndWait(new ManageStudyDefinitionView(study, studyDirectory));
}
}
105 changes: 63 additions & 42 deletions src/main/java/org/jabref/gui/slr/ExistingStudySearchAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.JabRefFrame;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.ActionHelper;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.importer.actions.OpenDatabaseAction;
import org.jabref.gui.theme.ThemeManager;
Expand All @@ -22,6 +23,7 @@
import org.jabref.logic.importer.ParseException;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.io.FileUtil;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.util.FileUpdateMonitor;
import org.jabref.preferences.GeneralPreferences;
Expand All @@ -35,30 +37,50 @@ public class ExistingStudySearchAction extends SimpleCommand {
private static final Logger LOGGER = LoggerFactory.getLogger(ExistingStudySearchAction.class);

protected final DialogService dialogService;
protected final Path workingDirectory;

Path studyDirectory;
protected Path studyDirectory;
protected final PreferencesService preferencesService;
protected final StateManager stateManager;

private final JabRefFrame frame;
private final FileUpdateMonitor fileUpdateMonitor;
private final TaskExecutor taskExecutor;
private final PreferencesService preferencesService;
private final StateManager stateManager;
private final ThemeManager themeManager;
private final GeneralPreferences generalPreferences;
private final ImportFormatPreferences importFormatPreferences;
private final ImporterPreferences importerPreferences;
private final SavePreferences savePreferences;
// This can be either populated before crawl is called or is populated in the call using the directory dialog. This is helpful if the directory is selected in a previous dialog/UI element

public ExistingStudySearchAction(JabRefFrame frame,
FileUpdateMonitor fileUpdateMonitor,
TaskExecutor taskExecutor,
PreferencesService preferencesService,
StateManager stateManager,
ThemeManager themeManager) {
private final JabRefFrame frame;
private final OpenDatabaseAction openDatabaseAction;

/**
* @param frame Required to close the tab before the study is updated
* @param openDatabaseAction Required to open the tab after the study is exectued
*/
public ExistingStudySearchAction(
JabRefFrame frame,
OpenDatabaseAction openDatabaseAction,
DialogService dialogService,
FileUpdateMonitor fileUpdateMonitor,
TaskExecutor taskExecutor,
PreferencesService preferencesService,
StateManager stateManager,
ThemeManager themeManager) {
this(frame, openDatabaseAction, dialogService, fileUpdateMonitor, taskExecutor, preferencesService, stateManager, themeManager, false);
}

protected ExistingStudySearchAction(
JabRefFrame frame,
OpenDatabaseAction openDatabaseAction,
DialogService dialogService,
FileUpdateMonitor fileUpdateMonitor,
TaskExecutor taskExecutor,
PreferencesService preferencesService,
StateManager stateManager,
ThemeManager themeManager,
boolean isNew) {
this.frame = frame;
this.dialogService = frame.getDialogService();
this.openDatabaseAction = openDatabaseAction;
this.dialogService = dialogService;
this.fileUpdateMonitor = fileUpdateMonitor;
this.taskExecutor = taskExecutor;
this.preferencesService = preferencesService;
Expand All @@ -69,46 +91,45 @@ public ExistingStudySearchAction(JabRefFrame frame,
this.importerPreferences = preferencesService.getImporterPreferences();
this.savePreferences = preferencesService.getSavePreferences();

this.workingDirectory = stateManager.getActiveDatabase()
.map(database -> FileUtil.getInitialDirectory(
database,
preferencesService.getFilePreferences().getWorkingDirectory()))
.orElse(preferencesService.getFilePreferences().getWorkingDirectory());
if (!isNew) {
this.executable.bind(ActionHelper.needsStudyDatabase(stateManager));
}
}

@Override
public void execute() {
// Reset before each execution
studyDirectory = null;
crawl();
}
if (stateManager.getActiveDatabase().isEmpty()) {
LOGGER.error("Database is not present, even if it should");
return;
}
BibDatabaseContext bibDatabaseContext = stateManager.getActiveDatabase().get();

public void crawl() {
if (Objects.isNull(studyDirectory)) {
DirectoryDialogConfiguration directoryDialogConfiguration = new DirectoryDialogConfiguration.Builder()
.withInitialDirectory(workingDirectory)
.build();
if (bibDatabaseContext.getDatabasePath().isEmpty()) {
LOGGER.error("Database path is not present, even if it should");
return;
}
this.studyDirectory = bibDatabaseContext.getDatabasePath().get().getParent();

Optional<Path> studyRepositoryRoot = dialogService.showDirectorySelectionDialog(directoryDialogConfiguration);
crawl();
}

if (studyRepositoryRoot.isEmpty()) {
// Do nothing if selection was canceled
return;
}
studyDirectory = studyRepositoryRoot.get();
}
protected void crawl() {
// We hard close the tab
// Future work: Properly close the tab (with saving, ...)
frame.closeCurrentTab();

try {
setupRepository(studyDirectory);
setupRepository(this.studyDirectory);
} catch (IOException | GitAPIException e) {
dialogService.showErrorDialogAndWait(Localization.lang("Study repository could not be created"), e);
return;
}

final Crawler crawler;
try {
crawler = new Crawler(
studyDirectory,
new SlrGitHandler(studyDirectory),
this.studyDirectory,
new SlrGitHandler(this.studyDirectory),
generalPreferences,
importFormatPreferences,
importerPreferences,
Expand All @@ -120,7 +141,8 @@ public void crawl() {
dialogService.showErrorDialogAndWait(Localization.lang("Error during reading of study definition file."), e);
return;
}
dialogService.notify(Localization.lang("Searching"));

dialogService.notify(Localization.lang("Searching..."));
BackgroundTask.wrap(() -> {
crawler.performCrawl();
return 0; // Return any value to make this a callable instead of a runnable. This allows throwing exceptions.
Expand All @@ -130,9 +152,8 @@ public void crawl() {
dialogService.showErrorDialogAndWait(Localization.lang("Error during persistence of crawling results."), e);
})
.onSuccess(unused -> {
new OpenDatabaseAction(frame, preferencesService, dialogService, stateManager, themeManager).openFile(Path.of(studyDirectory.toString(), "studyResult.bib"));
// If finished reset command object for next use
studyDirectory = null;
dialogService.notify(Localization.lang("Finished Searching"));
openDatabaseAction.openFile(Path.of(this.studyDirectory.toString(), Crawler.FILENAME_STUDY_RESULT_BIB));
})
.executeWith(taskExecutor);
}
Expand Down
Loading