add hydra menu
[org-ref.git] / doi-utils.org
index 3fe93f0..68c74bc 100644 (file)
@@ -570,7 +570,7 @@ prompt. Otherwise, you have to type or pste in a DOI."
                              (s-match "^10" (buffer-substring
                                              (region-beginning)
                                              (region-end))))
-                      (buffer-susbstring (region-beginning) (region-end)))
+                      (buffer-substring (region-beginning) (region-end)))
                      ;; if the first entry in the kill-ring looks
                      ;; like a DOI, let's use it.
                      ((if (s-match "^10" (car kill-ring))
@@ -834,7 +834,9 @@ Here is our example bibtex entry.
   number =      6,
   pages =       1996,
   year =        2014,
-  doi =                 {10.1039/c3ee43874k},
+  doi =                 {10.1039/c3ee43874k,
+  url =                 {http://dx.doi.org/10.1039/c3ee43874k}},
+
 }
 
 
@@ -845,47 +847,55 @@ The idea is to query Crossref in a way that is likely to give us a hit relevant
 According to http://search.crossref.org/help/api we can send a query with a free form citation that may give us something back. We do this to get a list of candidates, and run a helm command to get the doi.
 
 
-#+BEGIN_SRC emacs-lisp
+#+BEGIN_SRC emacs-lisp :tangle doi-utils.el
 (defun doi-utils-crossref-citation-query ()
+  "Query Crossref with the title of the bibtex entry at point to
+get a list of possible matches. This opens a helm buffer to
+select an entry. The default action inserts a doi and url field
+in the bibtex entry at point. The second action opens the doi
+url. If there is already a doi field, the function raises an
+error."
   (interactive)
   (bibtex-beginning-of-entry)
   (let* ((entry (bibtex-parse-entry))
-        (title  (reftex-get-bib-field "title" entry))
         (json-string)
         (json-data)
         (doi))
     (unless (string= ""(reftex-get-bib-field "doi" entry))
       (error "Entry already has a doi field"))
-    ;; clean up title for the crossref query
-    (setq title (replace-regexp-in-string "\n\\|\t\\|\s+" " " title))
 
     (with-current-buffer
        (url-retrieve-synchronously
         (concat
          "http://search.crossref.org/dois?q="
-         (url-hexify-string title)))
+         (url-hexify-string (org-ref-bib-citation))))
       (setq json-string (buffer-substring url-http-end-of-headers (point-max)))
       (setq json-data (json-read-from-string json-string)))
 
-    (let* ((name (format "Crossref hits for %s" title))
+    (let* ((name (format "Crossref hits for %s" (org-ref-bib-citation)))
           (helm-candidates (mapcar (lambda (x)
                                      (cons
-                                      (cdr (assoc 'fullCitation x))
+                                      (concat
+                                       (cdr (assoc 'fullCitation x))
+                                       " "
+                                       (cdr (assoc 'doi x)))
                                       (cdr (assoc 'doi x))))
                                      json-data))
           (source `((name . ,name)
                     (candidates . ,helm-candidates)
                     ;; just return the candidate
-                    (action . (lambda (candidate)
-                                candidate )))))
-      (setq doi
-           (helm :sources '(source)))
-
-      (when doi
-       (bibtex-make-field "doi")
-       (backward-char)
-        ;; crossref returns doi url, but I prefer only a doi for the doi field
-       (insert (replace-regexp-in-string "^http://dx.doi.org/" "" doi))))))
+                    (action . (("Insert doi and url field" . (lambda (doi)
+                                                               (bibtex-make-field "doi")
+                                                               (backward-char)
+                                                               ;; crossref returns doi url, but I prefer only a doi for the doi field
+                                                               (insert (replace-regexp-in-string "^http://dx.doi.org/" "" doi))
+                                                               (when (string= ""(reftex-get-bib-field "url" entry))
+                                                                 (bibtex-make-field "url")
+                                                                 (backward-char)
+                                                                 (insert doi))))
+                               ("Open url" . (lambda (doi)
+                                               (browse-url doi))))))))
+      (helm :sources '(source)))))
 #+END_SRC
 
 #+RESULTS:
@@ -893,7 +903,61 @@ According to http://search.crossref.org/help/api we can send a query with a free
 
 
 
+* 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.
+
+#+BEGIN_SRC emacs-lisp :tangle doi-utils.el
+(defun doi-utils-add-entry-from-crossref-query (query bibtex-file)
+  (interactive (list
+               (read-input
+                "Query: "
+                ;; now set initial input
+                (cond
+                 ;; If region is active assume we want it
+                 ((region-active-p)
+                  (replace-regexp-in-string
+                   "\n" " "
+                   (buffer-substring (region-beginning) (region-end))))
+                 ;; type or paste it in
+                 (t
+                  nil)))
+               (ido-completing-read
+                "Bibfile: "
+                (append (f-entries "." (lambda (f) (f-ext? f "bib")))
+                        org-ref-default-bibliography))))
+  (let* ((json-string)
+        (json-data)
+        (doi))
+
+    (with-current-buffer
+       (url-retrieve-synchronously
+        (concat
+         "http://search.crossref.org/dois?q="
+         (url-hexify-string query)))
+      (setq json-string (buffer-substring url-http-end-of-headers (point-max)))
+      (setq json-data (json-read-from-string json-string)))
 
+    (let* ((name (format "Crossref hits for %s"
+                        ;; remove carriage returns. they cause problems in helm.
+                        (replace-regexp-in-string "\n" " " query)))
+          (helm-candidates (mapcar (lambda (x)
+                                     (cons
+                                      (concat
+                                       (cdr (assoc 'fullCitation x))
+                                       " "
+                                       (cdr (assoc 'doi x)))
+                                      (cdr (assoc 'doi x))))
+                                     json-data))
+          (source `((name . ,name)
+                    (candidates . ,helm-candidates)
+                    ;; just return the candidate
+                    (action . (("Insert bibtex entry" . (lambda (doi)
+                                                          (doi-utils-add-bibtex-entry-from-doi
+                                                           (replace-regexp-in-string "^http://dx.doi.org/" "" doi) ,bibtex-file)))
+                               ("Open url" . (lambda (doi)
+                                               (browse-url doi))))))))
+      (helm :sources '(source)))))
+#+END_SRC
 
 ** json
 
@@ -1103,7 +1167,8 @@ Here is a list of helm candidates
 #+END_SRC
 
 #+RESULTS:
-#+BEGIN_SRC emacs-lisp
+#+BEGIN_SRC emacs-lisp :tangle no
+
 (((fullCitation . "Ann M. Deml, Vladan Stevanovi\304\207, Christopher L. Muhich, Charles B. Musgrave, Ryan O'Hayre, 2014, 'Oxide enthalpy of formation and band gap energy as accurate descriptors of oxygen vacancy formation energetics', <i>Energy &amp; Environmental Science</i>, vol. 7, no. 6, p. 1996")
   (year . "2014")
   (coins . "ctx_ver=Z39.88-2004&amp;rft_id=info%3Adoi%2Fhttp%3A%2F%2Fdx.doi.org%2F10.1039%2Fc3ee43874k&amp;rfr_id=info%3Asid%2Fcrossref.org%3Asearch&amp;rft.atitle=Oxide+enthalpy+of+formation+and+band+gap+energy+as+accurate+descriptors+of+oxygen+vacancy+formation+energetics&amp;rft.jtitle=Energy+%26+Environmental+Science&amp;rft.date=2014&amp;rft.volume=7&amp;rft.issue=6&amp;rft.spage=1996&amp;rft.aufirst=Ann+M.&amp;rft.aulast=Deml&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=article&amp;rft.au=Ann+M.+Deml&amp;rft.au=+Vladan+Stevanovi%C4%87&amp;rft.au=+Christopher+L.+Muhich&amp;rft.au=+Charles+B.+Musgrave&amp;rft.au=+Ryan+O%27Hayre")
@@ -1187,36 +1252,114 @@ Here is a list of helm candidates
  ...)
 #+END_SRC
 
-* end of file
-#+BEGIN_SRC emacs-lisp :tangle doi-utils.el
-(provide 'doi-utils)
-#+END_SRC
-* load
-#+BEGIN_SRC emacs-lisp :tangle no
-(org-babel-load-file "doi-utils.org")
-#+END_SRC
 
-#+RESULTS:
-: Loaded doi-utils.el
+* ISBN utility
+These are not really doi utilities, but for now I am putting them here.
 
+I found this on the web. It can be handy, but the bibtex entry has a lot of stuff in it.
 
+#+BEGIN_SRC emacs-lisp :tangle doi-utils.el
+(defun isbn-to-bibtex-lead (isbn)
+ "Search lead.to for ISBN bibtex entry. You have to copy the entry if it is on the page to your bibtex file."
+ (interactive "sISBN: ")
+(browse-url
+(format "http://lead.to/amazon/en/?key=%s+&si=all&op=bt&bn=&so=sa&ht=us" isbn)))
+#+END_SRC
 
+Here we get isbn metadata and build a bibtex entry.
+http://xisbn.worldcat.org/xisbnadmin/doc/api.htm#getmetadata
 
 
-#+BEGIN_SRC emacs-lisp
-(setq data '(("John" . "john@email.com")
-             ("Jim" . "jim@email.com")
-             ("Jane" . "jane@email.com")
-             ("Jill" . "jill@email.com")))
+#+BEGIN_SRC emacs-lisp :tangle doi-utils.el
+(defun isbn-to-bibtex (isbn bibfile)
+  "Get bibtex entry for ISBN and insert it into BIBFILE unless an
+entry with the generated key already exists in the file."
+  (interactive
+   (list
+    (read-input
+     "ISBN: "
+     ;; now set initial input
+     (cond
+      ;; If region is active and it starts with a number, we use it
+      ((and  (region-active-p)
+            (s-match "^[0-9]" (buffer-substring (region-beginning) (region-end))))
+       (buffer-substring (region-beginning) (region-end)))
+      ;; if first entry in kill ring starts with a number assume it is an isbn
+      ;; and use it as the guess
+      ((if (s-match "^[0-9]" (car kill-ring))
+          (car kill-ring)))
+      ;; type or paste it in
+      (t
+       nil)))
+    (ido-completing-read
+     "Bibfile: "
+     (append (f-entries "." (lambda (f) (f-ext? f "bib")))
+            org-ref-default-bibliography))))
+
+  (let* ((results (with-current-buffer
+                     (url-retrieve-synchronously
+                      (format
+                       "http://xisbn.worldcat.org/webservices/xid/isbn/%s?method=getMetadata&format=json&fl=*"
+                       isbn))
+                   (json-read-from-string
+                    (buffer-substring url-http-end-of-headers (point-max)))))
+        (status (cdr (nth 1 results)))
+        (metadata (aref (cdar results) 0))
+        (new-entry)
+        (new-key))
+
+    ;; check if we got something
+    (unless (string= "ok" status)
+      (error "Status is %s" status))
+
+    ;; construct an alphabetically sorted bibtex entry. I assume ISBN numbers go
+    ;; with book entries.
+    (setq new-entry
+         (concat "\n@book{,\n"
+                 (mapconcat
+                  'identity
+                  (loop for field in (-sort 'string-lessp (mapcar 'car metadata))
+                        collect
+                        (format "  %s={%s}," field (cdr (assoc field metadata))))
+                  "\n")
+                 "\n}\n"))
+
+    ;; build entry in temp buffer to get the key so we can check for duplicates
+    (setq new-entry (with-temp-buffer
+                     (insert new-entry)
+                     (org-ref-clean-bibtex-entry)
+                     (setq new-key (bibtex-key-in-head))
+                     (buffer-string)))
+    (find-file bibfile)
+    (goto-char (point-min))
+    (when (search-forward new-key nil t)
+      (beep)
+      (setq new-key (read-input
+                    (format  "%s already exists. Enter new key (C-g to cancel): " new-key)
+                    new-key)))
+    (goto-char (point-max))
+    (insert new-entry)
+    ;; set key. It is simplest to just replace it, even if it is the same.
+    (bibtex-beginning-of-entry)
+    (re-search-forward bibtex-entry-maybe-empty-head)
+    (if (match-beginning bibtex-key-in-head)
+       (delete-region (match-beginning bibtex-key-in-head)
+                      (match-end bibtex-key-in-head)))
+    (insert new-key)
+    (bibtex-fill-entry)
+    (save-buffer)))
+#+END_SRC
 
-(setq some-helm-source
-      `((name . "HELM at the Emacs")
-        (candidates . ,(mapcar 'car data))
-        (action . (lambda (candidate)
-                    (message-box "%s" (cdr (assoc candidate data)))))))
 
-(message-box "you chose %s" (helm :sources '(some-helm-source)))
+
+* end of file
+#+BEGIN_SRC emacs-lisp :tangle doi-utils.el
+(provide 'doi-utils)
+#+END_SRC
+* load
+#+BEGIN_SRC emacs-lisp :tangle no
+(org-babel-load-file "doi-utils.org")
 #+END_SRC
 
 #+RESULTS:
-: you chose jim@email.com
+: Loaded doi-utils.el