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:

  1. At the start of a catch-up I run TJ/create-catchup-note
  2. 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.
  3. It presents me with a list of names I can choose from.
  4. Selecting a name creates a new note for the current catch-up meeting.
  5. A link is placed in the current catch-up notes back to the main note.
  6. 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.

  1. Template for new catch-up notes
  2. 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.")))