From 2ac533544cf327347e83257f3949cb66955b7acc Mon Sep 17 00:00:00 2001 From: Roc <1844478+ror3d@users.noreply.github.com> Date: Mon, 18 Mar 2024 17:12:49 +0000 Subject: [PATCH] Improve citation relations (#11016) * Collect DOI and publication type from semantich scholar to be able to expand the information of the new entries later by search through DOI * Include abstract in the request. This lets the GUI show the abstract since that was implemented already. Refactor api request string since most of it is shared * Add button to open the relation paper's DOI URL. Fix DOI for some ArXiv entries. * Don't show the open link button if there is no link to open. * Make field value null error a bit more useful * Include SemanticScholar url in the request and use it as the URL field. * Add changes to changelog * Change tooltip text to an existing, more informative one * Run rewriter to fix pull request * improve url optional handling --------- Co-authored-by: Siedlerchr --- CHANGELOG.md | 1 + .../CitationRelationsTab.java | 34 ++++++- .../semanticscholar/PaperDetails.java | 90 +++++++++++++++++++ .../SemanticScholarFetcher.java | 23 ++--- .../java/org/jabref/model/entry/BibEntry.java | 2 +- 5 files changed, 130 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d506d56736..306e7ae35d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We enhanced the dialog for adding new fields in the content selector with a selection box containing a list of standard fields. [#10912](https://github.com/JabRef/jabref/pull/10912) - We store the citation relations in an LRU cache to avoid bloating the memory and out-of-memory exceptions. [#10958](https://github.com/JabRef/jabref/issues/10958) - Keywords filed are now displayed as tags. [#10910](https://github.com/JabRef/jabref/pull/10910) +- Citation relations now get more information, and have quick access to view the articles in a browser without adding them to the library [#10869](https://github.com/JabRef/jabref/issues/10869) ### Fixed diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java index b3027a6f9b5..7f8fc40d15c 100644 --- a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java @@ -1,5 +1,7 @@ package org.jabref.gui.entryeditor.citationrelationtab; +import java.io.IOException; +import java.net.URI; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -28,6 +30,7 @@ import org.jabref.gui.Globals; import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; +import org.jabref.gui.desktop.JabRefDesktop; import org.jabref.gui.entryeditor.EntryEditorPreferences; import org.jabref.gui.entryeditor.EntryEditorTab; import org.jabref.gui.entryeditor.citationrelationtab.semanticscholar.CitationFetcher; @@ -40,6 +43,9 @@ import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.identifier.DOI; +import org.jabref.model.strings.StringUtil; import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; @@ -201,6 +207,8 @@ private void styleFetchedListView(CheckListView listView) HBox hContainer = new HBox(); hContainer.prefWidthProperty().bind(listView.widthProperty().subtract(25)); + VBox vContainer = new VBox(); + if (entry.isLocal()) { Button jumpTo = IconTheme.JabRefIcons.LINK.asButton(); jumpTo.setTooltip(new Tooltip(Localization.lang("Jump to entry in library"))); @@ -211,7 +219,7 @@ private void styleFetchedListView(CheckListView listView) citingTask.cancel(); citedByTask.cancel(); }); - hContainer.getChildren().addAll(entryNode, separator, jumpTo); + vContainer.getChildren().add(jumpTo); } else { ToggleButton addToggle = IconTheme.JabRefIcons.ADD.asToggleButton(); addToggle.setTooltip(new Tooltip(Localization.lang("Select entry"))); @@ -224,8 +232,28 @@ private void styleFetchedListView(CheckListView listView) }); addToggle.getStyleClass().add("addEntryButton"); addToggle.selectedProperty().bindBidirectional(listView.getItemBooleanProperty(entry)); - hContainer.getChildren().addAll(entryNode, separator, addToggle); + vContainer.getChildren().add(addToggle); + } + + if (entry.entry().getDOI().isPresent() || entry.entry().getField(StandardField.URL).isPresent()) { + Button openWeb = IconTheme.JabRefIcons.OPEN_LINK.asButton(); + openWeb.setTooltip(new Tooltip(Localization.lang("Open URL or DOI"))); + openWeb.setOnMouseClicked(event -> { + String url = entry.entry().getDOI().flatMap(DOI::getExternalURI).map(URI::toString) + .or(() -> entry.entry().getField(StandardField.URL)).orElse(""); + if (StringUtil.isNullOrEmpty(url)) { + return; + } + try { + JabRefDesktop.openBrowser(url, preferencesService.getFilePreferences()); + } catch (IOException ex) { + dialogService.notify(Localization.lang("Unable to open link.")); + } + }); + vContainer.getChildren().addLast(openWeb); } + + hContainer.getChildren().addAll(entryNode, separator, vContainer); hContainer.getStyleClass().add("entry-container"); return hContainer; @@ -392,8 +420,6 @@ private void showNodes(Node... nodes) { Arrays.stream(nodes).forEach(node -> node.setVisible(true)); } - // Absolute-phase phenomena in photoionization with few-cycle laser pulses - /** * Function to import selected entries to the database. Also writes the entries to import to the CITING/CITED field * diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/PaperDetails.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/PaperDetails.java index 48db00777cf..58ba269616e 100644 --- a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/PaperDetails.java +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/PaperDetails.java @@ -1,18 +1,28 @@ package org.jabref.gui.entryeditor.citationrelationtab.semanticscholar; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.types.StandardEntryType; + +import com.google.gson.annotations.SerializedName; public class PaperDetails { private String paperId; private String title; private String year; + + @SerializedName("abstract") + private String abstr; + private String url; private int citationCount; private int referenceCount; private List authors; + private List publicationTypes; + private Map externalIds; public String getPaperId() { return paperId; @@ -38,6 +48,22 @@ public void setYear(String year) { this.year = year; } + public String getAbstract() { + return abstr; + } + + public void setAbstract(String abstr) { + this.abstr = abstr; + } + + public String getURL() { + return url; + } + + public void setURL(String url) { + this.url = url; + } + public int getCitationCount() { return citationCount; } @@ -58,6 +84,56 @@ public List getAuthors() { return authors; } + public String getPublicationType() { + if (publicationTypes == null || publicationTypes.isEmpty()) { + return "Misc"; + } + if (publicationTypes.contains("Conference")) { + return "InProceedings"; + } else if (publicationTypes.contains("JournalArticle")) { + return "Article"; + } else { + return switch (publicationTypes.getFirst()) { + case "Review" -> + "Misc"; + case "CaseReport" -> + "Report"; + case "ClinicalTrial" -> + "Report"; + case "Dataset" -> + "Dataset"; + case "Editorial" -> + "Misc"; + case "LettersAndComments" -> + "Misc"; + case "MetaAnalysis" -> + "Article"; + case "News" -> + "Misc"; + case "Study" -> + "Article"; + case "Book" -> + "Book"; + case "BookSection" -> + "InBook"; + default -> + "Misc"; + }; + } + } + + public String getDOI() { + if (externalIds != null) { + if (externalIds.containsKey("DOI")) { + return externalIds.get("DOI"); + } else if (externalIds.containsKey("ArXiv")) { + // Some ArXiv articles don't return the DOI, even though it's easy to obtain from the ArXiv ID + return "10.48550/arXiv." + externalIds.get("ArXiv"); + } + } + return ""; + } + public BibEntry toBibEntry() { BibEntry bibEntry = new BibEntry(); bibEntry.setField(StandardField.TITLE, getTitle()); @@ -70,6 +146,20 @@ public BibEntry toBibEntry() { .collect(Collectors.joining(" and ")); bibEntry.setField(StandardField.AUTHOR, authors); + bibEntry.setType(StandardEntryType.valueOf(getPublicationType())); + + if (getDOI() != null) { + bibEntry.setField(StandardField.DOI, getDOI()); + } + + if (getURL() != null) { + bibEntry.setField(StandardField.URL, getURL()); + } + + if (getAbstract() != null) { + bibEntry.setField(StandardField.ABSTRACT, getAbstract()); + } + return bibEntry; } diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java index e263d27e12f..1463d22aa9c 100644 --- a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/semanticscholar/SemanticScholarFetcher.java @@ -25,17 +25,17 @@ public SemanticScholarFetcher(ImporterPreferences importerPreferences) { this.importerPreferences = importerPreferences; } + public String getAPIUrl(String entry_point, BibEntry entry) { + return SEMANTIC_SCHOLAR_API + "paper/" + "DOI:" + entry.getDOI().orElseThrow().getDOI() + "/" + entry_point + + "?fields=" + "title,authors,year,citationCount,referenceCount,externalIds,publicationTypes,abstract,url" + + "&limit=1000"; + } + @Override public List searchCitedBy(BibEntry entry) throws FetcherException { if (entry.getDOI().isPresent()) { - StringBuilder urlBuilder = new StringBuilder(SEMANTIC_SCHOLAR_API) - .append("paper/") - .append("DOI:").append(entry.getDOI().get().getDOI()) - .append("/citations") - .append("?fields=").append("title,authors,year,citationCount,referenceCount") - .append("&limit=1000"); try { - URL citationsUrl = URI.create(urlBuilder.toString()).toURL(); + URL citationsUrl = URI.create(getAPIUrl("citations", entry)).toURL(); URLDownload urlDownload = new URLDownload(citationsUrl); String apiKey = getApiKey(); @@ -58,15 +58,8 @@ public List searchCitedBy(BibEntry entry) throws FetcherException { @Override public List searchCiting(BibEntry entry) throws FetcherException { if (entry.getDOI().isPresent()) { - StringBuilder urlBuilder = new StringBuilder(SEMANTIC_SCHOLAR_API) - .append("paper/") - .append("DOI:").append(entry.getDOI().get().getDOI()) - .append("/references") - .append("?fields=") - .append("title,authors,year,citationCount,referenceCount") - .append("&limit=1000"); try { - URL referencesUrl = URI.create(urlBuilder.toString()).toURL(); + URL referencesUrl = URI.create(getAPIUrl("references", entry)).toURL(); URLDownload urlDownload = new URLDownload(referencesUrl); String apiKey = getApiKey(); if (!apiKey.isEmpty()) { diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java index 56fc9302796..3e26335a11e 100644 --- a/src/main/java/org/jabref/model/entry/BibEntry.java +++ b/src/main/java/org/jabref/model/entry/BibEntry.java @@ -602,7 +602,7 @@ public void setField(Map fields) { */ public Optional setField(Field field, String value, EntriesEventSource eventSource) { Objects.requireNonNull(field, "field name must not be null"); - Objects.requireNonNull(value, "field value must not be null"); + Objects.requireNonNull(value, "field value for field " + field.getName() + " must not be null"); Objects.requireNonNull(eventSource, "field eventSource must not be null"); if (value.isEmpty()) {