update packages

This commit is contained in:
2025-11-25 19:52:03 +01:00
parent 14ba373378
commit dbbae92267
280 changed files with 13451 additions and 11207 deletions

View File

@@ -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<>\"\"")))