Skip to content

Web Import: Add New Entries to 'Imported Entries' Group #12998

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.groups.GroupEntryChanger;
import org.jabref.model.groups.GroupTreeNode;
import org.jabref.model.groups.SmartGroup;
import org.jabref.model.util.FileUpdateMonitor;
import org.jabref.model.util.OptionalUtil;

Expand Down Expand Up @@ -164,6 +165,7 @@ public List<ImportFilesResultItemViewModel> call() {
// Modifiers do not work on macOS: https://bugs.openjdk.org/browse/JDK-8264172
// Similar code as org.jabref.gui.preview.PreviewPanel.PreviewPanel
DragDrop.handleDropOfFiles(List.of(file), transferMode, fileLinker, entry);
addToImportEntriesGroup(pdfEntriesInFile);
entriesToAdd.addAll(pdfEntriesInFile);
addResultToList(file, true, Localization.lang("File was successfully imported as a new entry"));
});
Expand Down Expand Up @@ -263,6 +265,7 @@ private void importEntryWithDuplicateCheck(BibDatabaseContext bibDatabaseContext
finalEntry = duplicateHandledEntry.get();
}
importCleanedEntries(bibDatabaseContext, List.of(finalEntry));
addToImportEntriesGroup(List.of(finalEntry));
downloadLinkedFiles(finalEntry);
BibEntry entryToFocus = finalEntry;
stateManager.activeTabProperty().get().ifPresent(tab -> tab.clearAndSelect(entryToFocus));
Expand Down Expand Up @@ -501,4 +504,17 @@ private List<BibEntry> handlePdfUrl(String pdfUrl) throws IOException {
return List.of();
}
}

