diff --git a/CHANGELOG.md b/CHANGELOG.md index 67855ca84f8..e8e21da7a06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Added +- We added a field for the citation count field on the General tab. [#13477](https://github.com/JabRef/jabref/issues/13477) - We added focus on the field Link in the "Add file link" dialog. [#13486](https://github.com/JabRef/jabref/issues/13486) - We introduced a settings parameter to manage citations' relations local storage time-to-live with a default value set to 30 days. [#11189](https://github.com/JabRef/jabref/issues/11189) - We distribute arm64 images for Linux. [#10842](https://github.com/JabRef/jabref/issues/10842) diff --git a/jabgui/src/main/java/org/jabref/gui/fieldeditors/CitationCountEditor.java b/jabgui/src/main/java/org/jabref/gui/fieldeditors/CitationCountEditor.java new file mode 100644 index 00000000000..68544e68f91 --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/fieldeditors/CitationCountEditor.java @@ -0,0 +1,84 @@ +package org.jabref.gui.fieldeditors; + +import javax.swing.undo.UndoManager; + +import javafx.fxml.FXML; +import javafx.scene.Parent; +import javafx.scene.control.Button; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.HBox; + +import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.autocompleter.SuggestionProvider; +import org.jabref.gui.fieldeditors.contextmenu.DefaultMenu; +import org.jabref.gui.preferences.GuiPreferences; +import org.jabref.logic.citation.SearchCitationsRelationsService; +import org.jabref.logic.integrity.FieldCheckers; +import org.jabref.logic.l10n.Localization; +import org.jabref.logic.util.TaskExecutor; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.Field; + +import com.airhacks.afterburner.injection.Injector; +import com.airhacks.afterburner.views.ViewLoader; +import jakarta.inject.Inject; + +public class CitationCountEditor extends HBox implements FieldEditorFX { + @FXML private CitationCountEditorViewModel viewModel; + @FXML private EditorTextField textField; + @FXML private Button fetchCitationCountButton; + + @Inject private DialogService dialogService; + @Inject private GuiPreferences preferences; + @Inject private UndoManager undoManager; + @Inject private TaskExecutor taskExecutor; + @Inject private StateManager stateManager; + @Inject private SearchCitationsRelationsService searchCitationsRelationsService; + + public CitationCountEditor(Field field, + SuggestionProvider suggestionProvider, + FieldCheckers fieldCheckers) { + Injector.registerExistingAndInject(this); + this.viewModel = new CitationCountEditorViewModel( + field, + suggestionProvider, + fieldCheckers, + taskExecutor, + dialogService, + undoManager, + stateManager, + preferences, + searchCitationsRelationsService); + + ViewLoader.view(this) + .root(this) + .load(); + + textField.textProperty().bindBidirectional(viewModel.textProperty()); + + fetchCitationCountButton.setTooltip( + new Tooltip(Localization.lang("Look up %0", field.getDisplayName()))); + textField.initContextMenu(new DefaultMenu(textField), preferences.getKeyBindingRepository()); + new EditorValidator(preferences).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textField); + } + + @FXML + private void fetchCitationCount() { + viewModel.getCitationCount(); + } + + public CitationCountEditorViewModel getViewModel() { + return viewModel; + } + + @Override + public void bindToEntry(BibEntry entry) { + viewModel.bindToEntry(entry); + } + + @Override + public Parent getNode() { + return this; + } +} diff --git a/jabgui/src/main/java/org/jabref/gui/fieldeditors/CitationCountEditorViewModel.java b/jabgui/src/main/java/org/jabref/gui/fieldeditors/CitationCountEditorViewModel.java new file mode 100644 index 00000000000..6e1909f5bf6 --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/fieldeditors/CitationCountEditorViewModel.java @@ -0,0 +1,74 @@ +package org.jabref.gui.fieldeditors; + +import java.util.Optional; + +import javax.swing.undo.UndoManager; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; + +import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.autocompleter.SuggestionProvider; +import org.jabref.gui.preferences.GuiPreferences; +import org.jabref.logic.citation.SearchCitationsRelationsService; +import org.jabref.logic.integrity.FieldCheckers; +import org.jabref.logic.l10n.Localization; +import org.jabref.logic.util.BackgroundTask; +import org.jabref.logic.util.TaskExecutor; +import org.jabref.model.entry.field.Field; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CitationCountEditorViewModel extends AbstractEditorViewModel { + private static final Logger LOGGER = LoggerFactory.getLogger(CitationCountEditorViewModel.class); + + protected final BooleanProperty fetchCitationCountInProgress = new SimpleBooleanProperty(false); + private final TaskExecutor taskExecutor; + private final DialogService dialogService; + private final UndoManager undoManager; + private final StateManager stateManager; + private final GuiPreferences preferences; + private final SearchCitationsRelationsService searchCitationsRelationsService; + public CitationCountEditorViewModel( + Field field, + SuggestionProvider suggestionProvider, + FieldCheckers fieldCheckers, + TaskExecutor taskExecutor, + DialogService dialogService, + UndoManager undoManager, + StateManager stateManager, + GuiPreferences preferences, + SearchCitationsRelationsService searchCitationsRelationsService) { + super(field, suggestionProvider, fieldCheckers, undoManager); + this.taskExecutor = taskExecutor; + this.dialogService = dialogService; + this.undoManager = undoManager; + this.stateManager = stateManager; + this.preferences = preferences; + this.searchCitationsRelationsService = searchCitationsRelationsService; + } + + public BooleanProperty fetchCitationCountInProgressProperty() { + return fetchCitationCountInProgress; + } + + public boolean getFetchCitationCountInProgress() { + return fetchCitationCountInProgress.get(); + } + + public void getCitationCount() { + Optional fieldContent = entry.getField(field); + BackgroundTask.wrap(() -> searchCitationsRelationsService.getCitationCount(this.entry, fieldContent)) + .onRunning(() -> fetchCitationCountInProgress.setValue(true)) + .onFinished(() -> fetchCitationCountInProgress.setValue(false)) + .onFailure(e -> { + dialogService.notify(Localization.lang("Error occurred when getting citation count, please try again or check the identifier.")); + LOGGER.error("Error while fetching citation count", e); + }) + .onSuccess(identifier -> { + entry.setField(field, String.valueOf(identifier)); + }).executeWith(taskExecutor); + } +} diff --git a/jabgui/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/jabgui/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java index ac5a7096f24..9583f0e87c2 100644 --- a/jabgui/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java +++ b/jabgui/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java @@ -75,6 +75,8 @@ public static FieldEditorFX getForField(final Field field, } else if (fieldProperties.contains(FieldProperty.IDENTIFIER) && field != StandardField.PMID || field == StandardField.ISBN) { // Identifier editor does not support PMID, therefore excluded at the condition above return new IdentifierEditor(field, suggestionProvider, fieldCheckers); + } else if (field == StandardField.CITATIONCOUNT) { + return new CitationCountEditor(field, suggestionProvider, fieldCheckers); } else if (field == StandardField.ISSN) { return new ISSNEditor(field, suggestionProvider, fieldCheckers, undoAction, redoAction); } else if (field == StandardField.OWNER) { diff --git a/jabgui/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java b/jabgui/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java index 105472c79be..c603f7b1b95 100644 --- a/jabgui/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java +++ b/jabgui/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java @@ -45,7 +45,6 @@ public class IdentifierEditor extends HBox implements FieldEditorFX { @Inject private GuiPreferences preferences; @Inject private UndoManager undoManager; @Inject private StateManager stateManager; - private Optional entry = Optional.empty(); public IdentifierEditor(Field field, @@ -63,6 +62,7 @@ public IdentifierEditor(Field field, this.viewModel = new ISBNIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferences, undoManager, stateManager); case EPRINT -> this.viewModel = new EprintIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferences, undoManager); + // TODO: Add support for PMID case null, default -> { assert field != null; diff --git a/jabgui/src/main/resources/org/jabref/gui/fieldeditors/CitationCountEditor.fxml b/jabgui/src/main/resources/org/jabref/gui/fieldeditors/CitationCountEditor.fxml new file mode 100644 index 00000000000..89f7f29bffa --- /dev/null +++ b/jabgui/src/main/resources/org/jabref/gui/fieldeditors/CitationCountEditor.fxml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + diff --git a/jablib/src/main/java/org/jabref/logic/citation/SearchCitationsRelationsService.java b/jablib/src/main/java/org/jabref/logic/citation/SearchCitationsRelationsService.java index 95db8900c1b..fb80968bebc 100644 --- a/jablib/src/main/java/org/jabref/logic/citation/SearchCitationsRelationsService.java +++ b/jablib/src/main/java/org/jabref/logic/citation/SearchCitationsRelationsService.java @@ -1,6 +1,7 @@ package org.jabref.logic.citation; import java.util.List; +import java.util.Optional; import org.jabref.logic.bibtex.FieldPreferences; import org.jabref.logic.citation.repository.BibEntryCitationsAndReferencesRepository; @@ -83,6 +84,17 @@ public List searchCitations(BibEntry cited) { return relationsRepository.readCitations(cited); } + public int getCitationCount(BibEntry citationCounted, Optional actualFieldValue) throws FetcherException { + boolean isFetchingAllowed = actualFieldValue.isEmpty() || + relationsRepository.isCitationsUpdatable(citationCounted); + if (isFetchingAllowed) { + Optional citationCountResult = citationFetcher.searchCitationCount(citationCounted); + return citationCountResult.orElse(0); + } + assert actualFieldValue.isPresent(); + return Integer.parseInt(actualFieldValue.get()); + } + public void close() { relationsRepository.close(); } diff --git a/jablib/src/main/java/org/jabref/logic/importer/fetcher/citation/CitationFetcher.java b/jablib/src/main/java/org/jabref/logic/importer/fetcher/citation/CitationFetcher.java index 959f5fa275a..84d44c0199b 100644 --- a/jablib/src/main/java/org/jabref/logic/importer/fetcher/citation/CitationFetcher.java +++ b/jablib/src/main/java/org/jabref/logic/importer/fetcher/citation/CitationFetcher.java @@ -1,6 +1,7 @@ package org.jabref.logic.importer.fetcher.citation; import java.util.List; +import java.util.Optional; import org.jabref.logic.importer.FetcherException; import org.jabref.model.entry.BibEntry; @@ -40,6 +41,14 @@ enum SearchType { */ List searchCiting(BibEntry entry) throws FetcherException; + /** + * Get the paper details that includes citation count field for a given {@link BibEntry}. + * + * @param entry entry to search citation count field + * @return returns a {@link Integer} for citation count field (may be empty) + */ + Optional searchCitationCount(BibEntry entry) throws FetcherException; + /** * Returns the localized name of this fetcher. * The title can be used to display the fetcher in the menu and in the side pane. diff --git a/jablib/src/main/java/org/jabref/logic/importer/fetcher/citation/semanticscholar/SemanticScholarCitationFetcher.java b/jablib/src/main/java/org/jabref/logic/importer/fetcher/citation/semanticscholar/SemanticScholarCitationFetcher.java index 646ae02952c..fe9ac59d3e7 100644 --- a/jablib/src/main/java/org/jabref/logic/importer/fetcher/citation/semanticscholar/SemanticScholarCitationFetcher.java +++ b/jablib/src/main/java/org/jabref/logic/importer/fetcher/citation/semanticscholar/SemanticScholarCitationFetcher.java @@ -3,6 +3,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.List; +import java.util.Optional; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.ImporterPreferences; @@ -33,6 +34,12 @@ public String getAPIUrl(String entry_point, BibEntry entry) { + "&limit=1000"; } + public String getUrlForCitationCount(BibEntry entry) { + return SEMANTIC_SCHOLAR_API + "paper/" + "DOI:" + entry.getDOI().orElseThrow().asString() + + "?fields=" + "citationCount" + + "&limit=1"; + } + @Override public List searchCitedBy(BibEntry entry) throws FetcherException { if (entry.getDOI().isEmpty()) { @@ -84,6 +91,27 @@ public List searchCitedBy(BibEntry entry) throws FetcherException { .map(referenceDataItem -> referenceDataItem.getCitedPaper().toBibEntry()).toList(); } + @Override + public Optional searchCitationCount(BibEntry entry) throws FetcherException { + if (entry.getDOI().isEmpty()) { + return Optional.empty(); + } + URL referencesUrl; + try { + referencesUrl = URLUtil.create(getUrlForCitationCount(entry)); + } catch (MalformedURLException e) { + throw new FetcherException("Malformed URL", e); + } + URLDownload urlDownload = new URLDownload(referencesUrl); + importerPreferences.getApiKey(getName()).ifPresent(apiKey -> urlDownload.addHeader("x-api-key", apiKey)); + PaperDetails paperDetails = GSON.fromJson(urlDownload.asString(), PaperDetails.class); + + if (paperDetails == null) { + return Optional.empty(); + } + return Optional.of(paperDetails.getCitationCount()); + } + @Override public String getName() { return FETCHER_NAME; diff --git a/jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java b/jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java index 2b7f2788d43..b1266e61b1d 100644 --- a/jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java +++ b/jablib/src/main/java/org/jabref/model/entry/field/FieldFactory.java @@ -231,7 +231,7 @@ private static Set getAllFields() { * separate preferences object */ public static List getDefaultGeneralFields() { - List defaultGeneralFields = new ArrayList<>(Arrays.asList(StandardField.DOI, StandardField.CROSSREF, StandardField.KEYWORDS, StandardField.EPRINT, StandardField.URL, StandardField.FILE, StandardField.GROUPS, StandardField.OWNER, StandardField.TIMESTAMP)); + List defaultGeneralFields = new ArrayList<>(Arrays.asList(StandardField.DOI, StandardField.CITATIONCOUNT, StandardField.CROSSREF, StandardField.KEYWORDS, StandardField.EPRINT, StandardField.URL, StandardField.FILE, StandardField.GROUPS, StandardField.OWNER, StandardField.TIMESTAMP)); defaultGeneralFields.addAll(EnumSet.allOf(SpecialField.class)); return defaultGeneralFields; } diff --git a/jablib/src/main/java/org/jabref/model/entry/field/StandardField.java b/jablib/src/main/java/org/jabref/model/entry/field/StandardField.java index 5b192953b4e..e4a62490a00 100644 --- a/jablib/src/main/java/org/jabref/model/entry/field/StandardField.java +++ b/jablib/src/main/java/org/jabref/model/entry/field/StandardField.java @@ -136,6 +136,7 @@ public enum StandardField implements Field { // JabRef-specific fields GROUPS("groups"), OWNER("owner"), + CITATIONCOUNT("Citation count"), TIMESTAMP("timestamp", FieldProperty.DATE), CREATIONDATE("creationdate", FieldProperty.DATE), MODIFICATIONDATE("modificationdate", FieldProperty.DATE); diff --git a/jablib/src/main/resources/l10n/JabRef_en.properties b/jablib/src/main/resources/l10n/JabRef_en.properties index 5b983088790..ee9148c8ce9 100644 --- a/jablib/src/main/resources/l10n/JabRef_en.properties +++ b/jablib/src/main/resources/l10n/JabRef_en.properties @@ -311,6 +311,7 @@ Entry\ table\ columns=Entry table columns Entry\ Title\ (Required\ to\ deliver\ recommendations.)=Entry Title (Required to deliver recommendations.) Error=Error Error\ occurred\ when\ parsing\ entry=Error occurred when parsing entry +Error\ occurred\ when\ getting\ citation\ count,\ please\ try\ again\ or\ check\ the\ identifier.=Error occurred when getting citation count, please try again or check the identifier. Error\ during\ persistence\ of\ crawling\ results.=Error during persistence of crawling results. '%0'\ exists.\ Overwrite\ file?='%0' exists. Overwrite file? Export=Export diff --git a/jablib/src/test/java/org/jabref/logic/citation/SearchCitationsRelationsServiceTest.java b/jablib/src/test/java/org/jabref/logic/citation/SearchCitationsRelationsServiceTest.java index f2ee7bc476b..41609987871 100644 --- a/jablib/src/test/java/org/jabref/logic/citation/SearchCitationsRelationsServiceTest.java +++ b/jablib/src/test/java/org/jabref/logic/citation/SearchCitationsRelationsServiceTest.java @@ -3,9 +3,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import org.jabref.logic.citation.repository.BibEntryCitationsAndReferencesRepository; import org.jabref.logic.citation.repository.BibEntryRelationsRepositoryTestHelpers; +import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.fetcher.citation.CitationFetcher; import org.jabref.logic.importer.fetcher.citation.CitationFetcherHelpersForTest; import org.jabref.model.entry.BibEntry; @@ -21,7 +23,7 @@ class SearchCitationsRelationsServiceTest { /** * Creates a mock CitationFetcher that returns specific results for citations and references */ - private CitationFetcher createMockFetcher(BibEntry targetEntry, List citationsToReturn, List referencesToReturn) { + private CitationFetcher createMockFetcher(BibEntry targetEntry, List citationsToReturn, List referencesToReturn, Integer citationCount) { return CitationFetcherHelpersForTest.Mocks.from( entry -> { if (entry == targetEntry) { @@ -34,6 +36,12 @@ private CitationFetcher createMockFetcher(BibEntry targetEntry, List c return referencesToReturn != null ? referencesToReturn : List.of(); } return List.of(); + }, + entry -> { + if (entry == targetEntry) { + return Optional.of(citationCount); + } + return Optional.empty(); } ); } @@ -44,7 +52,8 @@ private CitationFetcher createMockFetcher(BibEntry targetEntry, List c private CitationFetcher createEmptyMockFetcher() { return CitationFetcherHelpersForTest.Mocks.from( _ -> List.of(), - _ -> List.of() + _ -> List.of(), + _ -> Optional.empty() ); } @@ -74,7 +83,7 @@ void serviceShouldCallTheFetcherForCitationsWhenRepositoryIsUpdatable() { BibEntry newCitations = new BibEntry(); List citationsToReturn = List.of(newCitations); Map> citationsDatabase = HashMap.newHashMap(300); - CitationFetcher fetcher = createMockFetcher(cited, citationsToReturn, null); + CitationFetcher fetcher = createMockFetcher(cited, citationsToReturn, null, null); BibEntryCitationsAndReferencesRepository repository = BibEntryRelationsRepositoryTestHelpers.Mocks.from( _ -> citationsToReturn, citationsDatabase::put, @@ -100,8 +109,8 @@ void serviceShouldFetchCitationsIfRepositoryIsEmpty() { BibEntry newCitations = new BibEntry(); List citationsToReturn = List.of(newCitations); Map> citationsDatabase = HashMap.newHashMap(300); - CitationFetcher fetcher = createMockFetcher(cited, citationsToReturn, null); - BibEntryCitationsAndReferencesRepository repository = BibEntryRelationsRepositoryTestHelpers.Mocks.from(citationsDatabase, null); + CitationFetcher fetcher = createMockFetcher(cited, citationsToReturn, null, null); + BibEntryCitationsAndReferencesRepository repository = BibEntryRelationsRepositoryTestHelpers.Mocks.from(citationsDatabase, null, true); SearchCitationsRelationsService searchService = new SearchCitationsRelationsService(fetcher, repository); // WHEN @@ -118,7 +127,7 @@ void insertingAnEmptyCitationsShouldBePossible() { BibEntry cited = new BibEntry(); Map> citationsDatabase = new HashMap<>(); CitationFetcher fetcher = createEmptyMockFetcher(); - BibEntryCitationsAndReferencesRepository repository = BibEntryRelationsRepositoryTestHelpers.Mocks.from(citationsDatabase, null); + BibEntryCitationsAndReferencesRepository repository = BibEntryRelationsRepositoryTestHelpers.Mocks.from(citationsDatabase, null, true); SearchCitationsRelationsService searchService = new SearchCitationsRelationsService(fetcher, repository); // WHEN @@ -157,7 +166,7 @@ void serviceShouldCallTheFetcherForReferencesWhenRepositoryIsUpdatable() { BibEntry newReference = new BibEntry(); List referencesToReturn = List.of(newReference); Map> referencesDatabase = new HashMap<>(); - CitationFetcher fetcher = createMockFetcher(referencer, null, referencesToReturn); + CitationFetcher fetcher = createMockFetcher(referencer, null, referencesToReturn, null); BibEntryCitationsAndReferencesRepository repository = BibEntryRelationsRepositoryTestHelpers.Mocks.from( List::of, (_, _) -> { }, @@ -183,9 +192,9 @@ void serviceShouldFetchReferencesIfRepositoryIsEmpty() { BibEntry newCitations = new BibEntry(); List referencesToReturn = List.of(newCitations); Map> referencesDatabase = new HashMap<>(); - CitationFetcher fetcher = createMockFetcher(reference, null, referencesToReturn); + CitationFetcher fetcher = createMockFetcher(reference, null, referencesToReturn, null); BibEntryCitationsAndReferencesRepository repository = BibEntryRelationsRepositoryTestHelpers.Mocks.from( - null, referencesDatabase + null, referencesDatabase, true ); SearchCitationsRelationsService searchService = new SearchCitationsRelationsService(fetcher, repository); @@ -204,7 +213,7 @@ void insertingAnEmptyReferencesShouldBePossible() { Map> referenceDatabase = new HashMap<>(); CitationFetcher fetcher = createEmptyMockFetcher(); BibEntryCitationsAndReferencesRepository repository = BibEntryRelationsRepositoryTestHelpers.Mocks.from( - null, referenceDatabase + null, referenceDatabase, true ); SearchCitationsRelationsService searchService = new SearchCitationsRelationsService(fetcher, repository); @@ -216,5 +225,56 @@ void insertingAnEmptyReferencesShouldBePossible() { assertTrue(referenceDatabase.containsKey(referencer)); assertTrue(referenceDatabase.get(referencer).isEmpty()); } + + @Test + void serviceShouldUpdateCitationCountWithEmptyPaperDetailsResponse() throws FetcherException { + int expectedResult = 0; + BibEntry referencer = new BibEntry(); + Map> referenceDatabase = new HashMap<>(); + CitationFetcher fetcher = createEmptyMockFetcher(); + BibEntryCitationsAndReferencesRepository repository = BibEntryRelationsRepositoryTestHelpers.Mocks.from( + null, referenceDatabase, true + ); + SearchCitationsRelationsService searchService = new SearchCitationsRelationsService(fetcher, repository); + Optional field = Optional.empty(); + int citationsCount = searchService.getCitationCount(referencer, field); + assertEquals(citationsCount, expectedResult); + } + + @Test + void serviceShouldCorrectlyFetchCitationCountField() throws FetcherException { + int expectedResult = 3; + BibEntry reference = new BibEntry(); + Integer citationCount = 3; + Map> referencesDatabase = new HashMap<>(); + CitationFetcher fetcher = createMockFetcher(reference, null, null, citationCount); + + BibEntryCitationsAndReferencesRepository repository = BibEntryRelationsRepositoryTestHelpers.Mocks.from( + null, referencesDatabase, true + ); + SearchCitationsRelationsService searchService = new SearchCitationsRelationsService(fetcher, repository); + Optional field = Optional.empty(); + int citationsCount = searchService.getCitationCount(reference, field); + assertEquals(citationsCount, expectedResult); + } + + @Test + void serviceShouldUpdateBecauseIsisCitationsUpdatableTrue() throws FetcherException { + int expectedResult = 3; + BibEntry reference = new BibEntry(); + Integer citationCount = 3; + Map> referencesDatabase = new HashMap<>(); + referencesDatabase.put(reference, List.of()); + + CitationFetcher fetcher = createMockFetcher(reference, null, null, citationCount); + BibEntryCitationsAndReferencesRepository repository = BibEntryRelationsRepositoryTestHelpers.Mocks.from( + null, referencesDatabase, true + ); + + SearchCitationsRelationsService searchService = new SearchCitationsRelationsService(fetcher, repository); + Optional field = Optional.empty(); + int citationsCount = searchService.getCitationCount(reference, field); + assertEquals(citationsCount, expectedResult); + } } } diff --git a/jablib/src/test/java/org/jabref/logic/citation/repository/BibEntryRelationsRepositoryTestHelpers.java b/jablib/src/test/java/org/jabref/logic/citation/repository/BibEntryRelationsRepositoryTestHelpers.java index 3be2ce09010..1fe6cf42c8b 100644 --- a/jablib/src/test/java/org/jabref/logic/citation/repository/BibEntryRelationsRepositoryTestHelpers.java +++ b/jablib/src/test/java/org/jabref/logic/citation/repository/BibEntryRelationsRepositoryTestHelpers.java @@ -76,7 +76,7 @@ public void close() { } public static BibEntryCitationsAndReferencesRepository from( - Map> citationsDB, Map> referencesDB + Map> citationsDB, Map> referencesDB, boolean isCitationsUpdatable ) { return new BibEntryCitationsAndReferencesRepository() { @Override @@ -96,7 +96,7 @@ public boolean containsCitations(BibEntry entry) { @Override public boolean isCitationsUpdatable(BibEntry entry) { - return true; + return isCitationsUpdatable; } @Override diff --git a/jablib/src/test/java/org/jabref/logic/importer/fetcher/citation/CitationFetcherHelpersForTest.java b/jablib/src/test/java/org/jabref/logic/importer/fetcher/citation/CitationFetcherHelpersForTest.java index b84e767e186..3092048f1f0 100644 --- a/jablib/src/test/java/org/jabref/logic/importer/fetcher/citation/CitationFetcherHelpersForTest.java +++ b/jablib/src/test/java/org/jabref/logic/importer/fetcher/citation/CitationFetcherHelpersForTest.java @@ -1,15 +1,18 @@ package org.jabref.logic.importer.fetcher.citation; import java.util.List; +import java.util.Optional; import java.util.function.Function; +import org.jabref.logic.importer.FetcherException; import org.jabref.model.entry.BibEntry; public class CitationFetcherHelpersForTest { public static class Mocks { public static CitationFetcher from( Function> retrieveCitedBy, - Function> retrieveCiting + Function> retrieveCiting, + Function> retrieveCitationCount ) { return new CitationFetcher() { @Override @@ -22,6 +25,11 @@ public List searchCiting(BibEntry entry) { return retrieveCiting.apply(entry); } + @Override + public Optional searchCitationCount(BibEntry entry) throws FetcherException { + return retrieveCitationCount.apply(entry); + } + @Override public String getName() { return "Test citation fetcher"; diff --git a/jablib/src/test/java/org/jabref/logic/importer/fetcher/citation/semanticscholar/SemanticScholarCitationFetcherTest.java b/jablib/src/test/java/org/jabref/logic/importer/fetcher/citation/semanticscholar/SemanticScholarCitationFetcherTest.java index c6f1e1e4e15..34d93805cbe 100644 --- a/jablib/src/test/java/org/jabref/logic/importer/fetcher/citation/semanticscholar/SemanticScholarCitationFetcherTest.java +++ b/jablib/src/test/java/org/jabref/logic/importer/fetcher/citation/semanticscholar/SemanticScholarCitationFetcherTest.java @@ -1,6 +1,7 @@ package org.jabref.logic.importer.fetcher.citation.semanticscholar; import java.util.List; +import java.util.Optional; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.ImporterPreferences; @@ -13,7 +14,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Answers; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.mock; @FetcherTest @@ -45,4 +49,24 @@ void smoke() throws FetcherException { // Paper has more than 400 cites, but server returns "null" as data assertNotEquals(null, result); } + + @Test + void smokeCitationCount() throws FetcherException { + BibEntry entry = new BibEntry(StandardEntryType.Article) + .withCitationKey("Macht_2007") + .withField(StandardField.AUTHOR, "Macht, Michael and Mueller, Jochen") + .withField(StandardField.TITLE, "Immediate effects of chocolate on experimentally induced mood states") + .withField(StandardField.JOURNALTITLE, "Appetite") + .withField(StandardField.DATE, "2007-11") + .withField(StandardField.VOLUME, "49") + .withField(StandardField.NUMBER, "3") + .withField(StandardField.PAGES, "667--674") + .withField(StandardField.DOI, "10.1016/j.appet.2007.05.004") + .withField(StandardField.ISSN, "0195-6663") + .withField(StandardField.PUBLISHER, "Elsevier BV"); + + Optional result = fetcher.searchCitationCount(entry); + assertNotNull(result.get()); + assertThat(result.get(), greaterThan(0)); + } } diff --git a/jablib/src/test/resources/org/jabref/logic/exporter/OldOpenOfficeCalcExportFormatContentSingleEntry.xml b/jablib/src/test/resources/org/jabref/logic/exporter/OldOpenOfficeCalcExportFormatContentSingleEntry.xml index ac7e806de40..acb7cf686dd 100644 --- a/jablib/src/test/resources/org/jabref/logic/exporter/OldOpenOfficeCalcExportFormatContentSingleEntry.xml +++ b/jablib/src/test/resources/org/jabref/logic/exporter/OldOpenOfficeCalcExportFormatContentSingleEntry.xml @@ -382,6 +382,9 @@ Owner + + Citation count + Timestamp @@ -400,380 +403,382 @@ 7 - + - + - + New York, NY, USA - + - + - + - + - + - + Tony Clear - + - + - + - + - + - + - + - + - + - + - + - + - + http://doi.acm.org/10.1145/820127.820136 - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + 0097-8418 - + - + - + SIGCSE Bull. - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + 4 - + - + - + 13--14 - + - + - + - + - + - + ACM - + - + - + - + - + - + - + - + - + - + - + - + - + - + - Design and usability in security systems: daily life as a context of - use? + Design and usability in security systems: daily life as a context of use? - + - + - + - + - + - + - + - + 34 - + 2002 - + + + + - + - + - + - + - + - + - + - + - + - + - +