Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
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 @@ -21,6 +21,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We added the field `monthfiled` to the default list of fields to resolve BibTeX-Strings for [#13375](https://github.com/JabRef/jabref/issues/13375)
- We added a new ID based fetcher for [EuropePMC](https://europepmc.org/). [#13389](https://github.com/JabRef/jabref/pull/13389)
- We added an initial [cite as you write](https://retorque.re/zotero-better-bibtex/citing/cayw/) endpoint. [#13187](https://github.com/JabRef/jabref/issues/13187)
- In case no citation relation information can be fetched, we show the data providers reason. [#13549](https://github.com/JabRef/jabref/pull/13549)

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
public final class GroupChangeDetailsView extends DatabaseChangeDetailsView {

public GroupChangeDetailsView(GroupChange groupChange) {
String labelValue = "";
String labelValue;
if (groupChange.getGroupDiff().getNewGroupRoot() == null) {
labelValue = groupChange.getName() + '.';
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,9 +476,9 @@ private void executeSearch(CitationComponents citationComponents) {
citationComponents.listView().setItems(observableList);

// TODO: It should not be possible to cancel a search task that is already running for same tab
if (citingTask != null && !citingTask.isCancelled() && citationComponents.searchType() == CitationFetcher.SearchType.CITES) {
if (citationComponents.searchType() == CitationFetcher.SearchType.CITES && citingTask != null && !citingTask.isCancelled()) {
citingTask.cancel();
} else if (citedByTask != null && !citedByTask.isCancelled() && citationComponents.searchType() == CitationFetcher.SearchType.CITED_BY) {
} else if (citationComponents.searchType() == CitationFetcher.SearchType.CITED_BY && citedByTask != null && !citedByTask.isCancelled()) {
citedByTask.cancel();
}

Expand All @@ -489,10 +489,17 @@ private void executeSearch(CitationComponents citationComponents) {
observableList
))
.onFailure(exception -> {
LOGGER.error("Error while fetching citing Articles", exception);
LOGGER.error("Error while fetching {} papers", citationComponents.searchType() == CitationFetcher.SearchType.CITES ? "cited" : "citing", exception);
hideNodes(citationComponents.abortButton(), citationComponents.progress(), citationComponents.importButton());
citationComponents.listView().setPlaceholder(new Label(Localization.lang("Error while fetching citing entries: %0",
exception.getMessage())));
String labelText;
if (citationComponents.searchType() == CitationFetcher.SearchType.CITES) {
labelText = Localization.lang("Error while fetching cited entries: %0", exception.getMessage());
} else {
labelText = Localization.lang("Error while fetching citing entries: %0", exception.getMessage());
}
Label placeholder = new Label(labelText);
placeholder.setWrapText(true);
citationComponents.listView().setPlaceholder(placeholder);
citationComponents.refreshButton().setVisible(true);
dialogService.notify(exception.getMessage());
})
Expand All @@ -508,13 +515,13 @@ private BackgroundTask<List<BibEntry>> createBackgroundTask(
return switch (searchType) {
case CitationFetcher.SearchType.CITES -> {
citingTask = BackgroundTask.wrap(
() -> this.searchCitationsRelationsService.searchReferences(entry)
() -> this.searchCitationsRelationsService.searchCites(entry)
);
yield citingTask;
}
case CitationFetcher.SearchType.CITED_BY -> {
citedByTask = BackgroundTask.wrap(
() -> this.searchCitationsRelationsService.searchCitations(entry)
() -> this.searchCitationsRelationsService.searchCitedBy(entry)
);
yield citedByTask;
}
Expand All @@ -527,6 +534,8 @@ private void onSearchForRelationsSucceed(CitationComponents citationComponents,

hideNodes(citationComponents.abortButton(), citationComponents.progress());

// TODO: This could be a wrong database, because the user might have switched to another library
// If we were on fixing this, we would need to a) associate a BibEntry with a database or b) pass the database at "bindToEntry"
BibDatabase database = stateManager.getActiveDatabase().map(BibDatabaseContext::getDatabase).orElse(new BibDatabase());
observableList.setAll(
fetchedList.stream().map(entr ->
Expand All @@ -539,11 +548,11 @@ private void onSearchForRelationsSucceed(CitationComponents citationComponents,
.toList()
);

if (!observableList.isEmpty()) {
citationComponents.listView().refresh();
} else {
if (observableList.isEmpty()) {
Label placeholder = new Label(Localization.lang("No articles found"));
citationComponents.listView().setPlaceholder(placeholder);
} else {
citationComponents.listView().refresh();
}
BooleanBinding booleanBind = Bindings.isEmpty(citationComponents.listView().getCheckModel().getCheckedItems());
citationComponents.importButton().disableProperty().bind(booleanBind);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,46 +40,37 @@ public SearchCitationsRelationsService(ImporterPreferences importerPreferences,
);
}

/**
* @implNote Typically, this would be a Shim in JavaFX
*/
@VisibleForTesting
public SearchCitationsRelationsService(CitationFetcher citationFetcher,
SearchCitationsRelationsService(CitationFetcher citationFetcher,
BibEntryCitationsAndReferencesRepository repository
) {
this.citationFetcher = citationFetcher;
this.relationsRepository = repository;
}

public List<BibEntry> searchReferences(BibEntry referenced) {
boolean isFetchingAllowed = relationsRepository.isReferencesUpdatable(referenced)
|| !relationsRepository.containsReferences(referenced);
public List<BibEntry> searchCites(BibEntry referencing) throws FetcherException {
boolean isFetchingAllowed =
!relationsRepository.containsReferences(referencing) ||
relationsRepository.isReferencesUpdatable(referencing);
if (isFetchingAllowed) {
try {
List<BibEntry> referencedBy = citationFetcher.searchCiting(referenced);
relationsRepository.insertReferences(referenced, referencedBy);
} catch (FetcherException e) {
LOGGER.error("Error while fetching references for entry {}", referenced.getTitle(), e);
}
List<BibEntry> referencedBy = citationFetcher.searchCiting(referencing);
relationsRepository.insertReferences(referencing, referencedBy);
}
return relationsRepository.readReferences(referenced);
return relationsRepository.readReferences(referencing);
}

/**
* If the store was empty and nothing was fetch in any case (empty fetch, or error) then yes => empty list
* If the store was not empty and nothing was fetched after a successful fetch => the store will be erased and the returned collection will be empty
* If the store was not empty and an error occurs while fetching => will return the content of the store
*/
public List<BibEntry> searchCitations(BibEntry cited) {
boolean isFetchingAllowed = relationsRepository.isCitationsUpdatable(cited)
|| !relationsRepository.containsCitations(cited);
public List<BibEntry> searchCitedBy(BibEntry cited) throws FetcherException {
boolean isFetchingAllowed =
!relationsRepository.containsCitations(cited) ||
relationsRepository.isCitationsUpdatable(cited);
if (isFetchingAllowed) {
try {
List<BibEntry> citedBy = citationFetcher.searchCitedBy(cited);
relationsRepository.insertCitations(cited, citedBy);
} catch (FetcherException e) {
LOGGER.error("Error while fetching citations for entry {}", cited.getTitle(), e);
}
List<BibEntry> citedBy = citationFetcher.searchCitedBy(cited);
relationsRepository.insertCitations(cited, citedBy);
}
return relationsRepository.readCitations(cited);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.jabref.logic.importer.ImporterPreferences;
import org.jabref.logic.importer.fetcher.CustomizableKeyFetcher;
import org.jabref.logic.importer.fetcher.citation.CitationFetcher;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.net.URLDownload;
import org.jabref.logic.util.URLUtil;
import org.jabref.model.entry.BibEntry;
Expand All @@ -17,9 +18,14 @@
import kong.unirest.core.json.JSONObject;
import org.jooq.lambda.Unchecked;
import org.jspecify.annotations.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SemanticScholarCitationFetcher implements CitationFetcher, CustomizableKeyFetcher {
public static final String FETCHER_NAME = "Semantic Scholar Citations Fetcher";

private static final Logger LOGGER = LoggerFactory.getLogger(SemanticScholarCitationFetcher.class);

private static final String SEMANTIC_SCHOLAR_API = "https://api.semanticscholar.org/graph/v1/";

private static final Gson GSON = new Gson();
Expand All @@ -30,8 +36,8 @@ public SemanticScholarCitationFetcher(ImporterPreferences importerPreferences) {
this.importerPreferences = importerPreferences;
}

public String getAPIUrl(String entry_point, BibEntry entry) {
return SEMANTIC_SCHOLAR_API + "paper/" + "DOI:" + entry.getDOI().orElseThrow().asString() + "/" + entry_point
public String getAPIUrl(String entryPoint, BibEntry entry) {
return SEMANTIC_SCHOLAR_API + "paper/" + "DOI:" + entry.getDOI().orElseThrow().asString() + "/" + entryPoint
+ "?fields=" + "title,authors,year,citationCount,referenceCount,externalIds,publicationTypes,abstract,url"
+ "&limit=1000";
}
Expand All @@ -51,6 +57,7 @@ public List<BibEntry> searchCitedBy(BibEntry entry) throws FetcherException {
URL citationsUrl;
try {
citationsUrl = URLUtil.create(getAPIUrl("citations", entry));
LOGGER.debug("Cited URL {} ", citationsUrl);
} catch (MalformedURLException e) {
throw new FetcherException("Malformed URL", e);
}
Expand All @@ -75,15 +82,29 @@ public List<BibEntry> searchCitedBy(BibEntry entry) throws FetcherException {
URL referencesUrl;
try {
referencesUrl = URLUtil.create(getAPIUrl("references", entry));
LOGGER.debug("Citing URL {} ", referencesUrl);
} 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));
ReferencesResponse referencesResponse = GSON.fromJson(urlDownload.asString(), ReferencesResponse.class);
String response = urlDownload.asString();
ReferencesResponse referencesResponse = GSON.fromJson(response, ReferencesResponse.class);

if (referencesResponse.getData() == null) {
// Get error message from citingPaperInfo.openAccessPdf.disclaimer
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Comment simply describes what the following code does without adding any new information or reasoning, violating the principle of meaningful comments.

JSONObject responseObject = new JSONObject(response);
Optional.ofNullable(responseObject.optJSONObject("citingPaperInfo"))
.flatMap(citingPaperInfo -> Optional.ofNullable(citingPaperInfo.optJSONObject("openAccessPdf")))
.flatMap(openAccessPdf -> Optional.ofNullable(openAccessPdf.optString("disclaimer")))
.ifPresent(Unchecked.consumer(disclaimer -> {
LOGGER.debug("Received a disclaimer from Semantic Scholar: {}", disclaimer);
if (disclaimer.contains("references")) {
throw new FetcherException(Localization.lang("Restricted access to references: %0", disclaimer));
}
}
));
return List.of();
}

Expand Down
10 changes: 7 additions & 3 deletions jablib/src/main/resources/l10n/JabRef_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1698,6 +1698,12 @@ Open\ one\ before\ citing.=Open one before citing.
Select\ one\ before\ citing.=Select one before citing.
Select\ some\ before\ citing.=Select some before citing.

Citation\ relations=Citation relations
Show\ articles\ related\ by\ citation=Show articles related by citation
Error\ while\ fetching\ cited\ entries\:\ %0=Error while fetching cited entries: %0
Error\ while\ fetching\ citing\ entries\:\ %0=Error while fetching citing entries: %0
Restricted\ access\ to\ references\:\ %0=Restricted access to references: %0

Found\ identical\ ranges=Found identical ranges
Found\ overlapping\ ranges=Found overlapping ranges
Found\ touching\ ranges=Found touching ranges
Expand Down Expand Up @@ -2812,9 +2818,7 @@ Restart\ search=Restart search
Cancel\ search=Cancel search
Select\ entry=Select entry
Search\ aborted.=Search aborted.
Citation\ relations=Citation relations
Show\ articles\ related\ by\ citation=Show articles related by citation
Error\ while\ fetching\ citing\ entries\:\ %0=Error while fetching citing entries: %0

Help\ on\ external\ applications=Help on external applications
Identifier-based\ Web\ Search=Identifier-based Web Search

Expand Down
Loading