Skip to content

Instantly share code, notes, and snippets.

@nonducor
Forked from ironchicken/clocktable-by-tag.el
Last active December 18, 2023 20:59
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save nonducor/004b903670c83d88176ab2714e21b598 to your computer and use it in GitHub Desktop.
Save nonducor/004b903670c83d88176ab2714e21b598 to your computer and use it in GitHub Desktop.
Emacs org-mode dynamic block similar to clocktable, but grouped by tag. See: https://stackoverflow.com/questions/70568361/org-mode-review-clocked-time-by-multiple-tags
(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)))
@shamiv
Copy link

shamiv commented Dec 21, 2022

Thanks for great this addition. Would it be possible/easy to allow for (some of) the customization that are posssible in the default clocktable (like ":scope filetree")?

@jschaeff
Copy link

This function is super handy. Thank you !

Would it be possible/easy to allow for (some of) the customization that are posssible in the default clocktable (like ":scope filetree")?

Yes, it's possible, just add it in your dynamic bloc. ie:

#+BEGIN: clocktable-by-tag :tags ("B" "A") :maxlevel 2 :block today :scope agenda

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment