Motivation
A key task at the moment is individual 1-to-1s/catch-ups. These come around quick, and I often don’t have a huge amount of time to deal set them up.
I have stared to tag things in Emacs with peoples name so that I can run queries against my agenda and pull out all tasks that I’ve tagged for someone - a great way to get info to the right people.
What I’m missing, however, is a quick way to log notes during these calls.
Proposed flow
I want the below flow to kick off:
- At the start of a catch-up I run
TJ/create-catchup-note
- The function looks for all lvl 1 headings in a master
.org
file - pulling out the names of all those who I have catch-ups with. - It presents me with a list of names I can choose from.
- Selecting a name creates a new note for the current catch-up meeting.
- A link is placed in the current catch-up notes back to the main note.
- A link to the current catch-up note is time stamped and added into the main note file.
Let’s get it done.
Code
First let’s get our master catch-up file sorted. This is an .org
file with each lvl 1 heading being the name of someone I have catch-ups with
(defvar my/catchup-file "~/path/to/catch-up-file.org"
"Path to the central catch-up file.")
The file should be structured like this:
* Name 1
* Name 2
* Name 3
Here we now iterate over the lvl 1 headings and pull the names out
(defun TJ/get-catchup-names ()
"Extract level 1 headings (names) from the catch-up file."
(with-current-buffer (find-file-noselect TJ/catchup-file)
(org-element-map (org-element-parse-buffer) 'headline
(lambda (hl)
(when (= (org-element-property :level hl) 1)
(org-element-property :raw-value hl)))))
This function helps insert the link back once the note is created, along with a time stamp, it is called by the main function TJ/create-catchup-note
(defun TJ/insert-catchup-link (catchup-file name note-file note-title)
"Insert a link to the new note under the selected NAME in CATCHUP-FILE, with a timestamp."
(let ((timestamp (format-time-string "[%Y-%m-%d]"))) ;; Org-style timestamp
(with-current-buffer (find-file-noselect catchup-file)
(goto-char (point-min))
(if (search-forward (format "* %s" name) nil t)
(progn
(end-of-line)
(insert (format "\n- %s [[file:%s][%s]]" timestamp note-file note-title))) ;; Insert timestamp + link
(message "Name '%s' not found in the file." name))
(save-buffer))))
Finally, we pull it all together, adding interactive selection of names and calling the above functions.
(defun TJ/create-catchup-note ()
"Create a Denote catch-up note and link it in both directions with the catch-up file."
(interactive)
(let* ((names (TJ/get-catchup-names))
(name (completing-read "Select name: " names nil t))
(title (format "%s_catchup" name))
(note-file (denote title '("catchup") "txt")) ;; Create the Denote note
(note-link (format "[[file:%s][%s]]" note-file title))
(catchup-link (format "[[file:%s][Catch-ups]]" TJ/catchup-file)))
;; Insert link with timestamp into the catch-up file
(TJ/insert-catchup-link TJ/catchup-file name note-file title)
;; Insert a backlink to the catch-up file in the new note
(with-current-buffer (find-file-noselect note-file)
(goto-char (point-max))
(insert (format "\n\n** Back to Catch-ups\n%s" catchup-link))
(save-buffer))
(message "Catch-up note created and linked successfully with timestamp.")))
Next steps
There you go, super simple but should help with the admin of meetings. Key next steps are below - this would round out all I want this function to do.
- Template for new catch-up notes
- Automatically search for
TODO
items that are tagged with that person
Just the code
All together it looks like this:
(defvar TJ/catchup-file "~/path/to/catchup/master.org"
"Path to the central catch-up file.")
(defun TJ/get-catchup-names ()
"Extract level 1 headings (names) from the catch-up file."
(with-current-buffer (find-file-noselect TJ/catchup-file)
(org-element-map (org-element-parse-buffer) 'headline
(lambda (hl)
(when (= (org-element-property :level hl) 1)
(org-element-property :raw-value hl))))))
(defun TJ/insert-catchup-link (catchup-file name note-file note-title)
"Insert a link to the new note under the selected NAME in CATCHUP-FILE, with a timestamp."
(let ((timestamp (format-time-string "[%Y-%m-%d]"))) ;; Org-style timestamp
(with-current-buffer (find-file-noselect catchup-file)
(goto-char (point-min))
(if (search-forward (format "* %s" name) nil t)
(progn
(end-of-line)
(insert (format "\n- %s [[file:%s][%s]]" timestamp note-file note-title))) ;; Insert timestamp + link
(message "Name '%s' not found in the file." name))
(save-buffer))))
(defun TJ/create-catchup-note ()
"Create a Denote catch-up note and link it in both directions with the catch-up file."
(interactive)
(let* ((names (TJ/get-catchup-names))
(name (completing-read "Select name: " names nil t))
(title (format "%s_catchup" name))
(note-file (denote title '("catchup") "txt")) ;; Create the Denote note
(note-link (format "[[file:%s][%s]]" note-file title))
(catchup-link (format "[[file:%s][Catch-ups]]" TJ/catchup-file)))
;; Insert link with timestamp into the catch-up file
(TJ/insert-catchup-link TJ/catchup-file name note-file title)
;; Insert a backlink to the catch-up file in the new note
(with-current-buffer (find-file-noselect note-file)
(goto-char (point-max))
(insert (format "\n\n** Back to Catch-ups\n%s" catchup-link))
(save-buffer))
(message "Catch-up note created and linked successfully with timestamp.")))