Skip to content

Add a new field for citation count #13531

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 42 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
dcc6076
first changes
SalvadorRomo Jul 8, 2025
1a76c57
partial change
SalvadorRomo Jul 8, 2025
aaef2ce
add fields to GUI
SalvadorRomo Jul 11, 2025
150bc2c
finish change
SalvadorRomo Jul 12, 2025
fe6b48d
fix citation count identifier
SalvadorRomo Jul 12, 2025
23fbd53
adding tests
SalvadorRomo Jul 12, 2025
b37475d
Merge branch 'JabRef:main' into fix-for-issue-13477
SalvadorRomo Jul 12, 2025
f4fa327
add more testing
SalvadorRomo Jul 12, 2025
1193823
merge files
SalvadorRomo Jul 12, 2025
44de54c
remove unwanted logic
SalvadorRomo Jul 12, 2025
05aa416
add entry to change log
SalvadorRomo Jul 12, 2025
15baed1
resolve comments
SalvadorRomo Jul 12, 2025
19923b8
resolve comments
SalvadorRomo Jul 12, 2025
db2ffd2
resolve conflicts
SalvadorRomo Jul 12, 2025
cd836e7
enhance citation count change log
SalvadorRomo Jul 12, 2025
a90024c
enhance citation count change log
SalvadorRomo Jul 12, 2025
9c3facb
change assertion style
SalvadorRomo Jul 12, 2025
84af44f
resolve conflicts
SalvadorRomo Jul 12, 2025
1d265d3
resolve conflicts
SalvadorRomo Jul 12, 2025
8bfc988
resolve conflicts
SalvadorRomo Jul 12, 2025
83709c9
resolve test errors
SalvadorRomo Jul 12, 2025
4333732
resolve conflicts
SalvadorRomo Jul 12, 2025
18f9de0
fix branch
SalvadorRomo Jul 13, 2025
91c8c62
fix test
SalvadorRomo Jul 13, 2025
0cf3850
remove unused class
SalvadorRomo Jul 13, 2025
e4fdd97
merge incoming changes
SalvadorRomo Jul 13, 2025
e1928d8
resolve comment
SalvadorRomo Jul 13, 2025
18e51bd
Update CHANGELOG.md
SalvadorRomo Jul 14, 2025
d12e8e4
resolve comments
SalvadorRomo Jul 14, 2025
1f7f5cf
merge master
SalvadorRomo Jul 14, 2025
ba09528
fix comments
SalvadorRomo Jul 14, 2025
25e3649
fix comments
SalvadorRomo Jul 14, 2025
4e0c1af
fix comments
SalvadorRomo Jul 14, 2025
1accb7c
fix issues
SalvadorRomo Jul 14, 2025
85190f9
Merge branch 'main' into fix-for-issue-13477
SalvadorRomo Jul 14, 2025
371f9f6
Merge branch 'main' into fix-for-issue-13477
SalvadorRomo Jul 15, 2025
3074fde
Merge branch 'main' into fix-for-issue-13477
koppor Jul 15, 2025
41deb2b
fix comments
SalvadorRomo Jul 15, 2025
fa1297f
remove unused import
SalvadorRomo Jul 15, 2025
4f1250d
fix comments
SalvadorRomo Jul 16, 2025
49d6d47
fix test cases
SalvadorRomo Jul 16, 2025
827b4a7
Merge branch 'main' into fix-for-issue-13477
SalvadorRomo Jul 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package org.jabref.gui.fieldeditors;

import javax.swing.undo.UndoManager;
Copy link

Choose a reason for hiding this comment

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

Using Swing components in a JavaFX application violates the architectural decision to use only JavaFX. The UndoManager should be replaced with a JavaFX-specific implementation.

Copy link
Member

Choose a reason for hiding this comment

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

JabRef uses this - which is OK.


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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
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;

public class CitationCountEditorViewModel extends AbstractEditorViewModel {
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<String> fieldAux = entry.getField(field);
Copy link
Member

Choose a reason for hiding this comment

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

aux is very strange. Maybe name it fieldContent

BackgroundTask.wrap(() -> searchCitationsRelationsService.getCitationCount(this.entry, fieldAux))
.onRunning(() -> fetchCitationCountInProgress.setValue(true))
.onFinished(() -> fetchCitationCountInProgress.setValue(false))
.onFailure(e ->
dialogService.showErrorDialogAndWait(Localization.lang("Error Occurred")))
Copy link
Member

Choose a reason for hiding this comment

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

This is not JabRef's style. The text does not contain any information what the user should do.

