-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathorb-book.el
279 lines (247 loc) · 10.6 KB
/
orb-book.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
;;; orb-book.el --- Book manager empowered by org-roam-bibtex -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2021 Junwei Wang
;;
;; Author: Junwei Wang <https://github.com/junwei-wang>
;; Maintainer: Junwei Wang <[email protected]>
;; Created: September 24, 2021
;; Modified: September 24, 2021
;; Version: 0.0.1
;; Keywords: bib book
;; Homepage: https://github.com/junwei-wang/orb-book
;; Package-Requires: ((emacs "27.2"))
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;;
;; The API is inspired by calibredb <https://github.com/chenyanming/calibredb.el>.
;;
;; Description:
;;
;;; Code:
;; ============================================================================
;;;; Dependencies
;; ============================================================================
(require 'helm)
(require 'org-roam-bibtex)
;; ============================================================================
;;;; Customize definitions
;; ============================================================================
(defcustom orb-book-helm-actions
(if (fboundp 'helm-make-actions)
(helm-make-actions
"Open PDF file" 'orb-book-open-pdf
"Attach PDF file" 'orb-book-add-pdf
"View details" 'orb-book-show-entry))
"Default actions for orb-book helm."
:group 'orb-book
:type '(alist :key-type string :value-type function))
(defcustom orb-book-bibliography nil
"Bib file for inserting new books by orb-book.
This file should be added to variable `bibtex-completion-bibliography'"
:group 'orb-book
:type '(string))
;; ============================================================================
;;;; Orb book query books, and process them
;; ============================================================================
(defun orb-book--all-refs ()
"Get the references for all org-roam nodes for books.
A book org-roam node is a node generated dy org-roam-bibtex and having a tag
\"book\" or \"BOOK\" or \"Book\"."
(org-roam-db-query [:select [tags:node_id, ref]
:from tags
:join refs :on (= tags:node_id refs:node_id)
:where (in tag ["book" "BOOK" "Book"])]))
(defun orb-book-query-book-tags (node-id)
"Query the tags of a book with NODE-ID."
(mapcar 'car
(org-roam-db-query [:select tag :from tags
:where (= node_id $s1)
:and (not-in tag ["book" "BOOK" "Book"])]
node-id)))
(defun orb-book-query ()
"Query books' information from bibtex."
(mapcar
(lambda(entry)
(let* ((node-id (nth 0 entry))
(ref (nth 1 entry))
(bib-entry (bibtex-completion-get-entry ref))
(title (bibtex-completion-get-value "title" bib-entry))
(long-authors (or (bibtex-completion-get-value "author" bib-entry)
(bibtex-completion-get-value "editor" bib-entry)))
(short-authors (bibtex-completion-shorten-authors long-authors))
(publisher (bibtex-completion-get-value "publisher" bib-entry))
(year (or (bibtex-completion-get-value "year" bib-entry)
(car (split-string (bibtex-completion-get-value "date" bib-entry "") "-"))))
(raw-edition (bibtex-completion-get-value "edition" bib-entry))
(edition (cond
((numberp raw-edition) raw-edition)
((stringp raw-edition)
(cond ((string= "Second" raw-edition) 2)
((string-match "[1-9]" raw-edition)
(string-to-number
(match-string (string-match "[1-9][0-9]*" raw-edition) raw-edition)))
(t raw-edition)))
(t raw-edition)))
(isbn (bibtex-completion-get-value "isbn" bib-entry))
(series (bibtex-completion-get-value "series" bib-entry))
(url (bibtex-completion-get-value "url" bib-entry))
(has-pdf (bibtex-completion-get-value "=has-pdf=" bib-entry))
(tags (orb-book-query-book-tags node-id)))
(list ref title long-authors short-authors year publisher edition series isbn url has-pdf tags)))
(orb-book--all-refs)))
(defun orb-book-query-to-alist (item)
"Builds alist out of a full `orb-book-query' query record result.
ITEM is an entry in the query result."
(if item
`((:ref ,(nth 0 item))
(:title ,(nth 1 item))
(:authors ,(nth 2 item))
(:short-authors ,(nth 3 item))
(:year ,(nth 4 item))
(:publisher ,(nth 5 item))
(:edition ,(nth 6 item))
(:series ,(nth 7 item))
(:isbn ,(nth 8 item))
(:url ,(nth 9 item))
(:has-pdf ,(nth 10 item))
(:tags ,(nth 11 item)))))
(defun orb-book-format-column (string width &optional right-align)
"Return STRING truncated or padded to WIDTH.
Argument RIGHT-ALIGN."
(cond ((< width 0) string)
((= width 0) "")
(t (format (format "%%%s%d.%ds" (if right-align "" "-") width width)
(or string "")))))
(defun orb-book-getattr (my-alist key)
"Get the associated attribute for KEY in MY-ALIST."
(cadr (assoc key my-alist)))
(defun orb-book-format-item (book-alist)
"Format the candidate string shown in helm.
Argument BOOK-ALIST."
(let ((title (orb-book-getattr book-alist :title))
(short-authors (orb-book-getattr book-alist :short-authors))
(year (orb-book-getattr book-alist :year))
(edition (orb-book-getattr book-alist :edition))
(publisher (orb-book-getattr book-alist :publisher))
(has-pdf (orb-book-getattr book-alist :has-pdf))
(tags (orb-book-getattr book-alist :tags)))
(format
"%s %s %s %s %s %s %s"
(orb-book-format-column has-pdf 1)
(orb-book-format-column short-authors 20)
(orb-book-format-column year 4 t)
(orb-book-format-column title 80)
(orb-book-format-column edition 4 t)
(orb-book-format-column publisher 30)
(orb-book-format-column tags 30))))
(defun orb-book-get-list-for-display (item-list)
"Get book list for display in helm from orb-book ITEM-LIST."
(mapcar (lambda(item) (list (orb-book-format-item item) item)) item-list))
(defun orb-book-candidates()
"Generate books candidates alist."
(let* ((query-result (orb-book-query)))
(cond ((equal nil query-result) '(""))
(t (orb-book-get-list-for-display
(mapcar 'orb-book-query-to-alist query-result))))))
;; ============================================================================
;;;; Orb book helm actions
;; ============================================================================
(defun orb-book-open-pdf (entry)
"Open associated pdf file for the book ENTRY."
(let ((ref (orb-book-getattr (car entry) :ref)))
(bibtex-completion-open-pdf (list ref))))
(defun orb-book-add-pdf (entry)
"Open associated pdf file for the book ENTRY."
(let ((ref (orb-book-getattr (car entry) :ref)))
(bibtex-completion-add-pdf-to-library (list ref))))
;; ============================================================================
;;;; Orb book helm search
;; ============================================================================
(defun orb-book-helm-read ()
"Helm read for orb-book."
(when (fboundp 'helm)
(when (get-buffer "*helm action*")
(kill-buffer "*helm action*"))
(unwind-protect
(helm :sources (if (fboundp 'helm-build-sync-source)
(helm-build-sync-source "Book list"
:candidates (orb-book-candidates)
:action 'orb-book-helm-actions))
:buffer "*helm orb-book*") )))
(defun orb-book-find-helm ()
"Use helm to list all book details."
(interactive)
(orb-book-helm-read))
;; ============================================================================
;;;; Add new book
;; ============================================================================
(defun orb-book-copy-add-book-buffer ()
"Copy the new book entry to the corresponding bib file."
(interactive)
;; kill all comments, then format the buffer,
;; then append the bib entry to bib file
(goto-char 0)
(comment-kill (count-lines (point-min) (point-max)))
(bibtex-reformat)
(append-to-file (point-min) (point-max) orb-book-bibliography)
(message "book entry has been added to `%s'" orb-book-bibliography)
(kill-current-buffer)
;; TODO get the ref key of the bib entry
;;
;; TODO test whether note already exist
;; (let ((bib-buffer-name (buffer-name)))
;; (save-excursion
;; ;; create org-roam-bibtex node
;; ;; FIXME: no ROAM_REFS is created
;; (orb-bibtex-completion-edit-note
;; (list (read-string "Enter the bib ref key (e.g. \"BOOK:Knuth1997:AOCP2\"): ")))
;; (kill-buffer bib-buffer-name)
;; )
)
(defun orb-book-add-book-quit ()
"Quit the buffer in `orb-book-add-book-mode'."
(interactive)
(kill-current-buffer))
(defvar orb-book-add-book-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "\C-c\C-k" 'orb-book-add-book-quit)
(define-key map "\C-c\C-c" 'orb-book-copy-add-book-buffer)
map)
"Keymap for `orb-book-add-book-mode'.")
(define-derived-mode orb-book-add-book-mode
bibtex-mode "orb-book add book"
"Major mode for org-book add book.")
(defun orb-book-add-book ()
"Add a new book to orb-book."
(interactive)
(unless orb-book-bibliography
(error "`orb-book-bibliography' is not set"))
(switch-to-buffer (make-temp-name "*orb-book-new-book*"))
(insert "\n\n"
"@Comment please insert your bibtex bib entry above.\n"
"@Comment\n"
"@Comment press C-c C-c to finish\n"
"@Comment press C-c C-k to quit\n"
"@Comment\n"
"@Comment for example:\n"
"@Comment\n"
"@Comment @book{BOOK:Knuth1997:AOCP2,\n"
"@Comment title = {The art of computer programming. Vol.2. Seminumerical algorithms},\n"
"@Comment author = {Knuth, Donald E},\n"
"@Comment publisher = {Addison-Wesley Professional},\n"
"@Comment isbn = {0-201-89684-2,978-0-201-89684-8,978-0-201-89683-1,0201896834,978-0-201-89685-5,0201896850,978-0-201-03804-0},\n"
"@Comment year = {1997},\n"
"@Comment series = {},\n"
"@Comment edition = {3ed.},\n"
"@Comment volume = {},\n"
"@Comment url = {https://www-cs-faculty.stanford.edu/~knuth/taocp.html}\n"
"@Comment }\n")
(goto-char 0)
(orb-book-add-book-mode))
(provide 'orb-book)
;;; orb-book.el ends here
; Local Variables:
; fill-column: 79
; End: