¶News
Reminder about the EmacsConf 2024 Call for Participation:
The conference will be December 7 and 8 this year:
https://emacsconf.org/2024/cfp/
If you have an Emacs-related topic you’re excited about, consider submitting a proposal!
¶Let’s Hack on Org Mode Files!
This week I made a post on socials asking Emacs users how much their life would be improved by understanding how to write Emacs Lisp code at an intermediate level:
https://fosstodon.org/@daviwil/112756516386132987
This post generated quite a lot of responses and discussion! One recurring theme I saw was the desire to automate Org Mode tasks with Emacs Lisp.
Let’s learn a bit about how we might do that by hacking on a few useful tasks:
- Looping over all TODO items in a file
- Refiling all DONE tasks to another file
- Sorting all TODO items under a heading based on their task state
- Scraping Org Agenda items to extract details
- Exporting clocked task data to another format
- What else?
¶Useful Functions
org-map-entries
: Loops over all headings in an org document, including filtering, etcorg-entry-get
: Gets the value of a property of a given entry
¶Existing Code for Agenda Scraping
(defun dw/get-schedule-entries () "Get all daily agenda entries with the category 'Schedule'." (let ((entries '())) (save-window-excursion (org-agenda nil "a") (goto-char (point-min)) (while (org-agenda-next-item 1) (when (string= (get-text-property (point) 'org-category) "Schedule") (push entry entries)))) entries))
Extracting details about the agenda item at point:
(get-text-property (point) 'time) ;; 14:00-16:00 (get-text-property (point) 'time-of-day) ;; 1400 (get-text-property (point) 'duration) ;; 120.0 or nil if no range (let* ((time-of-day (get-text-property (point) 'time-of-day)) (hour (/ time-of-day 100)) (min (% time-of-day 100)) (current-time (decode-time)) (current-hour (nth 2 current-time)) (current-min (nth 1 current-time))) ;; do comparison here )
¶The final code
;; -*- lexical-binding: t; -*- ;; (with-current-buffer "Tasks.org" ;; (org-map-entries (lambda () ;; (org-entry-get nil "TODO")) ;; "+TODO=\"DONE\"")) (defun my/refile-heading-to-file-heading (file heading) (let ((pos (save-excursion (find-file-noselect file) (org-find-exact-headline-in-buffer heading)))) (org-refile nil nil (list heading file nil pos)))) (defun my/refile-done-tasks-to-archive () (interactive) (let ((archive-file-name (format "%s_archive.org" (file-name-sans-extension (buffer-file-name))))) (org-map-entries (lambda () (my/refile-heading-to-file-heading archive-file-name "Archived Tasks")) "+TODO=\"DONE\""))) (defun my/org-move-done-tasks-to-bottom () "Sort all tasks in the topmost heading by TODO state." (interactive) (save-excursion (while (org-up-heading-safe)) (org-sort-entries nil ?o)) ;; Reset the view of TODO items (org-overview) (org-show-entry) (org-show-children))