Merge branch 'master' of github.com:jkitchin/org-ref
[org-ref.git] / pubmed.el
1 ;;; pubmed.el --- Links and functions for Pubmed and NIH databases  -*- lexical-binding: t; -*-
2
3 ;; Copyright (C) 2015  John Kitchin
4
5 ;; Author: John Kitchin <jkitchin@andrew.cmu.edu>
6 ;; Keywords: convenience
7
8 ;; This program is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation, either version 3 of the License, or
11 ;; (at your option) any later version.
12
13 ;; This program is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 ;; GNU General Public License for more details.
17
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 ;;; Commentary:
22
23 ;; * Introduction
24
25 ;; This document is an experiment at creating a literate program to provide functions for interacting with pubmed databases.
26
27 ;; This library provides links that go to pubmed resources, e.g.
28
29 ;; pmcid:PMC3498956
30 ;;
31 ;; pmid:23162369
32 ;;
33 ;; and nihmsid:NIHMS395714
34 ;;
35 ;; See http://www.ncbi.nlm.nih.gov/pmc/about/public-access-info/#p3 for details of these identifiers.
36 ;;
37 ;; For PMID there is one interactive function that inserts a bibtex entry: pubmed-insert-bibtex-from-pmid.
38
39 ;; This library is complementary to [[./doi-utils.org]].
40
41 ;;; Code:
42
43 ;; * PMID (from PubMed) link and functions
44
45 ;; A PMID is a number that identifies an entry in the Pubmed database.  The PMID
46 ;; is a unique reference number for PubMed citations. The PMID is a distinctly
47 ;; different number from the PMCID and is used only for PubMed records.
48
49 (org-add-link-type
50  "pmid"
51  ;; clicking
52  (lambda (link-string) (browse-url (format "http://www.ncbi.nlm.nih.gov/pubmed/%s" link-string)))
53  ;; formatting
54 (lambda (keyword desc format)
55    (cond
56     ((eq format 'html)
57      (format "<a href=\"http://www.ncbi.nlm.nih.gov/pmc/articles/mid/%s\">pmid:%s</a>" keyword keyword)); no output for html
58     ((eq format 'latex)
59      ;; write out the latex command
60      (format "\\url{http://www.ncbi.nlm.nih.gov/pmc/articles/mid/%s}" keyword)))))
61
62 ;; ** Get MEDLINE metadata
63
64 ;; We can get bibliographic metadata from a pmid. Here we get the MEDLINE
65 ;; text. The website wraps the data in <pre></pre> tags.
66
67 (defun pubmed-get-medline (pmid)
68   "Get MEDLINE text for PMID as a string."
69   (with-current-buffer
70     (url-retrieve-synchronously
71      (format "http://www.ncbi.nlm.nih.gov/pubmed/%s/?report=medline&format=text" pmid))
72     (goto-char (point-min))
73     (let ((p1 (search-forward "<pre>"))
74           (p2 (search-forward "</pre>")))
75       (buffer-substring (+ 1 p1) (- p2 6)))))
76
77 ;; ** Parse the PMID MEDLINE data
78
79 ;; We can parse this into a data structure.
80
81 (defun pubmed-parse-medline (pmid)
82   "Parse the medline text for PMID and return a list of cons cells."
83   (let ((data '())
84         (p1)
85         (p2)
86         (tag)
87         (value))
88     (with-temp-buffer (insert (pubmed-get-medline pmid))
89                       (goto-char (point-min))
90                       (while (re-search-forward "\\(^[A-Z]\\{2,4\\}\\)\\s-*- " nil t)
91                         (setq tag (match-string 1))
92                         ;; point is at end of the search
93                         (setq p1 (point))
94                         ;; now go to next tag
95                         (re-search-forward "\\(^[A-Z]\\{2,4\\}\\)\\s-*- " nil t)
96                         (setq p2 (- (match-beginning 1) 1))
97                         (setq value (buffer-substring p1 p2))
98                         (setq data (append data (list (cons tag value))))
99                         ;; now go back to last tag to get the next one
100                         (goto-char p1)))
101     data))
102
103 ;; ** PMID to bibtex entry
104
105 ;; The point of parsing the MEDLINE text is so we can make bibtex entries. We
106 ;; only support Journal articles for now.
107
108 (defun pubmed-pmid-to-bibtex (pmid)
109   "Convert a PMID to a bibtex entry."
110   (let* ((data (pubmed-parse-medline pmid))
111          (type (cdr (assoc "PT" data)))
112          (title (cdr (assoc "TI" data)))
113          (authors (mapconcat 'cdr
114                              (-filter (lambda (x)
115                                         (string= (car x) "FAU"))
116                                       data)
117                              " and "))
118          (abstract (cdr (assoc "AB" data)))
119          (volume (cdr (assoc "VI" data)))
120          (issue (cdr (assoc "IP" data)))
121          (journal (cdr (assoc "JT" data)))
122          (year (cdr (assoc "DP" data)))
123          (pages (cdr (assoc "PG" data)))
124          (aid (cdr (assoc "AID" data))))
125
126     (cond
127      ((string= type "JOURNAL ARTICLE")
128       (concat "@article{,
129  author = {" authors "},
130  title = {" title "},
131  abstract = {" abstract "},
132  journal = {" journal "},
133  volume = {" volume "},
134  number = {" issue "},
135  year = {" (car (split-string year)) "},
136  pages = {" pages "},
137  doi = {" (replace-regexp-in-string " \\[doi\\]" "" aid) "},
138 }"))
139     (t
140      (message "No conversion for type: %s" type)))))
141
142 ;; And we probably want to be able to insert a bibtex entry
143
144 (defun pubmed-insert-bibtex-from-pmid (pmid)
145  "Insert a bibtex entry at point derived from PMID.
146 You must clean the entry after insertion."
147  (interactive "sPMID: ")
148  (insert (pubmed-pmid-to-bibtex pmid)))
149
150 ;; ** PMID to xml
151
152 ;; We can also get xml of the MEDLINE data. The web page here also wraps the xml
153 ;; in a <pre> block and escapes the <> with &lt; and &gt;, which we have to
154 ;; undo. I have not used this code for anything, so I am not sure how good the
155 ;; xml code is.
156
157 (defun pubmed-get-medline-xml (pmid)
158   "Get MEDLINE xml for PMID as a string."
159   (interactive)
160   (with-current-buffer
161     (url-retrieve-synchronously
162      (format "http://www.ncbi.nlm.nih.gov/pubmed/%s/?report=xml&format=text" pmid))
163     (goto-char (point-min))
164     (while (search-forward "&lt;" nil t)
165       (replace-match "<"))
166     (goto-char (point-min))
167     (while (search-forward "&gt;" nil t)
168       (replace-match ">"))
169     (goto-char (point-min))
170
171     (let ((p1 (search-forward "<pre>"))
172           (p2 (search-forward "</pre>")))
173       (buffer-substring (+ 1 p1) (- p2 6)))))
174
175 ;; * Pubmed Central (PMC) link
176
177 ;; A PMCID starts with PMC and is followed by numbers. The PMCID is a unique
178 ;; reference number or identifier that is assigned to every article that is
179 ;; accepted into PMC. The PMCID is also used by recipients of NIH funding to
180 ;; demonstrate compliance with the NIH Public Access policy. The PMCID can be
181 ;; found in both PMC and PubMed.
182
183 ;; Here we define a new link. Clicking on it simply opens a webpage to the
184 ;; article.
185
186 (org-add-link-type
187  "pmcid"
188  ;; clicking
189  (lambda (link-string) (browse-url (format "http://www.ncbi.nlm.nih.gov/pmc/articles/%s" link-string)))
190  ;; formatting
191 (lambda (keyword desc format)
192    (cond
193     ((eq format 'html)
194      (format "<a href=\"http://www.ncbi.nlm.nih.gov/pmc/articles/%s\">pmcid:%s</a>" keyword keyword))
195     ((eq format 'latex)
196      (format "\\url{http://www.ncbi.nlm.nih.gov/pmc/articles/%s}" keyword)))))
197
198 ;; * NIHMSID
199
200 ;; The NIHMSID is a preliminary article identifier that applies only to
201 ;; manuscripts deposited through the NIHMS system. The NIHMSID is only valid for
202 ;; compliance reporting for 90 days after the publication date of an
203 ;; article. Once the Web version of the NIHMS submission is approved for
204 ;; inclusion in PMC and the corresponding citation is in PubMed, the article
205 ;; will also be assigned a PMCID.
206
207 (org-add-link-type
208  "nihmsid"
209  ;; clicking
210  (lambda (link-string) (browse-url (format "http://www.ncbi.nlm.nih.gov/pmc/articles/mid/%s" link-string)))
211  ;; formatting
212  (lambda (keyword desc format)
213    (cond
214     ((eq format 'html)
215      (format "<a href=\"http://www.ncbi.nlm.nih.gov/pmc/articles/mid//%s\">nihmsid:%s</a>" keyword keyword))
216     ((eq format 'latex)
217      ;; write out the latex command
218      (format "\\url{http://www.ncbi.nlm.nih.gov/pmc/articles/mid/%s}" keyword)))))
219
220
221 (provide 'pubmed)
222 ;;; pubmed.el ends here