Automated bibtex entry tweeting

| categories: twitter, bibtex | tags: | View Comments

The goal in this post is to develop an elisp function that will tweet a bibtex entry. What I want is to be on a bibtex entry, and run a command that will generate a tweet and tweet it. Here is an example bibtex entry I will use in this post. Clearly, I couldn't simply tweet the entry, it is too long. What I want instead is to generate a picture of a formatted citation, to make a gist out of the bibtex entry so we can link to it, and then to provide links in the tweet to the doi, and the bibtex entry gist.

(with-current-buffer (find-file-noselect "~/Dropbox/bibliography/references.bib")
  (goto-char (point-min))
  (re-search-forward "kitchin-2016-autom-data,")
  (bibtex-copy-entry-as-kill)
  (with-temp-buffer
    (bibtex-yank 1)
    (buffer-string)))

First, we tackle making an image. Emacs has some capability to generate svg, and we can readily convert that to png for the tweet. Here we just go to the entry, and then generate a png. I build off the citation capability of org-ref to generate a pretty reasonably formatted entry. It isn't perfect; the volume is missing in the entry, so there is a blank space between two commas, but this is good enough for me. Note we need a png for twitter. It appears you cannot upload svg yet.

(let* ((entry (with-current-buffer (find-file-noselect "~/Dropbox/bibliography/references.bib")
                (goto-char (point-min))
                (re-search-forward "kitchin-2016-autom-data,")
                (bibtex-beginning-of-entry)
                (bibtex-parse-entry t)))
       (formatted-entry (orhc-formatted-citation entry))
       (lines (with-temp-buffer
                (insert formatted-entry)
                (fill-paragraph)
                (split-string  (buffer-string) "\n")))
       (svg (svg-create 500 (* 20 (length lines)))))

  (loop for i from 0
        for line in lines
        do
        (svg-text svg line
                  :font-size "12"
                  :stroke "black"
                  :x 0
                  :y (+ 15 (* i 15))
                  :stroke-width 0.3))

  (with-temp-file "authoring.svg"
    (svg-print svg)))

(shell-command "convert authoring.svg authoring.png")

It is easy enough to get the doi, and generate the url to it.

(let* ((entry (with-current-buffer (find-file-noselect "~/Dropbox/bibliography/references.bib")
                (goto-char (point-min))
                (re-search-forward "kitchin-2016-autom-data,")
                (bibtex-beginning-of-entry)
                (bibtex-parse-entry t))))
  (format "http://dx.doi.org/%s" (cdr (assoc "doi" entry ))))

Next, we will put the entry as a gist on Github, so we can provide a link to it. I use the gist.el package, and here just do some trickery to put the entry in a temp-file named by the key so that the gist has a nice name. This returns the url to the gist, which we would want to incorporate into a tweet.

(with-current-buffer (find-file-noselect "~/Dropbox/bibliography/references.bib")
  (goto-char (point-min))
  (re-search-forward "kitchin-2016-autom-data,")
  (save-restriction
    (bibtex-narrow-to-entry)
    (bibtex-beginning-of-entry)
    (let* ((entry-string (buffer-string))
           (entry (bibtex-parse-entry))
           (key (cdr (assoc "=key=" entry)))
           (tfile (expand-file-name (format "%s.bib" key) temporary-file-directory)))
      (with-temp-file tfile
        (insert entry-string))
      (with-current-buffer (find-file-noselect tfile) 
        (gist-buffer)))
    (with-temp-buffer
      (yank)
      (buffer-string))))

Ok, All the pieces are in place. The only piece left is creating the tweet, and tweeting it. I couldn't see an obvious way to do this with twittering mode, since I didn't see where to add an image. There is a Python library for this though, and it looks pretty easy to use. Here is an example usage.

from TwitterAPI import TwitterAPI
from twitter_secrets import CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN_KEY, ACCESS_TOKEN_SECRET

