update packages
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
;;; openalex.el --- Org-ref interface to OpenAlex
|
||||
;;; openalex.el --- Org-ref interface to OpenAlex -*- lexical-binding: t; -*-
|
||||
|
||||
;;; Commentary:
|
||||
;; This is an elisp interface to OpenAlex (https://docs.openalex.org/) for org-ref.
|
||||
@@ -25,14 +25,13 @@
|
||||
;; `user-mail-address' value will be added to the queries if it exists so you
|
||||
;; will get the polite pool.
|
||||
;;
|
||||
;; This library extends the `org-ref-citation-hydra' and adds keys to get to
|
||||
;; This library extends the `org-ref-citation-menu' and adds keys to get to
|
||||
;; cited by, references and related documents in OpenAlex.
|
||||
|
||||
(require 'dash)
|
||||
(require 's)
|
||||
(require 'request)
|
||||
(require 'doi-utils)
|
||||
(require 'org-ref-citation-links)
|
||||
(require 'org-ref-utils)
|
||||
|
||||
(declare-function org-ref-possible-bibfiles "org-ref-core")
|
||||
(declare-function org-ref-find-bibliography "org-ref-core")
|
||||
@@ -40,6 +39,7 @@
|
||||
(declare-function bibtex-completion-show-entry "bibtex-completion")
|
||||
(declare-function bibtex-completion-apa-format-reference "bibtex-completion")
|
||||
(declare-function ivy-read "ivy")
|
||||
(declare-function ivy-more-chars "ivy")
|
||||
|
||||
(defcustom oa-api-key
|
||||
nil
|
||||
@@ -59,6 +59,22 @@
|
||||
(json-read)))
|
||||
|
||||
|
||||
(defun oa--params (&rest params)
|
||||
"Build API request parameters, excluding nil values.
|
||||
PARAMS should be an alist of (key . value) pairs.
|
||||
The mailto and api_key parameters are added automatically."
|
||||
(let ((result (when user-mail-address
|
||||
`(("mailto" . ,user-mail-address)))))
|
||||
;; Only add api_key if it's set and non-empty
|
||||
(when (and oa-api-key (not (string-empty-p oa-api-key)))
|
||||
(push `("api_key" . ,oa-api-key) result))
|
||||
;; Add other params, filtering out nil values
|
||||
(dolist (param params)
|
||||
(when (cdr param)
|
||||
(push param result)))
|
||||
(nreverse result)))
|
||||
|
||||
|
||||
(defun oa-get (data query &optional iterable)
|
||||
"Get fields from DATA with QUERY.
|
||||
QUERY is a dot notation string.
|
||||
@@ -77,7 +93,7 @@ Assumes data is in plist form."
|
||||
data)
|
||||
|
||||
;; query[] means get query then turn iteration on
|
||||
((and (s-ends-with-p "[]" current-field) (null iterable))
|
||||
((and (string-suffix-p "[]" current-field) (null iterable))
|
||||
(setq current-field (substring current-field 0 -2))
|
||||
(oa-get (plist-get data (intern-soft (concat ":" current-field)))
|
||||
(when fields
|
||||
@@ -86,7 +102,7 @@ Assumes data is in plist form."
|
||||
|
||||
;; this means another level of iteration. You already have a collection. we
|
||||
;; have to iterate over each one I think.
|
||||
((and (s-ends-with-p "[]" current-field) iterable)
|
||||
((and (string-suffix-p "[]" current-field) iterable)
|
||||
(setq current-field (substring current-field 0 -2))
|
||||
(cl-loop for item in data collect
|
||||
(oa-get (plist-get item (intern-soft (concat ":" current-field)))
|
||||
@@ -113,17 +129,16 @@ Assumes data is in plist form."
|
||||
(defun oa--query-all-data (endpoint &rest filter)
|
||||
(let* ((page 1)
|
||||
(url (concat "https://api.openalex.org/" endpoint))
|
||||
(filter-string (string-join (cl-loop for key in (plist-get-keys filter) collect
|
||||
(concat (substring (symbol-name key) 1)
|
||||
":"
|
||||
(url-hexify-string
|
||||
(plist-get filter key))))
|
||||
","))
|
||||
(params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)
|
||||
("filter" . ,filter-string)
|
||||
("page" . ,page)))
|
||||
|
||||
(filter-string (string-join
|
||||
(cl-loop for key in (plist-get-keys filter) collect
|
||||
(concat (substring (symbol-name key) 1)
|
||||
":"
|
||||
(url-hexify-string
|
||||
(plist-get filter key))))
|
||||
","))
|
||||
(params (oa--params `("filter" . ,filter-string)
|
||||
`("page" . ,page)))
|
||||
|
||||
(req (request url :sync t :parser 'oa--response-parser :params params))
|
||||
(data (request-response-data req))
|
||||
(meta (plist-get data :meta))
|
||||
@@ -133,7 +148,7 @@ Assumes data is in plist form."
|
||||
(results (plist-get data :results)))
|
||||
(cl-loop for i from 2 to pages do
|
||||
(setf (cdr (assoc "page" params)) i)
|
||||
(setq req (request purl :sync t :parser 'oa--response-parser :params params)
|
||||
(setq req (request url :sync t :parser 'oa--response-parser :params params)
|
||||
data (request-response-data req)
|
||||
results (append results (plist-get data :results))))
|
||||
results))
|
||||
@@ -171,12 +186,10 @@ non-nil, and `oa-api-key' if it is non-nil to the API url."
|
||||
(url-hexify-string
|
||||
(plist-get filter key))))
|
||||
","))
|
||||
|
||||
|
||||
(req (request url :sync t :parser 'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)
|
||||
("filter" . ,filter-string)
|
||||
("page" . ,page))))
|
||||
:params (oa--params `("filter" . ,filter-string)
|
||||
`("page" . ,page))))
|
||||
(data (request-response-data req))
|
||||
(meta (plist-get data :meta))
|
||||
(count (plist-get meta :count))
|
||||
@@ -184,7 +197,7 @@ non-nil, and `oa-api-key' if it is non-nil to the API url."
|
||||
(pages (ceiling (/ (float count) per-page)))
|
||||
(results (plist-get data :results))
|
||||
(next-page (format "[[elisp:(oa-query \"%s\" %s :page \"%s\")][Next page: %s]]"
|
||||
endpoint
|
||||
endpoint
|
||||
(string-join (cl-loop for x in filter
|
||||
collect
|
||||
(if (keywordp x)
|
||||
@@ -194,7 +207,7 @@ non-nil, and `oa-api-key' if it is non-nil to the API url."
|
||||
(+ page 1)
|
||||
(+ page 1)))
|
||||
(buf (generate-new-buffer "*OpenAlex - Query*")))
|
||||
|
||||
|
||||
(with-current-buffer buf
|
||||
(erase-buffer)
|
||||
(org-mode)
|
||||
@@ -216,7 +229,7 @@ non-nil, and `oa-api-key' if it is non-nil to the API url."
|
||||
((string= endpoint "works")
|
||||
(string-join
|
||||
(cl-loop for wrk in results collect
|
||||
(s-format "*** ${title}
|
||||
(org-ref--format-template "*** ${title}
|
||||
:PROPERTIES:
|
||||
:HOST: ${primary_location.source.display_name}
|
||||
:YEAR: ${publication_year}
|
||||
@@ -237,8 +250,6 @@ ${get-bibtex}
|
||||
${abstract}
|
||||
|
||||
"
|
||||
(lambda (key data)
|
||||
(or (cdr (assoc key data)) ""))
|
||||
`(("title" . ,(oa--title wrk))
|
||||
("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name"))
|
||||
("publication_year" . ,(oa-get wrk "publication_year"))
|
||||
@@ -283,9 +294,7 @@ https://docs.openalex.org/api-entities/works"
|
||||
(t
|
||||
(format "/%s" entity-id)))))
|
||||
(req (request url :sync t :parser 'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)
|
||||
("filter" . ,filter))))
|
||||
:params (oa--params `("filter" . ,filter))))
|
||||
(data (request-response-data req)))
|
||||
;; this is for convenience to inspect data in a browser, e.g. you can click
|
||||
;; on the url in Emacs and it opens in a browser.
|
||||
@@ -301,9 +310,10 @@ https://docs.openalex.org/api-entities/works"
|
||||
(ivy-more-chars)
|
||||
(let* ((url "https://api.openalex.org/autocomplete/works")
|
||||
(req (request url :sync t :parser 'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)
|
||||
("q" . ,query))))
|
||||
:params (let ((params `(("q" . ,query))))
|
||||
(when user-mail-address
|
||||
(push `("mailto" . ,user-mail-address) params))
|
||||
(nreverse params))))
|
||||
(data (request-response-data req))
|
||||
(results (plist-get data :results)))
|
||||
(cl-loop for work in results collect
|
||||
@@ -318,11 +328,11 @@ https://docs.openalex.org/api-entities/works"
|
||||
"Autocomplete works.
|
||||
This doesn't seem as useful as it could be."
|
||||
(interactive)
|
||||
|
||||
|
||||
(ivy-read "Work: " #'oa--works-candidates
|
||||
:dynamic-collection t
|
||||
:action
|
||||
'(1
|
||||
'(1
|
||||
("o" (lambda (candidate)
|
||||
(browse-url (get-text-property 0 'oaid candidate)))
|
||||
"Open in browser"))))
|
||||
@@ -338,7 +348,7 @@ This doesn't seem as useful as it could be."
|
||||
(defun oa--authors (wrk)
|
||||
"Return an author string for WRK.
|
||||
The string is a comma-separated list of links to author pages in OpenAlex."
|
||||
(s-join ", " (cl-loop for author in (plist-get wrk :authorships)
|
||||
(string-join (cl-loop for author in (plist-get wrk :authorships)
|
||||
collect
|
||||
(format "[[elisp:(oa--author-org \"%s\")][%s]]"
|
||||
(plist-get
|
||||
@@ -346,7 +356,8 @@ The string is a comma-separated list of links to author pages in OpenAlex."
|
||||
:id)
|
||||
(plist-get
|
||||
(plist-get author :author)
|
||||
:display_name)))))
|
||||
:display_name)))
|
||||
", "))
|
||||
|
||||
|
||||
(defun oa--title (wrk)
|
||||
@@ -358,7 +369,7 @@ The string is a comma-separated list of links to author pages in OpenAlex."
|
||||
;; be nice to integrate M-, navigation.
|
||||
(defun oa--elisp-get-bibtex (wrk)
|
||||
"Return a elisp link to get a bibtex entry for WRK if there is a doi."
|
||||
(if-let ((doi (plist-get wrk :doi)))
|
||||
(if-let* ((doi (plist-get wrk :doi)))
|
||||
(format "[[elisp:(doi-add-bibtex-entry \"%s\")][Get bibtex entry]]" doi)
|
||||
""))
|
||||
|
||||
@@ -389,7 +400,7 @@ The string is a comma-separated list of links to author pages in OpenAlex."
|
||||
WORKS is a list of results from OpenAlex."
|
||||
(cl-loop for wrk in (plist-get works :results)
|
||||
collect
|
||||
(s-format "** ${title}
|
||||
(org-ref--format-template "** ${title}
|
||||
:PROPERTIES:
|
||||
:HOST: ${primary_location.source.display_name}
|
||||
:YEAR: ${publication_year}
|
||||
@@ -407,8 +418,6 @@ ${get-bibtex}
|
||||
- ${oa-cited}
|
||||
|
||||
"
|
||||
(lambda (key data)
|
||||
(or (cdr (assoc key data)) ""))
|
||||
`(("title" . ,(oa-get wrk "title"))
|
||||
("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name"))
|
||||
("publication_year" . ,(oa-get wrk "publication_year"))
|
||||
@@ -428,7 +437,7 @@ HEADER is the first thing in the buffer
|
||||
WORKS is usually a list of results from OpenAlex.
|
||||
Argument ENTRIES A list of strings for each org entry."
|
||||
(let ((buf (get-buffer-create bufname)))
|
||||
|
||||
|
||||
(with-current-buffer buf
|
||||
(erase-buffer)
|
||||
(insert header)
|
||||
@@ -441,7 +450,7 @@ elisp:org-columns elisp:org-columns-quit
|
||||
| cited by | [[elisp:(oa-buffer-sort-cited-by-count t)][low first]] | [[elisp:(oa-buffer-sort-cited-by-count)][high first]] |
|
||||
|
||||
")
|
||||
(insert (s-join "\n" entries))
|
||||
(insert (string-join entries "\n"))
|
||||
(org-mode)
|
||||
(goto-char (point-min))
|
||||
(org-next-visible-heading 1))
|
||||
@@ -456,21 +465,21 @@ elisp:org-columns elisp:org-columns-quit
|
||||
split
|
||||
entries)
|
||||
(while related-work
|
||||
(setq split (-split-at 25 related-work)
|
||||
(setq split (org-ref--split-at 25 related-work)
|
||||
related-work (nth 1 split))
|
||||
|
||||
|
||||
;; split is what we process now
|
||||
(setq entries (append entries
|
||||
(oa--works-entries
|
||||
(oa--work (format "?filter=openalex:%s" (s-join "|" (nth 0 split))))))))
|
||||
|
||||
(oa--work (format "?filter=openalex:%s" (string-join (nth 0 split) "|")))))))
|
||||
|
||||
(oa--works-buffer
|
||||
"*OpenAlex - Related works*"
|
||||
(format "* OpenAlex - Related works for %s ([[%s][json]])
|
||||
%s\n\n"
|
||||
entity-id
|
||||
(plist-get wrk :oa-url)
|
||||
(s-format ":PROPERTIES:
|
||||
(org-ref--format-template ":PROPERTIES:
|
||||
:TITLE: ${title}
|
||||
:HOST: ${primary_location.source.display_name}
|
||||
:AUTHOR: ${authors}
|
||||
@@ -481,15 +490,13 @@ elisp:org-columns elisp:org-columns-quit
|
||||
|
||||
Found ${nentries} results.
|
||||
"
|
||||
(lambda (key data)
|
||||
(or (cdr (assoc key data)) ""))
|
||||
`(("title" . ,(oa-get wrk "title"))
|
||||
("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name"))
|
||||
("authors" . ,(oa--authors wrk))
|
||||
("doi" . ,(oa-get wrk "doi"))
|
||||
("publication_year" . ,(oa-get wrk "publication_year"))
|
||||
("id" . ,(oa-get wrk "id"))
|
||||
("nentries" . ,(length entries)))))
|
||||
`(("title" . ,(oa-get wrk "title"))
|
||||
("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name"))
|
||||
("authors" . ,(oa--authors wrk))
|
||||
("doi" . ,(oa-get wrk "doi"))
|
||||
("publication_year" . ,(oa-get wrk "publication_year"))
|
||||
("id" . ,(oa-get wrk "id"))
|
||||
("nentries" . ,(length entries)))))
|
||||
entries)))
|
||||
|
||||
|
||||
@@ -500,20 +507,20 @@ Found ${nentries} results.
|
||||
split
|
||||
(entries '()))
|
||||
(while referenced-work
|
||||
(setq split (-split-at 25 referenced-work)
|
||||
(setq split (org-ref--split-at 25 referenced-work)
|
||||
referenced-work (nth 1 split))
|
||||
;; split is what we process now
|
||||
(setq entries (append entries
|
||||
(oa--works-entries
|
||||
(oa--work (format "?filter=openalex:%s"
|
||||
(s-join "|" (nth 0 split))))))))
|
||||
(oa--work (format "?filter=openalex:%s"
|
||||
(string-join (nth 0 split) "|")))))))
|
||||
(oa--works-buffer
|
||||
"*OpenAlex - References*"
|
||||
(format "* OpenAlex - References from %s ([[%s][json]])
|
||||
%s\n\n"
|
||||
entity-id
|
||||
(plist-get wrk :oa-url)
|
||||
(s-format ":PROPERTIES:
|
||||
(org-ref--format-template ":PROPERTIES:
|
||||
:TITLE: ${title}
|
||||
:HOST: ${primary_location.source.display_name}
|
||||
:AUTHOR: ${authors}
|
||||
@@ -524,15 +531,13 @@ Found ${nentries} results.
|
||||
|
||||
Found ${nentries} results.
|
||||
"
|
||||
(lambda (key data)
|
||||
(or (cdr (assoc key data)) ""))
|
||||
`(("title" . ,(oa-get wrk "title"))
|
||||
("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name"))
|
||||
("authors" . ,(oa--authors wrk))
|
||||
("doi" . ,(oa-get wrk "doi"))
|
||||
("publication_year" . ,(oa-get wrk "publication_year"))
|
||||
("id" . ,(oa-get wrk "id"))
|
||||
("nentries" . ,(length entries)))))
|
||||
`(("title" . ,(oa-get wrk "title"))
|
||||
("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name"))
|
||||
("authors" . ,(oa--authors wrk))
|
||||
("doi" . ,(oa-get wrk "doi"))
|
||||
("publication_year" . ,(oa-get wrk "publication_year"))
|
||||
("id" . ,(oa-get wrk "id"))
|
||||
("nentries" . ,(length entries)))))
|
||||
entries)))
|
||||
|
||||
|
||||
@@ -546,8 +551,7 @@ Found ${nentries} results.
|
||||
(request url
|
||||
:sync t
|
||||
:parser 'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)))))
|
||||
:params (oa--params))))
|
||||
(count (plist-get (plist-get cited-by-works :meta) :count))
|
||||
(per-page (plist-get (plist-get cited-by-works :meta) :per_page))
|
||||
(entries '())
|
||||
@@ -559,19 +563,17 @@ Found ${nentries} results.
|
||||
(request url
|
||||
:sync t
|
||||
:parser 'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)
|
||||
("page" . ,page)))))
|
||||
:params (oa--params `("page" . ,page)))))
|
||||
(setq entries (append entries (oa--works-entries cited-by-works)))
|
||||
(cl-incf page))
|
||||
|
||||
|
||||
(oa--works-buffer
|
||||
"*OpenAlex - Cited by*"
|
||||
(format "* OpenAlex - %s Cited by ([[%s][json]])
|
||||
%s"
|
||||
entity-id
|
||||
url
|
||||
(s-format ":PROPERTIES:
|
||||
(org-ref--format-template ":PROPERTIES:
|
||||
:TITLE: ${title}
|
||||
:HOST: ${primary_location.source.display_name}
|
||||
:AUTHOR: ${authors}
|
||||
@@ -583,8 +585,6 @@ Found ${nentries} results.
|
||||
Found ${nentries} results.
|
||||
|
||||
"
|
||||
(lambda (key data)
|
||||
(or (cdr (assoc key data)) ""))
|
||||
`(("title" . ,(oa-get wrk "title"))
|
||||
("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name"))
|
||||
("authors" . ,(oa--authors wrk))
|
||||
@@ -665,12 +665,15 @@ With prefix arg ASCENDING sort from low to high."
|
||||
(browse-url (plist-get data :id))))
|
||||
|
||||
|
||||
(defhydra+ org-ref-citation-hydra ()
|
||||
"Add open from action to `org-ref-citation-hydra'."
|
||||
("xa" oa-open "Open in OpenAlex" :column "OpenAlex")
|
||||
("xr" oa-related-works "Related documents" :column "OpenAlex")
|
||||
("xc" oa-cited-by-works "Cited by documents" :column "OpenAlex")
|
||||
("xf" oa-referenced-works "References from" :column "OpenAlex"))
|
||||
(with-eval-after-load 'org-ref-citation-links
|
||||
(transient-append-suffix 'org-ref-citation-menu "u"
|
||||
'("xa" "Open in OpenAlex" oa-open))
|
||||
(transient-append-suffix 'org-ref-citation-menu "xa"
|
||||
'("xr" "Related documents" oa-related-works))
|
||||
(transient-append-suffix 'org-ref-citation-menu "xr"
|
||||
'("xc" "Cited by documents" oa-cited-by-works))
|
||||
(transient-append-suffix 'org-ref-citation-menu "xc"
|
||||
'("xf" "References from" oa-referenced-works)))
|
||||
|
||||
|
||||
;; * Author object
|
||||
@@ -681,10 +684,8 @@ FILTER is an optional string to add to the URL."
|
||||
(let* ((url (concat "https://api.openalex.org/authors"
|
||||
entity-id))
|
||||
(req (request url :sync t :parser 'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)
|
||||
("filter" . ,filter))))
|
||||
(data (request-response-data req)))
|
||||
:params (oa--params `("filter" . ,filter))))
|
||||
(data (request-response-data req)))
|
||||
;; this is for convenience to inspect data in a browser.
|
||||
(plist-put data :oa-url url)
|
||||
data))
|
||||
@@ -692,15 +693,14 @@ FILTER is an optional string to add to the URL."
|
||||
|
||||
(defun oa--author-entries (works-data url)
|
||||
"Get entries from WORKS-DATA."
|
||||
(let* ((meta (plist-get works-data :meta))
|
||||
(let* ((meta (plist-get works-data :meta))
|
||||
(per-page (plist-get meta :per_page))
|
||||
(count (plist-get meta :count))
|
||||
(pages (/ count per-page))
|
||||
(entries '())
|
||||
purl)
|
||||
(entries '()))
|
||||
;; if there is a remainder we need to get the rest
|
||||
(when (> (mod count per-page) 0) (cl-incf pages))
|
||||
|
||||
|
||||
;; Now we have to loop through the pages
|
||||
(cl-loop for i from 1 to pages
|
||||
do
|
||||
@@ -708,13 +708,11 @@ FILTER is an optional string to add to the URL."
|
||||
(request url
|
||||
:sync t
|
||||
:parser 'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)
|
||||
("page" . ,i))))
|
||||
:params (oa--params `("page" . ,i))))
|
||||
entries (append entries
|
||||
(cl-loop for result in (plist-get works-data :results)
|
||||
collect
|
||||
(s-format "*** ${title}
|
||||
(org-ref--format-template "*** ${title}
|
||||
:PROPERTIES:
|
||||
:ID: ${id}
|
||||
:DOI: ${ids.doi}
|
||||
@@ -734,9 +732,7 @@ ${get-bibtex}
|
||||
|
||||
${abstract}
|
||||
|
||||
" (lambda (key data)
|
||||
(or (cdr (assoc key data)) ""))
|
||||
`(("title" . ,(oa--title result))
|
||||
" `(("title" . ,(oa--title result))
|
||||
("id" . ,(oa-get result "id"))
|
||||
("ids.doi" . ,(oa-get result "ids.doi"))
|
||||
("publication_year" . ,(oa-get result "publication_year"))
|
||||
@@ -787,9 +783,10 @@ ${abstract}
|
||||
(ivy-more-chars)
|
||||
(let* ((url "https://api.openalex.org/autocomplete/authors")
|
||||
(req (request url :sync t :parser 'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)
|
||||
("q" . ,query))))
|
||||
:params (let ((params `(("q" . ,query))))
|
||||
(when user-mail-address
|
||||
(push `("mailto" . ,user-mail-address) params))
|
||||
(nreverse params))))
|
||||
(data (request-response-data req))
|
||||
(results (plist-get data :results)))
|
||||
(cl-loop for author in results collect
|
||||
@@ -801,6 +798,20 @@ ${abstract}
|
||||
'oaid (plist-get author :id))))))
|
||||
|
||||
|
||||
(defun oa--format-institution (data)
|
||||
"Format institution from DATA, handling nil values gracefully.
|
||||
Extracts the first institution from last_known_institutions array."
|
||||
(let* ((institutions (plist-get data :last_known_institutions))
|
||||
(first-inst (and institutions (listp institutions) (car institutions)))
|
||||
(name (and first-inst (plist-get first-inst :display_name)))
|
||||
(country (and first-inst (plist-get first-inst :country_code))))
|
||||
(cond
|
||||
((and name country) (format "%s, %s" name country))
|
||||
(name name)
|
||||
(country country)
|
||||
(t ""))))
|
||||
|
||||
|
||||
(defun oa--counts-by-year (data)
|
||||
"Get citation counts by year and make a graph.
|
||||
DATA is an author from OpenAlex.
|
||||
@@ -858,18 +869,17 @@ plot $counts using 1:3:xtic(2) with boxes lc rgb \"grey\" title \"Citations per
|
||||
(request works-url
|
||||
:sync t
|
||||
:parser 'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key))))))
|
||||
:params (oa--params)))))
|
||||
(with-current-buffer buf
|
||||
(erase-buffer)
|
||||
(insert (s-format "* ${display_name} ([[${oa-url}][json]])
|
||||
(insert (org-ref--format-template "* ${display_name} ([[${oa-url}][json]])
|
||||
:PROPERTIES:
|
||||
:OPENALEX: ${id}
|
||||
:ORCID: ${orcid}
|
||||
:SCOPUS: ${ids.scopus}
|
||||
:WORKS_COUNT: ${works_count}
|
||||
:CITED_BY_COUNT: ${cited_by_count}
|
||||
:INSTITUTION: ${last_known_institution.display_name}, ${last_known_institution.country_code}
|
||||
:INSTITUTION: ${institution}
|
||||
:END:
|
||||
|
||||
#+COLUMNS: %25ITEM %YEAR %CITED_BY_COUNT
|
||||
@@ -884,18 +894,16 @@ ${citations-image}
|
||||
| cited by | [[elisp:(oa-buffer-sort-cited-by-count t)][low first]] | [[elisp:(oa-buffer-sort-cited-by-count)][high first]] |
|
||||
|
||||
"
|
||||
(lambda (key data)
|
||||
(or (cdr (assoc key data)) ""))
|
||||
`(("display_name" . ,(oa-get data "display_name"))
|
||||
("oa-url" . ,(oa-get data "oa-url"))
|
||||
("id" . ,(oa-get data "id"))
|
||||
("orcid" . ,(oa-get data "orcid"))
|
||||
("ids.scopus" . ,(oa-get data "ids.scopus"))
|
||||
("works_count" . ,(oa-get data "works_count"))
|
||||
("last_known_institution.display_name" . ,(oa-get data "last_known_institution.display_name"))
|
||||
("last_known_institution.country_code" . ,(oa-get data "last_known_institution.country_code"))
|
||||
("cited_by_count" . ,(oa-get data "cited_by_count"))
|
||||
("institution" . ,(oa--format-institution data))
|
||||
("citations-image" . ,citations-image))))
|
||||
(insert (s-join "\n" (oa--author-entries works-data works-url)))
|
||||
(insert (string-join (oa--author-entries works-data works-url) "\n"))
|
||||
|
||||
(org-mode)
|
||||
(goto-char (point-min))
|
||||
@@ -906,7 +914,7 @@ ${citations-image}
|
||||
(defun oa-author ()
|
||||
"Get data and act on it for an author."
|
||||
(interactive)
|
||||
|
||||
|
||||
(ivy-read "Author: " #'oa--author-candidates
|
||||
:dynamic-collection t
|
||||
:action
|
||||
@@ -936,10 +944,8 @@ PAGE is optional, and loads that page of results. Defaults to 1."
|
||||
(req (request url
|
||||
:sync t
|
||||
:parser #'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)
|
||||
("page" . ,page)
|
||||
("filter" . ,(format "fulltext.search:%s" query)))))
|
||||
:params (oa--params `("page" . ,page)
|
||||
`("filter" . ,(format "fulltext.search:%s" query)))))
|
||||
(data (request-response-data req))
|
||||
(metadata (plist-get data :meta))
|
||||
(count (plist-get metadata :count))
|
||||
@@ -952,29 +958,27 @@ PAGE is optional, and loads that page of results. Defaults to 1."
|
||||
(+ page 1)
|
||||
npages))
|
||||
(buf (get-buffer-create "*OpenAlex Full-text search*")))
|
||||
|
||||
|
||||
(with-current-buffer buf
|
||||
(erase-buffer)
|
||||
(org-mode)
|
||||
(insert (s-format "#+title: Full-text search: ${query}
|
||||
(insert (org-ref--format-template "#+title: Full-text search: ${query}
|
||||
|
||||
[[elisp:(oa-fulltext-search \"${query}\" ${page})]]"
|
||||
'aget
|
||||
`(("query" . ,query)
|
||||
("page" . ,page))))
|
||||
(insert (s-format
|
||||
(insert (org-ref--format-template
|
||||
"
|
||||
${meta.count} results: Page ${meta.page} of ${s1} ${s2}
|
||||
\n\n"
|
||||
(lambda (key data)
|
||||
(or (cdr (assoc key data)) ""))
|
||||
`(("meta.page" . ,(oa-get data "meta.page"))
|
||||
`(("meta.count" . ,count)
|
||||
("meta.page" . ,(oa-get data "meta.page"))
|
||||
("s1" . ,(format "%s" npages))
|
||||
("s2" . ,(format "%s" next-page)))))
|
||||
|
||||
|
||||
(insert
|
||||
(cl-loop for result in results concat
|
||||
(s-format "* ${title}
|
||||
(cl-loop for result in results concat
|
||||
(org-ref--format-template "* ${title}
|
||||
:PROPERTIES:
|
||||
:JOURNAL: ${primary_location.source.display_name}
|
||||
:AUTHOR: ${authors}
|
||||
@@ -989,10 +993,7 @@ ${get-bibtex}
|
||||
- ${oa-related}
|
||||
- ${oa-cited}
|
||||
|
||||
" (lambda (key data)
|
||||
(or (cdr (assoc key data)) ""))
|
||||
|
||||
`(("title" . ,(oa--title result))
|
||||
" `(("title" . ,(oa--title result))
|
||||
("primary_location.source.display_name" . ,(oa-get result "primary_location.source.display_name"))
|
||||
("authors" . ,(oa--authors result))
|
||||
("publication_year" . ,(oa-get result "publication_year"))
|
||||
@@ -1004,7 +1005,7 @@ ${get-bibtex}
|
||||
("oa-cited" . ,(oa--elisp-get-oa-cited-by result))))))
|
||||
|
||||
(insert next-page)
|
||||
|
||||
|
||||
(goto-char (point-min)))
|
||||
(pop-to-buffer buf)))
|
||||
|
||||
@@ -1050,23 +1051,21 @@ Recently published papers are probably missing.
|
||||
:dynamic-collection t))
|
||||
(when (y-or-n-p "Save to file?")
|
||||
(read-file-name "File: "))))
|
||||
|
||||
|
||||
(let* ((data (oa--author entity-id))
|
||||
(works-url (plist-get data :works_api_url))
|
||||
(works-url (plist-get data :works_api_url))
|
||||
(works-data (request-response-data
|
||||
(request works-url
|
||||
:sync t
|
||||
:parser 'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)))))
|
||||
:params (oa--params))))
|
||||
(meta (plist-get works-data :meta))
|
||||
(count (plist-get meta :count))
|
||||
(per-page (plist-get meta :per_page))
|
||||
(pages (/ count per-page))
|
||||
(results (plist-get works-data :results))
|
||||
(current-year (string-to-number (format-time-string "%Y" (current-time))))
|
||||
(current-authors '())
|
||||
purl)
|
||||
(current-authors '()))
|
||||
|
||||
;; Now we need to accumulate the rest of the results from other pages
|
||||
(when (> (mod count per-page) 0) (cl-incf pages))
|
||||
@@ -1077,11 +1076,9 @@ Recently published papers are probably missing.
|
||||
(request works-url
|
||||
:sync t
|
||||
:parser 'oa--response-parser
|
||||
:params `(("mailto" . ,user-mail-address)
|
||||
("api_key" . ,oa-api-key)
|
||||
("page" . ,i))))
|
||||
:params (oa--params `("page" . ,i))))
|
||||
results (append results (plist-get works-data :results))))
|
||||
|
||||
|
||||
;; Now results is a list of your publications. We need to iterate over each
|
||||
;; one, and accumulate author information
|
||||
(cl-loop for result in results do
|
||||
@@ -1093,30 +1090,30 @@ Recently published papers are probably missing.
|
||||
(name-parts (mapcar #'capitalize (split-string name)))
|
||||
(name-string (concat (car (last name-parts)) ", "
|
||||
(string-join (butlast name-parts) " ")))
|
||||
|
||||
(institutions (plist-get authorship :institutions))
|
||||
|
||||
(institutions (plist-get authorship :institutions))
|
||||
(institution (plist-get (car institutions) :display_name)))
|
||||
;; name, institution, contact info, last-active
|
||||
;; we won't have contact info from openalex.
|
||||
(push (list name-string institution "" year
|
||||
(plist-get result :publication_date))
|
||||
current-authors))))))
|
||||
|
||||
|
||||
(setq current-authors (sort current-authors
|
||||
(lambda (a b)
|
||||
"Sort first on name, then on year in descending order"
|
||||
(if (string= (nth 0 a) (nth 0 b))
|
||||
(> (nth 3 a) (nth 3 b))
|
||||
(string< (car a) (car b))))))
|
||||
|
||||
|
||||
;; now filter for unique authors
|
||||
(setq current-authors (cl-loop for group in (seq-group-by (lambda (x)
|
||||
(car x))
|
||||
current-authors)
|
||||
current-authors)
|
||||
collect (cadr group)))
|
||||
|
||||
;; Finally lets fix the year so Excel reads it correctly. I use the publication date
|
||||
(setq current-authors (cl-loop for row in current-authors
|
||||
(setq current-authors (cl-loop for row in current-authors
|
||||
collect
|
||||
(list "A:"
|
||||
(nth 0 row)
|
||||
@@ -1132,7 +1129,7 @@ Recently published papers are probably missing.
|
||||
row)
|
||||
"\t")
|
||||
"\n")))
|
||||
|
||||
|
||||
|
||||
(kill-new (mapconcat (lambda (row)
|
||||
(concat (string-join (mapcar (lambda (x)
|
||||
@@ -1152,7 +1149,7 @@ Recently published papers are probably missing.
|
||||
(interactive)
|
||||
(cl-loop for buf in (buffer-list)
|
||||
do
|
||||
(when (s-starts-with? "*OpenAlex" (buffer-name buf))
|
||||
(when (string-prefix-p "*OpenAlex" (buffer-name buf))
|
||||
(kill-buffer buf))))
|
||||
|
||||
|
||||
@@ -1165,7 +1162,7 @@ Operates on headings with a DOI property."
|
||||
(lambda ()
|
||||
(kill-new (org-entry-get (point) "DOI"))
|
||||
(doi-utils-add-bibtex-entry-from-doi
|
||||
(doi-utils-maybe-doi-from-region-or-current-kill)
|
||||
(doi-utils-maybe-doi-from-region-or-current-kill)
|
||||
bibfile))
|
||||
"DOI<>\"\"")))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user