Embedding a list in org-mode tables? - emacs

Can I have tables like the following generated using org-mode,
Day 1 | a) A |
| b) B |
| c) C |
Day 2 | a) D |
Day 3 | a) E |

Like this?
|-----------+------|
| Monday | a) A |
| | b) B |
| | c) C |
|-----------+------|
| Tuesday | a) D |
|-----------+------|
| Wednesday | a) E |
|-----------+------|
I just entered
|||
Then RET inside the table to get more rows and C-RET to get lines. Fill out and go. Seems to export to HTML and text just fine...

Also you can write a function that converts arbitrary elisp list of lists to table. It can be useful to debug (message the "table" string to Messages buffer) lists of lists.
Something like this:
(defun list-of-lists-to-org-table (lol &optional justify min-cell-width columns)
(let ((buf (get-buffer-create "*org-tb*")))
(with-current-buffer buf
(erase-buffer)
(mapcar (lambda (x)
(mapcar (lambda (y) (insert (format "%s&" y))) x)(insert "\n")) lol)
(table-capture 1 (point-max) "&" "\n" justify min-cell-width columns)
(buffer-substring-no-properties (point-min) (point-max))
)))
Example input:
(list-of-lists-to-org-table '((Abc Def "xyz") (1 1 X1) (2 4 X2) (3 9 X3) (4 16 X4)))
Example output:
+-----+-----+-----+
|Abc |Def |xyz |
+-----+-----+-----+
|1 |1 |X1 |
+-----+-----+-----+
|2 |4 |X2 |
+-----+-----+-----+
|3 |9 |X3 |
+-----+-----+-----+
|4 |16 |X4 |
+-----+-----+-----+

Related

org-mode review clocked time by multiple tags

I would like to review my clocked time by tags, to answer e.g. how much time did I spend this week on my health, on my work, on a client or on social relationships?
I am using tags, because the items I want to review my clocked time, can be spread over multiple files and hidden in different subtrees. Maybe this is the problem and I need to restructure? E.g. "Write an entry in your diary" should be stored under "notes", but summed into "health" and of course under "notes" there would be also other notes like "finance"...
Any other solution, e.g. using a custom agenda view or categories instead of tags would also be very welcome.
So far I have tried to use org mode clocktable grouped by multiple tags. For using clocktables I was using this test data:
* Take out the trash :private:
:LOGBOOK:
CLOCK: [2021-03-12 Fri 11:24]--[2021-03-12 Fri 11:30] => 0:06
:END:
* Update document for client :client1:
:LOGBOOK:
CLOCK: [2021-03-12 Fri 12:45]--[2021-03-12 Fri 13:30] => 0:45
:END:
* Create my awesome note for work :work:
:LOGBOOK:
CLOCK: [2021-03-13 Sat 11:24]--[2021-03-13 Sat 12:53] => 1:29
:END:
* Fill in timesheet :work:
:LOGBOOK:
CLOCK: [2021-03-12 Fri 11:24]--[2021-03-12 Fri 11:40] => 0:16
:END:
I have found the following solutions, neither seems to work with my system.
Here my problem is perfectly described. I've downloaded the code, it will create a table, but won't show the sums. Unfortunately, that code snipped seems too old, and I am not able to fix it. I have found a a fork of that snipped which gives me this result:
#+BEGIN: clocktable-by-tag :tags ("work" "client1")
| Tag | Headline | Time (h) |
| | | <r> |
|---------+------------+----------|
| work | *Tag time* | *0.00* |
|---------+------------+----------|
| client1 | *Tag time* | *0.00* |
#+END:
Here I found another solution. The author uses a function to format the times, which are then used by orgaggregate. Unfortunately already the first step, doesn't seem to work correctly:
#+BEGIN: clocktable :scope file :maxlevel 3 :tags t :match "work|client1" :header "#+TBLNAME: timetable\n"
#+TBLNAME: timetable
| Tags | Headline | Time | T |
|---------+---------------------------------+--------+--------|
| | *Total time* | *2:30* | #ERROR |
|---------+---------------------------------+--------+--------|
| client1 | Update document for client | 0:45 | #ERROR |
| work | Create my awesome note for work | 1:29 | #ERROR |
| work | Fill in timesheet | 0:16 | #ERROR |
#+TBLFM: $4='(convert-org-clocktable-time-to-hhmm $3)::#1$4='(format "%s" "T")
#+END:
It really shouldn't be that hard, what I would like to achieve. At the moment the best solution I have, is to use multiple tables, one for each tag:
#+BEGIN: clocktable :scope file :maxlevel 3 :match "work"
#+CAPTION: Clock summary at [2022-01-03 Mon 16:55]
| Headline | Time |
|---------------------------------+--------|
| *Total time* | *1:45* |
|---------------------------------+--------|
| Create my awesome note for work | 1:29 |
| Fill in timesheet | 0:16 |
#+END:
#+BEGIN: clocktable :scope file :maxlevel 3 :match "client1"
#+CAPTION: Clock summary at [2022-01-03 Mon 16:55]
| Headline | Time |
|----------------------------+--------|
| *Total time* | *0:45* |
|----------------------------+--------|
| Update document for client | 0:45 |
#+END:
The first solution you found was almost there. It had two issues that gave you the wrong result:
It considered only agenda files, not the current file as an input for the table. That's the reason why you were getting empty results.
To convert minutes to a nicely formatted display, the function org-duration-from-minutes can be used.
With these updates, your test file gave this result to me:
#+BEGIN: clocktable-by-tag :tags ("work" "client1")
| Tag | Headline | Time (h) | |
|---------+-----------------------------------+----------+------|
| work | *Tag time* | 1:29 | |
| | File *test.org* | 1:29 | |
| | . Create my awesome note for work | | 1:29 |
|---------+-----------------------------------+----------+------|
| client1 | *Tag time* | 0:45 | |
| | File *test.org* | 0:45 | |
| | . Update document for client | | 0:45 |
#+END:
You can get a better summary by using :summary t (I personally prefer this option):
#+BEGIN: clocktable-by-tag :tags ("work" "client1") :summary t
| Tag | Headline | Time (h) |
|---------+------------+----------|
| work | *Tag time* | 1:29 |
|---------+------------+----------|
| client1 | *Tag time* | 0:45 |
#+END:
The :scope tag also works, except that I didn't implement scopes tighter than the current file (such as subtree).
You can find the code in a gist, or copy and paste from below:
(require 'org-clock)
(defun clocktable-by-tag/shift-cell (n)
(let ((str ""))
(dotimes (i n)
(setq str (concat str "| ")))
str))
(defun clocktable-by-tag/insert-tag (files params)
(let ((tag (plist-get params :tags))
(summary-only (plist-get params :summary))
(total 0))
(insert "|--\n")
(insert (format "| %s | *Tag time* |\n" tag))
(mapcar
(lambda (file)
(let ((clock-data (with-current-buffer (find-buffer-visiting file)
(org-clock-get-table-data (buffer-name) params))))
(when (> (nth 1 clock-data) 0)
(setq total (+ total (nth 1 clock-data)))
(if (not summary-only)
(progn
(insert (format "| | File *%s* | %s |\n"
(file-name-nondirectory file)
(org-duration-from-minutes (nth 1 clock-data))))
(dolist (entry (nth 2 clock-data))
(insert (format "| | . %s%s | %s %s |\n"
(org-clocktable-indent-string (nth 0 entry))
(nth 1 entry)
(clocktable-by-tag/shift-cell (nth 0 entry))
(org-duration-from-minutes (nth 4 entry))))))))))
files)
(save-excursion
(re-search-backward "*Tag time*")
(org-table-next-field)
(org-table-blank-field)
(insert (org-duration-from-minutes total))))
(org-table-align))
(defun org-dblock-write:clocktable-by-tag (params)
(insert "| Tag | Headline | Time (h) |\n")
(let ((params (org-combine-plists org-clocktable-defaults params))
(base-buffer (org-base-buffer (current-buffer)))
(files (pcase (plist-get params :scope)
(`agenda
(org-agenda-files t))
(`agenda-with-archives
(org-add-archive-files (org-agenda-files t)))
(`file-with-archives
(let ((base-file (buffer-file-name base-buffer)))
(and base-file
(org-add-archive-files (list base-file)))))
((or `nil `file)
(list (buffer-file-name)))
(_ (user-error "Unknown scope: %S" scope))))
(tags (plist-get params :tags)))
(mapcar (lambda (tag)
(clocktable-by-tag/insert-tag files (org-combine-plists params `(:match ,tag :tags ,tag))))
tags)))

How to format table fields as currency in org-mode

I'd like to format fields in an org-mode table as currency
- meaning with currency symbol ($) and commas as thousands separators. I've been using $%.2f to get e.g. $1000.00 but how to get the comma separators e.g. $1,000.00 ? I've RTFM but perhaps I am too dense to get it. Either calc or elisp formula is fine. See sample table below:
| Item | Quantity | Price | Ext |
|----------+----------+--------+----------|
| Widget 1 | 10 | 100.00 | 1000.00 |
| Widget 2 | 5 | 50.00 | 250.00 |
| Widget 3 | 1 | 5.00 | 5.00 |
|----------+----------+--------+----------|
| | | Total | $1255.00 |
#+TBLFM: $4=($2*$3);%.2f::#5$4=vsum(#2..#4);$%.2f
I found no way of doing it consistently, such that you get numbers with thousands separators, and these numbers instead are correctly interpreted for further calculations. So this is not an answer, just to record my research so far.
The following example steals code to format numbers with thousands separators. C-c C-c on the code to define the function, or add to your init file.
Then, the grand total is calculated using elisp, and transformed with the new formatting function.
#+begin_src elisp :results none
(defun my-thousands-separate (num)
"Formats the (possibly floating point) number with a thousands
separator."
(let* ((nstr (number-to-string num))
(dot-ind (string-match "\\." nstr))
(nstr-no-decimal (if dot-ind
(substring nstr 0 dot-ind)
nstr))
(nrest (if dot-ind
(substring nstr dot-ind)
nil))
(pretty nil)
(cnt 0))
(dolist (c (reverse (append nstr-no-decimal nil)))
(if (and (zerop (% cnt 3)) (> cnt 0))
(setq pretty (cons ?, pretty)))
(setq pretty (cons c pretty))
(setq cnt (1+ cnt)))
(concat pretty nrest)))
#+end_src
| Item | Quantity | Price | Ext |
|----------+----------+------------+--------------|
| Widget 1 | 10 | 1001001.00 | 10010010.00 |
| Widget 2 | 5 | 501001.00 | 2505005.00 |
| Widget 3 | 1 | 51001.00 | 51001.00 |
|----------+----------+------------+--------------|
| | | Total | 12,566,016.0 |
#+TBLFM: $4=($2*$3);%.2f::#5$4='(my-thousands-separate (apply '+ '(#2..#4)));N
Note that if you do the same for the row totals, then the comma-separated numbers will not be interpreted correctly for the grand total.
The correct way should be to set the numeric locale and let printf do the trick, but I don't know how to set this for emacs.

user-error: Not in table data field

The following table
#+BEGIN: clocktable :maxlevel 3 :tcolumns 4 :scope file :block 2015-6 :narrow 60
| Headline | Time | | |
|--------------+--------+------+------|
| *Total time* | *3:57* | | |
|--------------+--------+------+------|
| Tasks | 3:57 | | |
| 1 | | 3:57 | |
| 2 | | | 3:57 |
#+TBLFM: #3$5..#>$5=vsum($2..$4)*100
gives me
user-error: Not in table data field
where it should add the total of all previous columns to into a new column and multiply them by 100. Via http://notes.secretsauce.net/notes/2014/10/01_org-mode-for-invoices.html
I see two solutions a) add a command to TBLFM to add an additional
column b) make clocktable generate the additional column, but I don't
know how to do either.
Here is a quick hack that you could bind to a key that adds the extra column if needed and then recalculates:
(defun maybe-add-column-and-update ()
(interactive)
(save-excursion
(end-of-line)
(if (= (org-table-current-column) 5)
(let ((org-table-fix-formulas-confirm
(lambda (arg) nil)))
(org-table-insert-column)))
(org-table-recalculate 'iterate)))

emacs org mode: (conditional) if cell is empty then

How do you express the following conditional for an org mode table?
for every cell between the 2nd and 3rd hline:
if the cell is empty,
set the contents of the cell to todays date.
else
leave the cells contents as they are.
so, given the following table, i would like to insert todays date in the empty cell.
|------------------|
| date |
|------------------|
| [2014-05-23 Fri] |
| [2014-05-24 Sat] |
| |
|------------------|
The custom calc function appendToday will do what you want. It handles the case where all fields between the hlines are empty as well.
(defmath appendToday (idx v)
(let ((d (date (month (now)) (day (now))))
(len (vlen v)))
(if (<= idx len)
(if (equal (cadr v) 0)
d
(nth idx v)
)
d
)
)
)
The table before evaluation looks like:
|------------------|
| <2014-05-26 Mon> |
| |
| |
| |
| |
| |
|------------------|
#+TBLFM: #I..#II$1=appendToday(##,#I..#II$1)
The table after evaluation looks like:
|------------------|
| <2014-05-26 Mon> |
| <2014-05-28 Wed> |
| <2014-05-28 Wed> |
| <2014-05-28 Wed> |
| <2014-05-28 Wed> |
| <2014-05-28 Wed> |
|------------------|
#+TBLFM: #I..#II$1=appendToday(##,#I..#II$1)

Convert between an org-mode table and a table.el table without user interaction

I convert org-mode table to table.el table. For that I select the table:
| Option | Type | Value | Descr |
| -[no]h | bool | yes | Print |
| -[no]versio | bool | no | Print |
| -nice | int | 0 | Set t |
| -[no]v | bool | no | Be lo |
| -time | real | -1 | Take |
| -[no]rmvsbd | bool | yes | Removvirtual |
| sites | | | |
| -maxwarn | int | 0 | Numbe |
| procenerate | | | |
| unsta | | | |
| -[no]zero | bool | no | Set pthout |
| defau error | | | |
| -[no]renum | bool | yes | Renum |
| atomty | | | |
and press C-c ~. org-mode then asks me
Convert table to table.el table? (y or n)
How do I answer y programmically? I read the docs of that defun -- there's not way to do it with prefix arg.
Similar functionality in bash:
echo y | script-which-asks-y-or-n
C-c ~ calls the command org-table-create-with-table.el, which provides a bunch of wrappers around calling org-table-convert. If you want to use this function when you know you are already in an org-mode table, you don't need the wrappers, you just need the two (and probably only one) commands: org-table-align and org-table-convert.
So if you're doing this interactively, you can just call M-x org-table-convert and you're done. This assumes the table is already aligned. You can do this by hand by tabbing from one cell to the next, which triggers table alignment. Or you can do it with a small function:
(defun my-convert-tables ()
"No questions asked, just convert the table"
(interactive)
(org-table-align)
(org-table-convert))
You can do this programmatically as follows. You would replace the function name test1 with org-table-create-with-table.el in your defadvice functions that would be otherwise the same as those below.
Using defadvice to run some code before and after the function, we can save the function bound to the symbol y-or-n-p to a global variable and rebind it to a function that simply returns true. After the function we then restore the original functionality.
(setq save-y-or-n-p nil)
(defadvice test1 (around always-yes)
(fset 'save-y-or-n-p (symbol-function 'y-or-n-p))
(fset 'y-or-n-p (lambda (s) t))
ad-do-it
(fset 'y-or-n-p (symbol-function 'save-y-or-n-p)))
(defun test1 ()
(interactive)
(if (y-or-n-p "Happy? ")
(insert "Happy day")
(insert "Unhappy day")))