api = TwitterAPI(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN_KEY, ACCESS_TOKEN_SECRET)
file = open('authoring.png', 'rb')
data = file.read()
r = api.request('statuses/update_with_media', {'status':'A test tweet using the TwitterAPI with an image.'}, {'media[]':data})
print(r.status_code)

200

It will be a tad hacky, but the script is so simple we can just make a template, and run it. We need to do these things: 1) make the image, 2) make the gist 3) format and send the tweet. Here is the elisp function to do that.

(defun tweet-bibtex ()
  "Tweet the bibtex entry at point."
  (interactive)
  (bibtex-beginning-of-entry)
  (bibtex-set-field "tweeted" (current-time-string))

  (let* ((entry-string (save-restriction
                         (bibtex-beginning-of-entry)
                         (bibtex-narrow-to-entry)
                         (buffer-substring-no-properties (point-min) (point-max))))
         (entry (bibtex-parse-entry t))
         (key (cdr (assoc "=key=" entry)))
         (doi (cdr (assoc "doi" entry)))
         (svg-file (expand-file-name (format "%s.svg" key) temporary-file-directory))
         (png-file (expand-file-name (format "%s.png" key) temporary-file-directory))
         (bib-file (expand-file-name (format "%s.bib" key) temporary-file-directory))
         (py-file (expand-file-name (format "%s.py" key) temporary-file-directory))
         (formatted-entry (orhc-formatted-citation entry))
         (lines (with-temp-buffer
                  (insert formatted-entry)
                  (fill-paragraph)
                  (split-string  (buffer-string) "\n")))
         (svg (svg-create 500 (* 20 (length lines))))
         (tweet (read-string "Tweet: "))
         gist-url
         full-tweet) 

    ;; delete buffers and files
    (loop for buf in (list (concat key ".bib")
                           (concat key ".png")
                           (concat key ".svg")
                           (concat key ".py"))
          do 
          (when (get-buffer buf) 
            (kill-buffer (get-buffer buf))))

    ;; Step 1 make the image
    (loop for i from 0
          for line in lines
          do
          (svg-text svg line
                    :font-size "12"
                    :stroke "black"
                    :x 0
                    :y (+ 15 (* i 15))
                    :stroke-width 0.3))

    (with-temp-file svg-file
      (svg-print svg))

    (shell-command (format "convert %s %s" svg-file png-file))

    ;; Step 2, make the gist. Make a temp-file so the gist has a reasonable name
    (with-temp-file bib-file
      (insert entry-string))

    (let ((bib-buffer (find-file-noselect bib-file)))
      (with-current-buffer bib-buffer
        (gist-buffer))
      (kill-buffer bib-buffer))

    ;; get url off clipboard
    (setq gist-url (with-temp-buffer
                     (yank)
                     (buffer-string)))

    ;; Format and send the tweet:
    (setq full-tweet (format "#publication %s\nhttp://dx.doi.org/%s\nbibtex: %s" tweet doi gist-url))

    (with-temp-file py-file
      (insert (format "from TwitterAPI import TwitterAPI
from twitter_secrets import CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN_KEY, ACCESS_TOKEN_SECRET

api = TwitterAPI(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN_KEY, ACCESS_TOKEN_SECRET)
file = open('%s', 'rb')
data = file.read()
r = api.request('statuses/update_with_media', {'status':'''%s'''}, {'media[]':data})"
                      png-file
                      full-tweet)))

    (if (= 0 (shell-command (format "python %s" py-file)))
        (message "%s" full-tweet)
      (message "tweet failed ;("))))

;; Now, try it out.
(with-current-buffer (find-file-noselect "~/Dropbox/bibliography/references.bib")
  (goto-char (point-min))
  (re-search-forward "kitchin-2016-autom-data,")
  (tweet-bibtex))

You can see what this tweet looks like here:

That seems pretty reasonable. Now I only need to use it about 48,000 times to benefit from the time-savings M-x tweet-bibtex offers compared to manually making all those tweets ;)

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.3.4

Read and Post Comments

A python version of the s-exp bibtex entry

| categories: python, ref, bibtex | tags: | View Comments

