1 ;;; org-collector --- collect properties into tables
3 ;; Copyright (C) 2008-2014 Free Software Foundation, Inc.
5 ;; Author: Eric Schulte <schulte dot eric at gmail dot com>
6 ;; Keywords: outlines, hypermedia, calendar, wp, experimentation,
7 ;; organization, properties
8 ;; Homepage: http://orgmode.org
11 ;; This file is not yet part of GNU Emacs.
13 ;; This program is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 3, or (at your option)
18 ;; This program is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
28 ;; Pass in an alist of columns, each column can be either a single
29 ;; property or a function which takes column names as arguments.
31 ;; For example the following propview block would collect the value of
32 ;; the 'amount' property from each header in the current buffer
34 ;; #+BEGIN: propview :cols (ITEM amount)
35 ;; | "ITEM" | "amount" |
36 ;; |---------------------+----------|
37 ;; | "December Spending" | 0 |
38 ;; | "Grocery Store" | 56.77 |
39 ;; | "Athletic club" | 75.0 |
40 ;; | "Restaurant" | 30.67 |
41 ;; | "January Spending" | 0 |
42 ;; | "Athletic club" | 75.0 |
43 ;; | "Restaurant" | 50.00 |
44 ;; |---------------------+----------|
48 ;; This slightly more selective propview block will limit those
49 ;; headers included to those in the subtree with the id 'december'
50 ;; in which the spendtype property is equal to "food"
52 ;; #+BEGIN: propview :id "december" :conds ((string= spendtype "food")) :cols (ITEM amount)
53 ;; | "ITEM" | "amount" |
54 ;; |-----------------+----------|
55 ;; | "Grocery Store" | 56.77 |
56 ;; | "Restaurant" | 30.67 |
57 ;; |-----------------+----------|
61 ;; Org Collector allows arbitrary processing of the property values
62 ;; through elisp in the cols: property. This allows for both simple
63 ;; computations as in the following example
65 ;; #+BEGIN: propview :id "results" :cols (ITEM f d list (apply '+ list) (+ f d))
66 ;; | "ITEM" | "f" | "d" | "list" | "(apply (quote +) list)" | "(+ f d)" |
67 ;; |--------+-----+-----+-------------------------+--------------------------+-----------|
68 ;; | "run1" | 2 | 33 | (quote (9 2 3 4 5 6 7)) | 36 | 35 |
69 ;; | "run2" | 2 | 34 | :na | :na | 36 |
70 ;; | "run3" | 2 | 35 | :na | :na | 37 |
71 ;; | "run4" | 2 | 36 | :na | :na | 38 |
75 ;; or more complex computations as in the following example taken from
76 ;; an org file where each header in "results" subtree contained a
77 ;; property "sorted_hits" which was passed through the
78 ;; "average-precision" elisp function
80 ;; #+BEGIN: propview :id "results" :cols (ITEM (average-precision sorted_hits))
81 ;; | "ITEM" | "(average-precision sorted_hits)" |
82 ;; |-----------+-----------------------------------|
83 ;; | run (80) | 0.105092 |
84 ;; | run (70) | 0.108142 |
85 ;; | run (10) | 0.111348 |
86 ;; | run (60) | 0.113593 |
87 ;; | run (50) | 0.116446 |
88 ;; | run (100) | 0.118863 |
96 (defvar org-propview-default-value
0
97 "Default value to insert into the propview table when the no
98 value is calculated either through lack of required variables for
99 a column, or through the generation of an error.")
101 (defun and-rest (list)
103 (if (> (length list
) 1)
104 (and (car list
) (and-rest (cdr list
)))
108 (put 'org-collector-error
110 '(error column-prop-error org-collector-error
))
112 (defun org-dblock-write:propview
(params)
113 "collect the column specification from the #+cols line
114 preceeding the dblock, then update the contents of the dblock."
117 (let ((cols (plist-get params
:cols
))
118 (inherit (plist-get params
:inherit
))
119 (conds (plist-get params
:conds
))
120 (match (plist-get params
:match
))
121 (scope (plist-get params
:scope
))
122 (noquote (plist-get params
:noquote
))
123 (colnames (plist-get params
:colnames
))
124 (defaultval (plist-get params
:defaultval
))
125 (content-lines (org-split-string (plist-get params
:content
) "\n"))
128 (when (setq id
(plist-get params
:id
))
130 ((eq id
'global
) (goto-char (point-min)))
132 ((setq idpos
(org-find-entry-with-id id
))
134 (t (error "Cannot find entry with :ID: %s" id
))))
135 (unless (eq id
'global
) (org-narrow-to-subtree))
136 (setq stringformat
(if noquote
"%s" "%S"))
137 (let ((org-propview-default-value (if defaultval defaultval org-propview-default-value
)))
138 (setq table
(org-propview-to-table
139 (org-propview-collect cols stringformat conds match scope inherit
140 (if colnames colnames cols
)) stringformat
)))
144 (while (string-match "^#" (car content-lines
))
145 (insert (pop content-lines
) "\n")))
146 (insert table
) (insert "\n|--") (org-cycle) (move-end-of-line 1)
147 (message (format "point-%d" pos
))
148 (while (setq line
(pop content-lines
))
149 (when (string-match "^#" line
)
152 (org-table-recalculate 'all
))
153 (org-collector-error (widen) (error "%s" er
))
154 (error (widen) (error "%s" er
))))
156 (defun org-propview-eval-w-props (props body
)
157 "evaluate the BODY-FORMS binding the variables using the
158 variables and values specified in props"
159 (condition-case nil
;; catch any errors
161 (lambda (pair) (list (intern (car pair
)) (cdr pair
)))
166 (defun org-propview-get-with-inherited (&optional inherit
)
168 (org-entry-properties)
171 (let* ((n (symbol-name i
))
172 (p (org-entry-get (point) n
'do-inherit
)))
173 (when p
(cons n p
))))
176 (defun org-propview-collect (cols stringformat
&optional conds match scope inherit colnames
)
178 ;; collect the properties from every header
180 (let ((org-trust-scanner-tags t
) alst
)
182 (quote (cons (cons "ITEM" (org-get-heading t
))
183 (org-propview-get-with-inherited inherit
)))
185 ;; read property values
187 (mapcar (lambda (props)
188 (mapcar (lambda (pair)
189 (cons (car pair
) (org-babel-read (cdr pair
))))
192 ;; collect all property names
194 (mapcar 'intern
(delete-dups
195 (apply 'append
(mapcar (lambda (header)
196 (mapcar 'car header
))
200 (if colnames colnames
(mapcar (lambda (el) (format stringformat el
)) cols
))
201 'hline
) ;; ------------------------------------------------
202 (mapcar ;; calculate the value of the column for each header
203 (lambda (props) (mapcar (lambda (col)
204 (let ((result (org-propview-eval-w-props props col
)))
205 (if result result org-propview-default-value
)))
208 ;; eliminate the headers which don't satisfy the property
212 (if (and-rest (mapcar
214 (org-propview-eval-w-props props col
))
220 (defun org-propview-to-table (results stringformat
)
221 ;; (message (format "cols:%S" cols))
225 (if (equal row
'hline
)
227 (mapcar (lambda (el) (format stringformat el
)) row
)))
228 (delq nil results
)) '()))
230 (provide 'org-collector
)
231 ;;; org-collector ends here