private void addToImportEntriesGroup(List<BibEntry> entriesToInsert) {
if (preferences.getLibraryPreferences().isAddImportedEntriesEnabled()) {
// Only one SmartGroup
this.bibDatabaseContext.getMetaData()
.getGroups()
.flatMap(grp -> grp.getChildren()
.stream()
.filter(node -> node.getGroup() instanceof SmartGroup)
.findFirst())
.ifPresent(smtGrp -> smtGrp.addEntriesToGroup(entriesToInsert));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.jabref.model.groups.ExplicitGroup;
import org.jabref.model.groups.KeywordGroup;
import org.jabref.model.groups.SearchGroup;
import org.jabref.model.groups.SmartGroup;
import org.jabref.model.strings.StringUtil;

public class GroupDescriptions {
Expand Down Expand Up @@ -61,6 +62,10 @@ public static String getShortDescriptionAllEntriesGroup() {
return Localization.lang("<b>All Entries</b> (this group cannot be edited or removed)");
}

public static String getShortDescriptionSmartGroup(SmartGroup smartGroup) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method getShortDescriptionSmartGroup returns a String, which could potentially be null. New public methods should not return null and should use java.util.Optional instead.

return Localization.lang("<b>Smart Group</b> (Import Entries)");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string 'Smart Group' should be in sentence case as per the guidelines, so it should be 'Smart group'.

}

public static String getShortDescription(SearchGroup searchGroup, boolean showDynamic) {
StringBuilder sb = new StringBuilder();
sb.append("<b>");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.jabref.model.groups.LastNameGroup;
import org.jabref.model.groups.RegexKeywordGroup;
import org.jabref.model.groups.SearchGroup;
import org.jabref.model.groups.SmartGroup;
import org.jabref.model.groups.TexGroup;
import org.jabref.model.search.event.IndexAddedOrUpdatedEvent;
import org.jabref.model.search.event.IndexClosedEvent;
Expand Down Expand Up @@ -432,6 +433,8 @@ public boolean canAddEntriesIn() {
AbstractGroup group = groupNode.getGroup();
if (group instanceof AllEntriesGroup) {
return false;
} else if (group instanceof SmartGroup) {
return false;
} else if (group instanceof ExplicitGroup) {
return true;
} else if (group instanceof LastNameGroup || group instanceof RegexKeywordGroup) {
Expand All @@ -458,7 +461,7 @@ public boolean canAddEntriesIn() {
public boolean canBeDragged() {
AbstractGroup group = groupNode.getGroup();
return switch (group) {
case AllEntriesGroup _ -> false;
case AllEntriesGroup _, SmartGroup _ -> false;
case ExplicitGroup _, SearchGroup _, AutomaticKeywordGroup _, AutomaticPersonsGroup _, TexGroup _ -> true;
case KeywordGroup _ ->
// KeywordGroup is parent of LastNameGroup, RegexKeywordGroup and WordKeywordGroup
Expand All @@ -477,13 +480,13 @@ public boolean canAddGroupsIn() {
AbstractGroup group = groupNode.getGroup();
return switch (group) {
case AllEntriesGroup _, ExplicitGroup _, SearchGroup _, TexGroup _ -> true;
case AutomaticKeywordGroup _, AutomaticPersonsGroup _, SmartGroup _ -> false;
case KeywordGroup _ ->
// KeywordGroup is parent of LastNameGroup, RegexKeywordGroup and WordKeywordGroup
groupNode.getParent()
.map(GroupTreeNode::getGroup)
.map(groupParent -> !(groupParent instanceof AutomaticKeywordGroup || groupParent instanceof AutomaticPersonsGroup))
.orElse(false);
case AutomaticKeywordGroup _, AutomaticPersonsGroup _ -> false;
case null -> throw new IllegalArgumentException("Group cannot be null");
default -> throw new UnsupportedOperationException("canAddGroupsIn method not yet implemented in group: " + group.getClass().getName());
};
Expand All @@ -492,7 +495,7 @@ public boolean canAddGroupsIn() {
public boolean canRemove() {
AbstractGroup group = groupNode.getGroup();
return switch (group) {
case AllEntriesGroup _ -> false;
case AllEntriesGroup _, SmartGroup _ -> false;
case ExplicitGroup _, SearchGroup _, AutomaticKeywordGroup _, AutomaticPersonsGroup _, TexGroup _ -> true;
case KeywordGroup _ ->
// KeywordGroup is parent of LastNameGroup, RegexKeywordGroup and WordKeywordGroup
Expand All @@ -508,7 +511,7 @@ public boolean canRemove() {
public boolean isEditable() {
AbstractGroup group = groupNode.getGroup();
return switch (group) {
case AllEntriesGroup _ -> false;
case AllEntriesGroup _, SmartGroup _ -> false;
case ExplicitGroup _, SearchGroup _, AutomaticKeywordGroup _, AutomaticPersonsGroup _, TexGroup _ -> true;
case KeywordGroup _ ->
// KeywordGroup is parent of LastNameGroup, RegexKeywordGroup and WordKeywordGroup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.jabref.model.groups.GroupTreeNode;
import org.jabref.model.groups.KeywordGroup;
import org.jabref.model.groups.SearchGroup;
import org.jabref.model.groups.SmartGroup;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code uses Java SWING imports, which contradicts the instruction to use only JavaFX for UI technology. This could lead to inconsistencies in the UI framework used.


public class GroupTreeNodeViewModel {
private final GroupTreeNode node;
Expand Down Expand Up @@ -51,6 +52,8 @@ public String getDescription() {
String shortDescription = "";
boolean showDynamic = true;
shortDescription = switch (group) {
case SmartGroup smartGroup ->
GroupDescriptions.getShortDescriptionSmartGroup(smartGroup);
case ExplicitGroup explicitGroup ->
GroupDescriptions.getShortDescriptionExplicitGroup(explicitGroup);
case KeywordGroup keywordGroup ->
Expand Down
25 changes: 25 additions & 0 deletions jabgui/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@
import org.jabref.model.groups.AutomaticKeywordGroup;
import org.jabref.model.groups.AutomaticPersonsGroup;
import org.jabref.model.groups.ExplicitGroup;
import org.jabref.model.groups.GroupHierarchyType;
import org.jabref.model.groups.GroupTreeNode;
import org.jabref.model.groups.RegexKeywordGroup;
import org.jabref.model.groups.SearchGroup;
import org.jabref.model.groups.SmartGroup;
import org.jabref.model.groups.TexGroup;
import org.jabref.model.groups.WordKeywordGroup;
import org.jabref.model.metadata.MetaData;
Expand Down Expand Up @@ -172,6 +174,29 @@ private void onActiveDatabaseChanged(Optional<BibDatabaseContext> newDatabase) {
rootGroup.setValue(null);
}
currentDatabase = newDatabase;
newDatabase.ifPresent(db -> addGroupImportEntries(rootGroup.get()));
}

private void addGroupImportEntries(GroupNodeViewModel parent) {
if (!preferences.getLibraryPreferences().isAddImportedEntriesEnabled()) {
return;
}

String grpName = preferences.getLibraryPreferences().getAddImportedEntriesGroupName();
AbstractGroup importEntriesGroup = new SmartGroup(grpName, GroupHierarchyType.INDEPENDENT, ',');
boolean isGrpExist = parent.getGroupNode()
.getChildren()
.stream()
.map(GroupTreeNode::getGroup)
.anyMatch(grp -> grp instanceof SmartGroup);
if (!isGrpExist) {
currentDatabase.ifPresent(db -> {
GroupTreeNode newSubgroup = parent.addSubgroup(importEntriesGroup);
newSubgroup.moveTo(parent.getGroupNode(), 0);
selectedGroups.setAll(new GroupNodeViewModel(db, stateManager, taskExecutor, newSubgroup, localDragboard, preferences));
writeGroupChangesToMetaData();
});
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public class WebSearchTab extends AbstractPreferenceTabView<WebSearchTabViewMode
@FXML private CheckBox warnAboutDuplicatesOnImport;
@FXML private CheckBox downloadLinkedOnlineFiles;
@FXML private CheckBox keepDownloadUrl;
@FXML private CheckBox addImportedEntries;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name 'addImportedEntries' should be in camel-case format, such as 'addImportedEntriesCheckBox', to improve readability and maintain consistency with Java naming conventions.

@FXML private TextField addImportedEntriesGroupName;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name 'addImportedEntriesGroupName' should be in camel-case format, such as 'importedEntriesGroupNameField', to improve readability and maintain consistency with Java naming conventions.

@FXML private ComboBox<PlainCitationParserChoice> defaultPlainCitationParser;

@FXML private CheckBox useCustomDOI;
Expand Down Expand Up @@ -76,6 +78,10 @@ public void initialize() {
downloadLinkedOnlineFiles.selectedProperty().bindBidirectional(viewModel.shouldDownloadLinkedOnlineFiles());
keepDownloadUrl.selectedProperty().bindBidirectional(viewModel.shouldKeepDownloadUrl());

addImportedEntries.selectedProperty().bindBidirectional(viewModel.getAddImportedEntries());
addImportedEntriesGroupName.textProperty().bindBidirectional(viewModel.getAddImportedEntriesGroupName());
addImportedEntriesGroupName.disableProperty().bind(addImportedEntries.selectedProperty().not());

new ViewModelListCellFactory<PlainCitationParserChoice>()
.withText(PlainCitationParserChoice::getLocalizedName)
.install(defaultPlainCitationParser);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.jabref.gui.preferences.PreferenceTabViewModel;
import org.jabref.gui.slr.StudyCatalogItem;
import org.jabref.logic.FilePreferences;
import org.jabref.logic.LibraryPreferences;
import org.jabref.logic.importer.ImportFormatPreferences;
import org.jabref.logic.importer.ImporterPreferences;
import org.jabref.logic.importer.SearchBasedFetcher;
Expand Down Expand Up @@ -48,6 +49,9 @@ public class WebSearchTabViewModel implements PreferenceTabViewModel {
new SimpleListProperty<>(FXCollections.observableArrayList(PlainCitationParserChoice.values()));
private final ObjectProperty<PlainCitationParserChoice> defaultPlainCitationParser = new SimpleObjectProperty<>();

private final BooleanProperty addImportedEntries = new SimpleBooleanProperty();
private final StringProperty addImportedEntriesGroupName = new SimpleStringProperty("");

private final BooleanProperty useCustomDOIProperty = new SimpleBooleanProperty();
private final StringProperty useCustomDOINameProperty = new SimpleStringProperty("");

Expand All @@ -67,6 +71,7 @@ public class WebSearchTabViewModel implements PreferenceTabViewModel {
private final ImporterPreferences importerPreferences;
private final FilePreferences filePreferences;
private final ImportFormatPreferences importFormatPreferences;
private final LibraryPreferences libraryPreferences;

private final ReadOnlyBooleanProperty refAiEnabled;

Expand All @@ -78,6 +83,7 @@ public WebSearchTabViewModel(CliPreferences preferences, DialogService dialogSer
this.doiPreferences = preferences.getDOIPreferences();
this.filePreferences = preferences.getFilePreferences();
this.importFormatPreferences = preferences.getImportFormatPreferences();
this.libraryPreferences = preferences.getLibraryPreferences();

this.refAiEnabled = refAiEnabled;

Expand Down Expand Up @@ -128,6 +134,12 @@ public void setValues() {
warnAboutDuplicatesOnImportProperty.setValue(importerPreferences.shouldWarnAboutDuplicatesOnImport());
shouldDownloadLinkedOnlineFiles.setValue(filePreferences.shouldDownloadLinkedFiles());
shouldkeepDownloadUrl.setValue(filePreferences.shouldKeepDownloadUrl());
addImportedEntries.setValue(libraryPreferences.isAddImportedEntriesEnabled());
if (libraryPreferences.getAddImportedEntriesGroupName().isEmpty()) {
addImportedEntriesGroupName.setValue(Localization.lang("Imported entries"));
} else {
addImportedEntriesGroupName.setValue(libraryPreferences.getAddImportedEntriesGroupName());
}
defaultPlainCitationParser.setValue(importerPreferences.getDefaultPlainCitationParser());

useCustomDOIProperty.setValue(doiPreferences.isUseCustom());
Expand Down Expand Up @@ -159,7 +171,14 @@ public void storeSettings() {
importerPreferences.setWarnAboutDuplicatesOnImport(warnAboutDuplicatesOnImportProperty.getValue());
filePreferences.setDownloadLinkedFiles(shouldDownloadLinkedOnlineFiles.getValue());
filePreferences.setKeepDownloadUrl(shouldkeepDownloadUrl.getValue());
libraryPreferences.setAddImportedEntries(addImportedEntries.getValue());
if (addImportedEntriesGroupName.getValue().isEmpty() || addImportedEntriesGroupName.getValue().startsWith(" ")) {
libraryPreferences.setAddImportedEntriesGroupName("");
} else {
libraryPreferences.setAddImportedEntriesGroupName(addImportedEntriesGroupName.getValue());
}
importerPreferences.setDefaultPlainCitationParser(defaultPlainCitationParser.getValue());

grobidPreferences.setGrobidEnabled(grobidEnabledProperty.getValue());
grobidPreferences.setGrobidUseAsked(grobidPreferences.isGrobidUseAsked());
grobidPreferences.setGrobidURL(grobidURLProperty.getValue());
Expand Down Expand Up @@ -189,6 +208,14 @@ public ObjectProperty<PlainCitationParserChoice> defaultPlainCitationParserPrope
return defaultPlainCitationParser;
}

public BooleanProperty getAddImportedEntries() {
return addImportedEntries;
}

public StringProperty getAddImportedEntriesGroupName() {
return addImportedEntriesGroupName;
}

public BooleanProperty useCustomDOIProperty() {
return this.useCustomDOIProperty;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
<CheckBox fx:id="warnAboutDuplicatesOnImport" text="%Warn about duplicates on import"/>
<CheckBox fx:id="downloadLinkedOnlineFiles" text="%Download referenced files (PDFs, ...)"/>
<CheckBox fx:id="keepDownloadUrl" text="%Store url for downloaded file" />
<HBox alignment="CENTER_LEFT" spacing="10.0">
<CheckBox fx:id="addImportedEntries" text="%Add imported entries to group"/>
<TextField fx:id="addImportedEntriesGroupName" HBox.hgrow="ALWAYS"/>
</HBox>
<HBox alignment="BASELINE_LEFT" spacing="10">
<Label text="%Default plain citation parser"/>
<ComboBox fx:id="defaultPlainCitationParser" HBox.hgrow="ALWAYS"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package org.jabref.gui.groups;

import java.util.EnumSet;
import java.util.List;
import java.util.Optional;

import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.util.CustomLocalDragboard;
import org.jabref.logic.LibraryPreferences;
import org.jabref.logic.ai.AiService;
import org.jabref.logic.util.CurrentThreadTaskExecutor;
import org.jabref.logic.util.TaskExecutor;
Expand Down Expand Up @@ -47,6 +49,13 @@ void setUp() {
preferences = mock(GuiPreferences.class);
dialogService = mock(DialogService.class, Answers.RETURNS_DEEP_STUBS);

when(preferences.getLibraryPreferences()).thenReturn(new LibraryPreferences(
databaseContext.getMode(),
false,
false,
false,
"Imported entries"
));
when(preferences.getGroupsPreferences()).thenReturn(new GroupsPreferences(
EnumSet.noneOf(GroupViewMode.class),
true,
Expand Down Expand Up @@ -184,4 +193,27 @@ void shouldNotAddSuggestedGroupsWhenAllExist() {
assertEquals(2, rootGroup.getChildren().size());
assertTrue(rootGroup.hasAllSuggestedGroups());
}

@Test
void shouldCreateImportedEntriesGroupWhenEnabled() {
preferences.getLibraryPreferences().setAddImportedEntries(true);

List<GroupNodeViewModel> prevModel = groupTree.rootGroupProperty().getValue().getChildren();
GroupTreeViewModel model = new GroupTreeViewModel(stateManager, dialogService, mock(AiService.class), preferences, taskExecutor, new CustomLocalDragboard());
String actualGrpName = model.rootGroupProperty().getValue().getChildren().getFirst().getDisplayName();

assertTrue(prevModel.isEmpty());
assertEquals("Imported entries", actualGrpName);
}

@Test
void shouldReflectUpdatedNameForImportedEntriesGroup() {
preferences.getLibraryPreferences().setAddImportedEntries(true);
preferences.getLibraryPreferences().setAddImportedEntriesGroupName("Review list");

GroupTreeViewModel model = new GroupTreeViewModel(stateManager, dialogService, mock(AiService.class), preferences, taskExecutor, new CustomLocalDragboard());
String actualGrpName = model.rootGroupProperty().getValue().getChildren().getFirst().getDisplayName();

assertEquals("Review list", actualGrpName);
}
}
Loading
Loading