In this post we explored representing a bibtex entry in lisp s-exp notation, and showed interesting things that enables. Here, I explore something similar in Python. The s-exp notation in Python is really more like tuples. It looks almost identical, except we need a lot of commas for the Python syntax. One significant difference in Python is we need to define the functions in advance because otherwise the function symbols are undefined. Similar to lisp, we can define the field functions at run-time in a loop. We have to use an eval statement, which some Pythonistas find distasteful, but it is not that different to me than what we did in lisp.

The syntax for "executing" the data structure is quite different than in lisp, because this data is not code in Python. Instead, we have to deconstruct the data, knowing that the function is the first object, and it takes the remaining arguments in the tuple.

Here is the proof of concept:

def article(bibtex_key, *args):
    "Return the bibtex formatted entry"
    return ',\n'.join(['@article{{{0}}}'.format(bibtex_key)] +[arg[0](arg[1]) for arg in args[0]] + ['}'])

fields = ("author", "title", "journal", "pages", "number", "doi", "url", "eprint", "year")

for f in fields:
    locals()[f] = eval ('lambda x: "  {0} = {{{1}}}".format("' + f + '", x)')

entry = (article, "hallenbeck-2013-effec-o2",
         (author, "Hallenbeck, Alexander P. and Kitchin, John R."),
         (title, "Effects of \ce{O_2} and \ce{SO_2} on the capture capacity of a primary-amine based polymeric \ce{CO_2} sorbent"),
         (journal, "Industrial \& Engineering Chemistry Research"),
         (pages, "10788-10794"),
         (year, 2013),
         (number, 31),
         (doi, "10.1021/ie400582a"),
         (url, "http://pubs.acs.org/doi/abs/10.1021/ie400582a"),
         (eprint, "http://pubs.acs.org/doi/pdf/10.1021/ie400582a"))


