+# -*- org-edit-src-content-indentation: 0; -*-
#+TITLE: DOI utilities for making bibtex entries and downloading pdfs
This package provides functionality to download PDFs and bibtex entries from a DOI, as well as to update a bibtex entry from a DOI. It depends slightly on org-ref, to determine where to save pdf files too, and where to insert bibtex entries in the default bibliography.
As different bibtex types share common keys, it is advantageous to separate data extraction from json, and the formatting of the bibtex entry.
-#+BEGIN_SRC emacs-lisp :notangle doi-utils.el
-(defmacro defpar (name &optional value)
- `(progn (defvar ,name)
- (setf ,name ,value)))
-
-(defpar doi-utils-json-metadata-extract
- '((type (plist-get results :type))
- (author (mapconcat (lambda (x) (concat (plist-get x :given) " " (plist-get x :family)))
- (plist-get results :author) " and "))
- (title (plist-get results :title))
- (subtitle (plist-get results :subtitle))
- (journal (plist-get results :container-title))
- (series (plist-get results :container-title))
- (publisher (plist-get results :publisher))
- (volume (plist-get results :volume))
- (issue (plist-get results :issue))
- (number (plist-get results :issue))
- (year (elt (elt (plist-get (plist-get results :issued) :date-parts) 0) 0))
- (month (elt (elt (plist-get (plist-get results :issued) :date-parts) 0) 1))
- (pages (plist-get results :page))
- (doi (plist-get results :DOI))
- (url (plist-get results :URL))
- (booktitle (plist-get results :container-title))))
+#+BEGIN_SRC emacs-lisp :tangle doi-utils.el
+(setq doi-utils-json-metadata-extract
+ '((type (plist-get results :type))
+ (author (mapconcat (lambda (x) (concat (plist-get x :given) " " (plist-get x :family)))
+ (plist-get results :author) " and "))
+ (title (plist-get results :title))
+ (subtitle (plist-get results :subtitle))
+ (journal (plist-get results :container-title))
+ (series (plist-get results :container-title))
+ (publisher (plist-get results :publisher))
+ (volume (plist-get results :volume))
+ (issue (plist-get results :issue))
+ (number (plist-get results :issue))
+ (year (elt (elt (plist-get (plist-get results :issued) :date-parts) 0) 0))
+ (month (elt (elt (plist-get (plist-get results :issued) :date-parts) 0) 1))
+ (pages (plist-get results :page))
+ (doi (plist-get results :DOI))
+ (url (plist-get results :URL))
+ (booktitle (plist-get results :container-title))))
#+END_SRC
Next, we need to define the different bibtex types. Each type has a bibtex type (for output) and the type as provided in the doi record. Finally, we have to declare the fields we want to output.
#+BEGIN_SRC emacs-lisp :tangle doi-utils.el :results none
-(defpar doi-utils-bibtex-type-generators nil)
+(setq doi-utils-bibtex-type-generators nil)
-(defun concat-prepare (lst &optional acc)
+(defun doi-utils-concat-prepare (lst &optional acc)
"Given a list `lst' of strings and other expressions, which are
intented to passed to `concat', concat any subsequent strings,
minimising the number of arguments being passed to `concat'
(cond ((null lst) (nreverse acc))
((and (stringp (car lst))
(stringp (car acc)))
- (concat-prepare (cdr lst) (cons (concat (car acc) (car lst))
+ (doi-utils-concat-prepare (cdr lst) (cons (concat (car acc) (car lst))
(cdr acc))))
- (t (concat-prepare (cdr lst) (cons (car lst) acc)))))
-
+ (t (doi-utils-concat-prepare (cdr lst) (cons (car lst) acc)))))
(defmacro doi-utils-def-bibtex-type (name matching-types &rest fields)
"Define a BibTeX type identified by (symbol) `name' with
(error "unknown bibtex field type %s" field))))
fields)
(concat
- ,@(concat-prepare
+ ,@(doi-utils-concat-prepare
(-flatten
(list (concat "@" (symbol-name name) "{,\n")
;; there seems to be some bug with mapcan,
(doi-utils-def-bibtex-type book ("book")
author title series publisher year pages doi url)
+
+(doi-utils-def-bibtex-type inbook ("book-chapter")
+ author title booktitle series publisher year pages doi url)
+
#+END_SRC
With the code generating the bibtex entry in place, we can glue it to the json retrieval code.
-#+BEGIN_SRC emacs-lisp :notangle doi-utils.el
+#+BEGIN_SRC emacs-lisp :tangle doi-utils.el
(defun doi-utils-doi-to-bibtex-string (doi)
- "return a bibtex entry as a string for the doi. Only articles are currently supported"
+ "return a bibtex entry as a string for the doi. Not all types are supported yet."
(let* ((results (doi-utils-get-json-metadata doi))
(type (plist-get results :type)))
- (format "%s" results) ; json-data
+ ;(format "%s" results) ; json-data
(or (some (lambda (g) (funcall g type results)) doi-utils-bibtex-type-generators)
- (message-box "%s not supported yet." type))))
+ (message "%s not supported yet\n%S." type results))))
#+END_SRC
#+RESULTS:
(interactive "sDOI :")
(insert (doi-utils-doi-to-bibtex-string doi))
(backward-char)
+ ;; set date added for the record
+ (bibtex-set-field "DATE_ADDED" (current-time-string))
(if (bibtex-key-in-head nil)
(org-ref-clean-bibtex-entry t)
(org-ref-clean-bibtex-entry))
(doi-utils-get-bibtex-entry-pdf)
(save-selected-window
(org-ref-open-bibtex-notes)))
+
#+END_SRC
It may be you are in some other place when you want to add a bibtex entry. This next function will open the first entry in org-ref-default-bibliography go to the end, and add the entry. You can sort it later.
(insert value))))
#+END_SRC
-The updating function looks like this. We get all the keys from the json plist metadata, and update the fields if they exist.
+The updating function for a whole entry looks like this. We get all the keys from the json plist metadata, and update the fields if they exist.
#+BEGIN_SRC emacs-lisp :tangle doi-utils.el
(defun plist-get-keys (plist)
(org-ref-clean-bibtex-entry t)
(org-ref-clean-bibtex-entry)))
#+END_SRC
+
+A downside to updating an entry is it overwrites what you have already fixed. So, we next develop a function to update the field at point.
+
+#+BEGIN_SRC emacs-lisp
+(defun doi-utils-update-field ()
+ (interactive)
+ (let* ((doi (bibtex-autokey-get-field "doi"))
+ (results (doi-utils-get-json-metadata doi))
+ (field (car (bibtex-find-text-internal nil nil ","))))
+ (cond
+ ((string= field "volume")
+ (bibtex-set-field field (plist-get results :volume)))
+ ((string= field "number")
+ (bibtex-set-field field (plist-get results :issue)))
+ ((string= field "pages")
+ (bibtex-set-field field (plist-get results :page)))
+ ((string= field "year")
+ (bibtex-set-field field (plist-get results :year)))
+ (t
+ (message "%s not supported yet." field)))))
+#+END_SRC
+
+
* DOI functions for WOS
I came across this API http://wokinfo.com/media/pdf/OpenURL-guide.pdf to make links to the things I am interested in here. Based on that document, here are three links based on a doi:10.1021/jp047349j that take you to different Web Of Science (WOS) pages.
+* Debugging a DOI
+I wrote this function to help debug a DOI. This function generates an org-buffer with the doi, gets the json metadata, shows the bibtex entry, and the pdf link for it.
+
+#+BEGIN_SRC emacs-lisp :tangle doi-utils.el
+(defun doi-utils-debug (doi)
+ "Generate an org-buffer showing data about DOI."
+ (interactive "sDOI: ")
+ (switch-to-buffer "*debug-doi*")
+ (erase-buffer)
+ (org-mode)
+ (insert (concat "doi:" doi) "\n\n")
+ (insert "* JSON
+" (format "%s" (doi-utils-get-json-metadata doi)) "
+
+* Bibtex
+
+" (doi-utils-doi-to-bibtex-string doi) "
+
+* PDF
+" (doi-utils-get-pdf-url doi)))
+#+END_SRC
+
+#+RESULTS:
+: doi-utils-debug
+
* Adding a bibtex entry from a crossref query
The idea here is to perform a query on Crossref, get a helm buffer of candidates, and select the entry(ies) you want to add to your bibtex file. You can select a region, e.g. a free form citation, or set of words, or you can type the query in by hand.