  1. Rename string to Error occurred (also in JabRef_en.properties`)
  2. dialogService.notify(...)
  3. LOGGER.error("Error while fetching citation count", e)

With 3, there is a log entry, and users can see it in the log viewer accessible in the help menu.

.onSuccess(identifier -> {
entry.setField(field, String.valueOf(identifier));
}).executeWith(taskExecutor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<BibEntry> entry = Optional.empty();

public IdentifierEditor(Field field,
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import org.jabref.gui.fieldeditors.EditorTextField?>
<?import org.jabref.gui.icon.JabRefIconView?>
<fx:root xmlns:fx="http://javafx.com/fxml/1" type="HBox" xmlns="http://javafx.com/javafx/8.0.112"
fx:controller="org.jabref.gui.fieldeditors.CitationCountEditor">
<EditorTextField fx:id="textField" prefHeight="0.0" HBox.hgrow="ALWAYS"/>
<Button fx:id="fetchCitationCountButton" onAction="#fetchCitationCount" styleClass="icon-button">
<graphic>
<StackPane>
<JabRefIconView glyph="LOOKUP_IDENTIFIER"
visible="${controller.viewModel.fetchCitationCountInProgress == false}"/>
Copy link
Member

Choose a reason for hiding this comment

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

Use !... instead of == false

<ProgressIndicator maxHeight="12.0" maxWidth="12.0"
visible="${controller.viewModel.fetchCitationCountInProgress}"/>
</StackPane>
</graphic>
</Button>
</fx:root>
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -83,6 +84,17 @@ public List<BibEntry> searchCitations(BibEntry cited) {
return relationsRepository.readCitations(cited);
}

public int getCitationCount(BibEntry citationCounted, Optional<String> actualFieldValue) throws FetcherException {
boolean isFetchingAllowed = actualFieldValue.isEmpty() ||
relationsRepository.isCitationsUpdatable(citationCounted);
if (isFetchingAllowed) {
Optional<Integer> citationCountResult = citationFetcher.searchCitationCount(citationCounted);
return citationCountResult.orElse(0);
}
assert actualFieldValue.isPresent();
return Integer.parseInt(actualFieldValue.get());
}

public void close() {
relationsRepository.close();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.jabref.logic.importer.fetcher.citation;

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

import org.jabref.logic.importer.FetcherException;
import org.jabref.logic.importer.fetcher.citation.semanticscholar.PaperDetails;
import org.jabref.model.entry.BibEntry;

/**
Expand Down Expand Up @@ -40,6 +42,14 @@ enum SearchType {
*/
List<BibEntry> 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 PaperDetails}, that includes citation count field (may be empty)
Copy link
Member

Choose a reason for hiding this comment

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

Ajdust JavaDoc to actual return value (Integer is not PaperDetails)

*/
Optional<Integer> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<BibEntry> searchCitedBy(BibEntry entry) throws FetcherException {
if (entry.getDOI().isEmpty()) {
Expand Down Expand Up @@ -84,6 +91,27 @@ public List<BibEntry> searchCitedBy(BibEntry entry) throws FetcherException {
.map(referenceDataItem -> referenceDataItem.getCitedPaper().toBibEntry()).toList();
}

@Override
public Optional<Integer> searchCitationCount(BibEntry entry) throws FetcherException {
if (entry.getDOI().isEmpty()) {
return Optional.empty();
}
URL referencesUrl;
try {
referencesUrl = URLUtil.create(getUrlForCitationCount(entry));
Copy link

Choose a reason for hiding this comment

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

Variable name 'referencesUrl' is misleading as it's used for citation count URL, not references. This reduces code readability and maintainability.

} 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ private static Set<Field> getAllFields() {
* separate preferences object
*/
public static List<Field> getDefaultGeneralFields() {
List<Field> defaultGeneralFields = new ArrayList<>(Arrays.asList(StandardField.DOI, StandardField.CROSSREF, StandardField.KEYWORDS, StandardField.EPRINT, StandardField.URL, StandardField.FILE, StandardField.GROUPS, StandardField.OWNER, StandardField.TIMESTAMP));
List<Field> 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ public enum StandardField implements Field {
OWNER("owner"),
TIMESTAMP("timestamp", FieldProperty.DATE),
CREATIONDATE("creationdate", FieldProperty.DATE),
CITATIONCOUNT("Citation count"),
Copy link
Member

Choose a reason for hiding this comment

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

Move at the end - the two fields before and the one after is related to date - this one not.

Copy link
Author

Choose a reason for hiding this comment

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

Made this change, but seems to impact the open office tests, seems the order of this contstants impact how the output will be in files, so don't konw what you think about it

Copy link
Member

Choose a reason for hiding this comment

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

Please show the excerpt here.

I think, you ened to adapt jablib/src/test/resources/org/jabref/logic/exporter/OldOpenOfficeCalcExportFormatContentSingleEntry.xml accordingly. Which is OK - you add a thing there (and we have the discussion on that there. Maybe, this is really required)

(And please let us resolve the conversations so that we save one click while reviewing) - otherwise, I need to open all resolved conversations)

MODIFICATIONDATE("modificationdate", FieldProperty.DATE);

public static final Set<Field> AUTOMATIC_FIELDS = Set.of(OWNER, TIMESTAMP, CREATIONDATE, MODIFICATIONDATE);
Expand Down
Loading