print entry[0](entry[1], entry[2:])
@article{hallenbeck-2013-effec-o2},
  author = {Hallenbeck, Alexander P. and Kitchin, John R.},
  title = {Effects of \ce{O_2} and \ce{SO_2} on the capture capacity of a primary-amine based polymeric \ce{CO_2} sorbent},
  journal = {Industrial \& Engineering Chemistry Research},
  pages = {10788-10794},
  year = {2013},
  number = {31},
  doi = {10.1021/ie400582a},
  url = {http://pubs.acs.org/doi/abs/10.1021/ie400582a},
  eprint = {http://pubs.acs.org/doi/pdf/10.1021/ie400582a},
}

We can still get specific fields out. Since we used a tuple here, it is not quite as nice as using a dictionary, but it is neither too bad, and it can be wrapped in a reasonably convenient function.

def article(bibtex_key, *args):
    "Return the bibtex formatted entry"
    return ',\n'.join(['@article{{{0}}}'.format(bibtex_key)] +[arg[0](arg[1]) for arg in args[0]] + ['}'])

fields = ("author", "title", "journal", "pages", "number", "doi", "url", "eprint", "year")

for f in fields:
    locals()[f] = eval ('lambda x: "  {0} = {{{1}}}".format("' + f + '", x)')

entry = (article, "hallenbeck-2013-effec-o2",
         (author, "Hallenbeck, Alexander P. and Kitchin, John R."),
         (title, "Effects of \ce{O_2} and \ce{SO_2} on the capture capacity of a primary-amine based polymeric \ce{CO_2} sorbent"),
         (journal, "Industrial \& Engineering Chemistry Research"),
         (pages, "10788-10794"),
         (year, 2013),
         (number, 31),
         (doi, "10.1021/ie400582a"),
         (url, "http://pubs.acs.org/doi/abs/10.1021/ie400582a"),
         (eprint, "http://pubs.acs.org/doi/pdf/10.1021/ie400582a"))


for field in entry[2:]:
    if field[0] == author:
        print field

def get_field(entry, field):
    for element in entry[2:]:
        if element[0] == field:
            return element[1]
    else:
        return None

print get_field(entry, title)
print get_field(entry, "bad")
(<function <lambda> at 0x1005975f0>, 'Hallenbeck, Alexander P. and Kitchin, John R.')
Effects of \ce{O_2} and \ce{SO_2} on the capture capacity of a primary-amine based polymeric \ce{CO_2} sorbent
None

So, it seems Python can do some things like lisp in treating functions like first-class objects that can be used as functions, or keys. I still like the lisp s-exp better, but this is an interesting idea for Python too.

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Read and Post Comments

A sexp version of a bibtex entry

| categories: lisp, bibtex | tags: | View Comments

Below you see a typical bibtex entry. Today we explore an alternate approach to represent the information (data) in that entry as s-expressions, i.e. as a lisp data structure. Why? because it seems like an interesting exploration!

@article{hallenbeck-2013-effec-o2,
  author =       "Hallenbeck, Alexander P. and Kitchin, John R.",
  title =        {Effects of \ce{O_2} and \ce{SO_2} on the capture capacity of a
                  primary-amine based polymeric \ce{CO_2} sorbent},
  keywords =     {RUA, orgmode},
  journal =      "Industrial \& Engineering Chemistry Research",
  pages =        "10788-10794",
  year =         2013,
  volume =       {52},
  number =       {31},
  doi =          "10.1021/ie400582a",
  url =          "http://pubs.acs.org/doi/abs/10.1021/ie400582a",
  eprint =       "http://pubs.acs.org/doi/pdf/10.1021/ie400582a",
}

Here is what that same data structure might look like as a sexp-based lisp data structure.

(article "hallenbeck-2013-effec-o2"
         (author "Hallenbeck, Alexander P. and Kitchin, John R.")
         (title "Effects of \ce{O_2} and \ce{SO_2} on the capture capacity of a primary-amine based polymeric \ce{CO_2} sorbent")
         (journal "Industrial \& Engineering Chemistry Research")
         (pages "10788-10794")
         (year 2013)
         (number 31)
         (doi "10.1021/ie400582a")
         (url "http://pubs.acs.org/doi/abs/10.1021/ie400582a")
         (eprint "http://pubs.acs.org/doi/pdf/10.1021/ie400582a"))

We can retrieve data from the sexp form pretty easily. Here we get the authors.

(let* ((art '(article "hallenbeck-2013-effec-o2"
                      (author "Hallenbeck, Alexander P. and Kitchin, John R.")
                      (title "Effects of \ce{O_2} and \ce{SO_2} on the capture capacity of a primary-amine based polymeric \ce{CO_2} sorbent")
                      (journal "Industrial \& Engineering Chemistry Research")
                      (pages "10788-10794")
                      (year 2013)
                      (number 31)
                      (doi "10.1021/ie400582a")
                      (url "http://pubs.acs.org/doi/abs/10.1021/ie400582a")
                      (eprint "http://pubs.acs.org/doi/pdf/10.1021/ie400582a")))
       (fields (cddr art)))
  (cadr (assoc 'author fields)))
Hallenbeck, Alexander P. and Kitchin, John R.

That is simple enough you might just write a little function to streamline it like this, and return a formatted string.

(defun get-article-field (article field)
  "Return value of FIELD in ARTICLE."
  (cadr (assoc field (cddr article))))

(let ((art '(article "hallenbeck-2013-effec-o2"
                     (author "Hallenbeck, Alexander P. and Kitchin, John R.")
                     (title "Effects of \ce{O_2} and \ce{SO_2} on the capture capacity of a primary-amine based polymeric \ce{CO_2} sorbent")
                     (journal "Industrial \& Engineering Chemistry Research")
                     (pages "10788-10794")
                     (year 2013)
                     (number 31)
                     (doi "10.1021/ie400582a")
                     (url "http://pubs.acs.org/doi/abs/10.1021/ie400582a")
                     (eprint "http://pubs.acs.org/doi/pdf/10.1021/ie400582a"))))
  (format "%s, doi:%s (%s)"
          (get-article-field art 'author)
          (get-article-field art 'doi)
          (get-article-field art 'year)))
Hallenbeck, Alexander P. and Kitchin, John R., doi:10.1021/ie400582a (2013)

You might be wondering, why is that even a little bit interesting? One reason is that it looks a little like what lisp returns after parsing an xml file. Another is, the data structure looks kind of like data, but it is also some code, if article was defined as a function! Let us consider what this might look like. I use a macro to define the field functions since in this case they all do the same thing, and these simply return a string with the field-name and value in curly brackets. We eval the macro to make sure it defines the function. I define an article function that wraps the fields in @bibtex-key{fields}, which defines a bibtex entry.

(defmacro make-field (field-name)
  "define a field that returns a string"
  `(defun ,(intern field-name) (content)
     (format "  %s = {%s}" ,field-name content)))

(loop for field in '("author" "title" "journal" "pages" "number" "doi" "url" "eprint" "year")
  do (eval `(make-field ,field)))

(defun article (bibtex-key &rest fields)
  (concat
   (format "@article{%s,\n" bibtex-key)
   (mapconcat (lambda (field) (eval field)) fields ",\n")
   "\n}\n"))

(article "hallenbeck-2013-effec-o2"
         (author "Hallenbeck, Alexander P. and Kitchin, John R.")
         (title "Effects of \ce{O_2} and \ce{SO_2} on the capture capacity of a primary-amine based polymeric \ce{CO_2} sorbent")
         (journal "Industrial \& Engineering Chemistry Research")
         (pages "10788-10794")
         (number 31)
         (year 2013)
         (doi "10.1021/ie400582a")
         (url "http://pubs.acs.org/doi/abs/10.1021/ie400582a")
         (eprint "http://pubs.acs.org/doi/pdf/10.1021/ie400582a"))
@article{hallenbeck-2013-effec-o2,
  author = {Hallenbeck, Alexander P. and Kitchin, John R.},
  title = {Effects of ce{O_2} and ce{SO_2} on the capture capacity of a primary-amine based polymeric ce{CO_2} sorbent},
  journal = {Industrial & Engineering Chemistry Research},
  pages = {10788-10794},
  number = {31},
  year = {2013},
  doi = {10.1021/ie400582a},
  url = {http://pubs.acs.org/doi/abs/10.1021/ie400582a},
  eprint = {http://pubs.acs.org/doi/pdf/10.1021/ie400582a}
}

Wow. We executed our data structure, and got a bibtex entry! That seems moderately interesting to me. Next is an example of taking the same data structure and rendering it as xml. This is some lispy wizardry, rather than use a macro to define functions, I temporarily define functions within a cl-flet macro, which I have to collect as a list of code. Then, I eval the list. This feels pretty odd, but seems like a lispy kind of thing to do.

(eval
 (list 'cl-flet
       (append (loop for field in '("author" "title" "journal" "pages"
                                      "number" "doi" "url" "eprint" "year")
                       collect (list (intern field)
                                     '(content)
                                     `(format "  <%s>%s</%s>" ,field content ,field)))
               '((article (bibtex-key &rest fields)
                          (concat
                           (format
                            "<article bibtex-key=\"%s\">\n" bibtex-key)
                           (mapconcat (lambda (field) (eval field)) fields "\n")
                           "\n</article>")))
               )
       ;; body of cl-flet
       '(article "hallenbeck-2013-effec-o2"
                (author "Hallenbeck, Alexander P. and Kitchin, John R.")
                (title "Effects of \ce{O_2} and \ce{SO_2} on the capture capacity of a primary-amine based polymeric \ce{CO_2} sorbent")
                (journal "Industrial \& Engineering Chemistry Research")
                (pages "10788-10794")
                (number 31)
                (year 2013)
                (doi "10.1021/ie400582a")
                (url "http://pubs.acs.org/doi/abs/10.1021/ie400582a")
                (eprint "http://pubs.acs.org/doi/pdf/10.1021/ie400582a"))))
<article bibtex-key="hallenbeck-2013-effec-o2">
  <author>Hallenbeck, Alexander P. and Kitchin, John R.</author>
  <title>Effects of ce{O_2} and ce{SO_2} on the capture capacity of a primary-amine based polymeric ce{CO_2} sorbent</title>
  <journal>Industrial & Engineering Chemistry Research</journal>
  <pages>10788-10794</pages>
  <number>31</number>
  <year>2013</year>
  <doi>10.1021/ie400582a</doi>
  <url>http://pubs.acs.org/doi/abs/10.1021/ie400582a</url>
  <eprint>http://pubs.acs.org/doi/pdf/10.1021/ie400582a</eprint>
</article>

Prefer json? No problem, just reformat the functions!

(eval
 (list 'cl-flet
       (append (loop for field in '("author" "title" "journal" "pages"
                                      "number" "doi" "url" "eprint" "year")
                       collect (list (intern field)
                                     '(content)
                                     `(format "   \"%s\": \"%s\"" ,field content)))
               '((article (bibtex-key &rest fields)
                          (concat
                           (format
                            "{\"article\":\n  {\"bibtex-key\": \"%s\",\n" bibtex-key)
                           (mapconcat (lambda (field) (eval field)) fields ",\n")
                           "}\n}"))))
       ;; body of cl-flet
       '(article "hallenbeck-2013-effec-o2"
                (author "Hallenbeck, Alexander P. and Kitchin, John R.")
                (title "Effects of \ce{O_2} and \ce{SO_2} on the capture capacity of a primary-amine based polymeric \ce{CO_2} sorbent")
                (journal "Industrial \& Engineering Chemistry Research")
                (pages "10788-10794")
                (number 31)
                (year 2013)
                (doi "10.1021/ie400582a")
                (url "http://pubs.acs.org/doi/abs/10.1021/ie400582a")
                (eprint "http://pubs.acs.org/doi/pdf/10.1021/ie400582a"))))
{"article":
  {"bibtex-key": "hallenbeck-2013-effec-o2",
   "author": "Hallenbeck, Alexander P. and Kitchin, John R.",
   "title": "Effects of ce{O_2} and ce{SO_2} on the capture capacity of a primary-amine based polymeric ce{CO_2} sorbent",
   "journal": "Industrial & Engineering Chemistry Research",
   "pages": "10788-10794",
   "number": "31",
   "year": "2013",
   "doi": "10.1021/ie400582a",
   "url": "http://pubs.acs.org/doi/abs/10.1021/ie400582a",
   "eprint": "http://pubs.acs.org/doi/pdf/10.1021/ie400582a"}
}

Is this useful? Great question. I don't plan to convert by bibtex files to sexp format anytime soon ;) The format I used above is just a simple one. It might be desirable to include individual authors instead of an author string, and maybe support attributes to establish an author order. An author structure might be more complex to include scientific ids like an orcid, alternative names, etc… Finally, the s-exp data structure is super easy to use in lisp, but other languages would have parse it into some native structure the way they parse json or xml. There is limited support for s-expressions in most other non-lispy languages.

I like the idea of data representation as code, and its conversion to some other kind of format. It is subtle here, but notice we never had to write a parser for the sexp notation. That already exists as the lisp interpreter. We did write code to use the data, and convert the data. The sexp notation is pretty easy to write, in contrast to the xml or json representations. Some interesting issues might be what to do with fields that are not defined, perhaps a macro would be used on the fly, or in the cl-flet definition. It is hard to imagine doing these things in another language than lisp!

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Read and Post Comments

Bibtex Entries from Arxiv.org

| categories: emacs, bibtex | tags: | View Comments

http://arxiv.org is an open-source physics preprint server where copies of scientific manuscripts can be found. For example, http://arxiv.org/abs/0801.1144 is a paper I wrote, and you can find the PDF for that paper here: http://arxiv.org/pdf/0801.1144v1 . Each entry at Arxiv has an arxiv number, and for this paper the number is "0801.1144". In this post, we explore some capabilities of the arxiv.el library which is part of org-ref (https://github.com/jkitchin/org-ref ).

To use this library, get the org-ref distribution, make sure it is on your path, and then require the library:

(require 'arxiv)

First, there is a new org-link: arxiv:0801.1144. This is a clickable link that simply opens arxiv.org at the URL for an arxiv number, and exports as a link to that entry in arxiv.

On the right hand side of the arxiv page, there is a link under References & Citations that takes you to a page where you can get a bibtex entry. The link for this entry is http://adsabs.harvard.edu/cgi-bin/bib_query?arXiv:0801.1144 . On that page, there is a link to a bibtex entry (http://adsabs.harvard.edu/cgi-bin/nph-bib_query?bibcode=2008PhRvB..77g5437K&data_type=BIBTEX&db_key=PHY&nocookieset=1 ). We can construct this link pretty easily, we just need the bibcode for that entry. arxiv.el provides a function for that.

(arxiv-get-bibliographic-code "0801.1144")
2008PhRvB..77g5437K

Next, once we have a url, we can get the text of the bibtex entry.

(arxiv-get-bibtex-entry "2008PhRvB..77g5437K")
@ARTICLE{2008PhRvB..77g5437K,
   author = {{Kitchin}, J.~R. and {Reuter}, K. and {Scheffler}, M.},
    title = "{Alloy surface segregation in reactive environments: First-principles atomistic thermodynamics study of Ag$_{3}$Pd(111) in oxygen atmospheres}",
  journal = {\prb},
archivePrefix = "arXiv",
   eprint = {0801.1144},
 primaryClass = "cond-mat.mtrl-sci",
 keywords = {Ab initio calculations of adsorbate structure and reactions, Density functional theory local density approximation gradient and other corrections, Oxidation},
     year = 2008,
    month = feb,
   volume = 77,
   number = 7,
      eid = {075437},
    pages = {075437},
      doi = {10.1103/PhysRevB.77.075437},
   adsurl = {http://adsabs.harvard.edu/abs/2008PhRvB..77g5437K},
  adsnote = {Provided by the SAO/NASA Astrophysics Data System}
}

Finally, arxiv.el wraps those to functions together into an interactive function arxiv-add-bibtex-entry which prompts you for an arxiv number, and then a bibtex file, and then adds the text above to your bibtex file. You can then clean the entry as you see fit. It is also possible to get the pdf for an arxiv entry via arxiv-get-pdf. This is an interactive function that will prompt you for an arxiv number and a pdf file name, and it will then get the pdf for you and open it. I have not integrated this with the bibtex entry function yet, but one would ideally clean the bibtex entry to get a uniform key, and then get the pdf and name it according to the key like we do in org-ref.

(arxiv-get-pdf "0801.1144" "0801.1144.pdf")

If you use words.el you will find a new function words-arxiv which allows you to search the selected text or word at point on arxiv.org.

I do not use arxiv.org a lot, so this is not super well tested on many articles in arxiv.org, but it has worked on the few examples I have tested so far.

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Read and Post Comments

Turn an ISBN to a bibtex entry

| categories: bibtex | tags: | View Comments

Occasionally, I need a bibtex entry for a book. Books are often identified by an ISBN number. Similar to using Crossref to get metadata about a DOI, we can use a web service to get metadata about an ISBN. From that, we might be able to construct a bibtex entry.

Here is an example of what we can get for ISBN 9780309095211. It does not seem to matter if there are dashes in the ISBN or not.

(with-current-buffer
        (url-retrieve-synchronously
"http://xisbn.worldcat.org/webservices/xid/isbn/9780309095211?method=getMetadata&format=json&fl=*")
      (buffer-substring url-http-end-of-headers (point-max)))
{
 "stat":"ok",
 "list":[{
	"url":["http://www.worldcat.org/oclc/224969280?referer=xid"],
	"publisher":"National Academies Press",
	"form":["BC"],
	"lccn":["2006016786"],
	"lang":"eng",
	"city":"Washington, D.C.",
	"author":"Committee on the Guide to Recruiting and Advancing Women Scientists and Engineers in Academia, Committee on Women in Science and Engineering, Policy and Global Affairs, National Research Council of the National Academies.",
	"ed":"[Online-Ausg.]",
	"year":"2006",
	"isbn":["9780309095211"],
	"title":"To recruit and advance women students and faculty in science and engineering",
	"oclcnum":["224969280",
	 "70060944",
	 "756709329",
	 "804792476",
	 "817950524",
	 "833420290",
	 "836338922",
	 "704551455"]}]}

We get a nice json data string back. We can parst that to get an actual data structure.

(with-current-buffer
        (url-retrieve-synchronously
"http://xisbn.worldcat.org/webservices/xid/isbn/9780309095211?method=getMetadata&format=json&fl=*")
      (json-read-from-string
        (buffer-substring url-http-end-of-headers (point-max))))
((list .
       [((oclcnum .
                  ["224969280" "70060944" "756709329" "804792476" "817950524" "833420290" "836338922" "704551455"])
         (title . "To recruit and advance women students and faculty in science and engineering")
         (isbn .
               ["9780309095211"])
         (year . "2006")
         (ed . "[Online-Ausg.]")
         (author . "Committee on the Guide to Recruiting and Advancing Women Scientists and Engineers in Academia, Committee on Women in Science and Engineering, Policy and Global Affairs, National Research Council of the National Academies.")
         (city . "Washington, D.C.")
         (lang . "eng")
         (lccn .
               ["2006016786"])
         (form .
               ["BC"])
         (publisher . "National Academies Press")
         (url .
              ["http://www.worldcat.org/oclc/224969280?referer=xid"]))])
 (stat . "ok"))

Ok, so we should check that stat is ok, then build the bibtex entry. Accessing the metadata below seems pretty hacky; but it works, and I don't understand the deep nesting of results, and there seems to be a vector in there.

(let* ((results  (with-current-buffer
                    (url-retrieve-synchronously
                     "http://xisbn.worldcat.org/webservices/xid/isbn/9780309095211?method=getMetadata&format=json&fl=*")
                  (json-read-from-string
                   (buffer-substring url-http-end-of-headers (point-max)))))
       (status (cdr (nth 1 results)))
       (metadata (aref (cdar results) 0)))

  (unless (string= "ok" status)
    (error "Status is %s" status))

  (concat "@book{,\n"
          (mapconcat (lambda (x)
                       (format "  %s={%s}," (car x) (cdr x)))
                     metadata "\n")
          "}\n"))
@book{,
  oclcnum={[224969280 70060944 756709329 804792476 817950524 833420290 836338922 704551455]},
  title={To recruit and advance women students and faculty in science and engineering},
  isbn={[9780309095211]},
  year={2006},
  ed={[Online-Ausg.]},
  author={Committee on the Guide to Recruiting and Advancing Women Scientists and Engineers in Academia, Committee on Women in Science and Engineering, Policy and Global Affairs, National Research Council of the National Academies.},
  city={Washington, D.C.},
  lang={eng},
  lccn={[2006016786]},
  form={[BC]},
  publisher={National Academies Press},
  url={[http://www.worldcat.org/oclc/224969280?referer=xid]},}

That looks good to me. Let us finally wrap it into a function that will take an ISBN and bibtex file interactively, create a bibtex entry, and insert it if there is not an entry with a key like that already. If we have selected region, lI should note this code uses some functionality from my org-ref package (and when I am done here, I am adding it to the doi-utils package inside org-ref). This is a fancy function, built from the experience I have from writing doi-utils.

(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)))
isbn-to-bibtex

That is it, for the one ISBN I have tested it on, I get a nicely sorted bibtex entry in the file I select! Hopefully that means no more tedious bibtex entry entering for books! If you use org-ref, just update to the latest version and you should be able to use this function.

Now, back to that proposal I am writing that needs a lot of citations to books that are not in my bibtex file yet, but will be soon ;)

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Read and Post Comments

Next Page ยป