Skip to content

Instantly share code, notes, and snippets.

@Gavinok
Created April 26, 2024 03:39
Show Gist options
  • Save Gavinok/50f804ea5a2856ee52dee1ba02e97cb9 to your computer and use it in GitHub Desktop.
Save Gavinok/50f804ea5a2856ee52dee1ba02e97cb9 to your computer and use it in GitHub Desktop.
Show notes from my Double Your Productivity With Emacs Org-mode video

All together

What is productivity?

Using the oxford dictionary

Productivity
the rate at which a worker, a company or a country produces goods, and the amount produced, compared with how much time, work and money is needed to produce them

So basically we want a way to produce a large amount of work in a small amount of time (forgive me for simplifying)

  • To get more things done in less time you are going to need 2 things
    1. a fast way to do things (emacs solves this)
    2. a way to schedule, plan, and keep track of those things being done
      • This is where Org comes in

Why?

Org Is Forever

  • Getting started is easy
  • While there are at this point thousands of TODO and note programs one that has stood the test of time is ORG-MODE
  • It has continually gained features seen in practically any alternative on the market
  • With it’s infinite extensibility the world is yours
  • While I admit nothing is forever its hard to imagine Emacs and Org-Mode becoming abandoned in my lifetime

A TODO List Isn’t Enough

  • They are great but they can become overwhelming as tasks build up
  • Org Mode provides
    • Timestamps
      • Deadlines
      • Scheduling Time to work on something
      • Plain old events
      • Monitoring progress with time tracking
    • Priority management
      • A, B, C, etc
    • Tagging
      • useful for connecting tasks to projects
    • Effort estimations
      • good for identifying which task to handle with the available time
    • Habit tracking
      • For repeating tasks that may not have a set time or work period
    • And More
  • Org Mode supports timestamps as a first class feature
    • Scheduled events, deadlines, regular timestamps and more

Time management beyond a simple calendar

  • Multiple ways to share calendars that can fit any workflow
  • While a simple calendar in some cases is enough e.g. a dentist visit
  • I want to look at my up coming events and be able to reference other notes
    • Often need more context
  • This has always been my issue with existing TODO apps and calendar apps
  • Few apps try to connect events notes and tasks together
  • This is why I never stuck with Google Keep, Google calendar, or any of the markdown note tools like Obsidian

Example Usecase

  • I have a calendar event for an upcoming product demo
    • I can link to a specific section of a file in a particular commit
      • Change I made
    • I can reference existing notes on this project
      • [[file:Work.org::*\[\[bcgov/vc-authn-oidc#429 pipeline for dev · Issue #429 · bcgov/vc-authn-oidc\]\]][CD pipeline for dev · Issue #429 · bcgov/vc-authn-oidc]]
    • I can leave notes on setup for that project and reuse them for my demo
echo hello
  • I can quickly reference what I did the day before using CLOSED timestamp
    • We need to build the package

Agenda

Setup your agenda files

org-directory

I have a dedicated directory for my main org files

(setq org-directory "~/Documents/org")

org-agenda-files

I personally use every file in this directory for my agenda

(setq org-agenda-files (directory-files-recursively org-directory "\\.org$"))

AKA add all org files in this directory

Finally I bind org agenda to C-c a

(keymap-global-set "C-c a" 'org-agenda)

Lets try it out

C-c a

If there is interest I will dig into my agenda customization.

  • It’s gotten a little crazy recently

Adding New Times

<2024-04-21 Sun> Org mode’s timestamp system is one of my fav features and one of the main reasons I consider it the best tool for time managment

Use regular times for non schedule or deadlines https://orgmode.org/manual/Deadlines-and-Scheduling.html

  • a lot of the time you can simply type the time in a you would say it
  • This is a big pro to using org mode

Inserting timestamps

e.g. C-c . tuesday

e.g. C-c . jan 15 2pm

Example using macros

<2025-01-01 Wed 14:00> <2025-02-03 Mon 01:00-13:00> <2024-04-23 Tue 01:00-13:00> <2024-04-26 Fri 01:00-13:00> <2024-04-26 Fri 13:00-14:00>

Other timestamps

  • Use scheduled for a time to work on something or time you will start working on something C-c C-s
    • I often use this for timeblocking
  • Deadline is for due dates C-c C-d

Capture

Make common capture templates. A few must haves for me are

  • Generic TODO that can capture whatever I am working on
    • For me I make the default have a deadline for today
    • also it will capture the region or a link to what I am looking at
("t" "Todo" entry
 (file (lambda () (concat org-directory "/refile.org")))
 "* TODO %?\nDEADLINE: %T\n %i\n %a")
  • Timeblocking
    • create a timerange to work on something
    • Errands
    • Assignments
    • Etc
("st" "Time Block" entry
 (file+headline (lambda () (concat org-directory "/Work.org"))
                "Time Blocks")
 "* Work On %?\nSCHEDULED: %T\n")
  • Events
    • Create an event for something like a trip
    • capture when it is
    • capture where it is
      • if you import your calendar google calendar can take advantage of this
("se" "Event" entry
 (file+headline (lambda () (concat org-directory "/mylife.org"))
                "Events")
 "* Go to the %?\n%T\n\n:PROPERTIES:\n:LOCATION: %^{location|Anywhere|Home|Work|School}\n:END:")
  • Meeting
    • For when I have a meeting to keep both notes about the meeting and keep track of upcoming meetings
("sm" "Meeting" entry
 (file+headline (lambda () (concat org-directory "/Work.org")) "Meetings")
  "* Meeting with %? :MEETING:\nSCHEDULED: %T\n:PROPERTIES:\n:LOCATION: %^{location|Anywhere|Home|Work|School|FGPC}\n:END:")
  • General knowledge capture. Not scheduling related but figured I should mention it
("k" "Knowledge")
("kc" "Cool Thing" entry
 (file+olp (lambda () (concat org-directory "/archive.org")) "Cool Projects")
 "* %?\nEntered on %U\n  %i\n  %a")
("kk" "Thing I Learned" entry
 (file+olp (lambda () (concat org-directory "/archive.org")) "Knowledge")
 "* %? %^g\nEntered on %U\n  %i\n  %a")
("ki" "Ideas" entry
 (file+olp (lambda () (concat org-directory "/archive.org")) "Ideas")
 "* %?\nEntered on %U\n  %i\n  %a")
  • I have a email specific ones that integrate with mu4e
    • Follow up
      • Schedule time to reply to an email
    • Read later
      • For emails I need dedicated time to go though
    • Both could replaced with time blocking
;; Email Stuff
("e" "Email Workflow")
("ef" "Follow Up" entry
 (file+olp (lambda () (concat org-directory "/Work.org")) "Follow Up")
 "* TODO Follow up with %:fromname on %a\n SCHEDULED:%t\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\n\n%i")
("er" "Read Later" entry
 (file+olp (lambda () (concat org-directory "/Work.org")) "Read Later")
 "* TODO Read %:subject\n SCHEDULED:%t\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\n\n%a\n%i")
(setq org-capture-templates
      '(("t" "Todo" entry (file (lambda () (concat org-directory "/refile.org")))
         "* TODO %?\nDEADLINE: %T\n %i\n %a")
        ("m" "movie")
        ("mt" "movie" entry
         (file+headline (lambda () (concat org-directory "/mylife.org")) "Movies to Watch"))
        ("v" "Video Idea" entry
         (file+olp (lambda () (concat org-directory "/youtube.org"))
                   "YouTube" "Video Ideas")
         "*RESEARCHING%?\n%? %a\n")

        ("g" "Gift")
        ("gs" "Gift For Seren" checkitem
         (file+headline (lambda () (concat org-directory "/archive.org")) "Gifts for Seren")
         nil
         :jump-to-captured t)
        ("gw" "Wish List" checkitem
         (file+headline (lambda () (concat org-directory "/site/wishlist.org")) "Wish list")
         nil
         :jump-to-captured t)

        ("u" "Update Current Clocked Heading" text
         (clock)
         "hello world %?"
         :unnarrowed t)

        ("k" "Knowledge")
        ("kc" "Cool Thing" entry
         (file+olp (lambda () (concat org-directory "/archive.org")) "Cool Projects")
         "* %?\nEntered on %U\n  %i\n  %a")
        ("kk" "Thing I Learned" entry
         (file+olp (lambda () (concat org-directory "/archive.org")) "Knowledge")
         "* %? %^g\nEntered on %U\n  %i\n  %a")
        ("ki" "Ideas" entry
         (file+olp (lambda () (concat org-directory "/archive.org")) "Ideas")
         "* %?\nEntered on %U\n  %i\n  %a")
        ("kT" "Thoughts" entry
         (file+olp (lambda () (concat org-directory "/archive.org")) "Thoughts")
         "* %?\nEntered on %U\n  %i\n  %a")
        ("kw" "Word I learnt" entry
         (file+olp (lambda () (concat org-directory "/archive.org")) "Words")
         "* %?\nEntered on %U\n  %i\n  %a")

        ("s" "Schedule")
        ;; Not sure I even want to have this
        ;; ("sE" "Errand" entry (file (lambda () (concat org-directory "/refile.org")))
        ;;  "* TODO %? :errand\nDEADLINE: %T\n  %a")
        ("sm" "Meeting" entry
         (file+headline (lambda () (concat org-directory "/Work.org")) "Meetings")
         "* Meeting with %? :MEETING:\nSCHEDULED: %T\n:PROPERTIES:\n:LOCATION: %^{location|Anywhere|Home|Work|School|FGPC}\n:END:")
        ("se" "Event" entry
         (file+headline (lambda () (concat org-directory "/mylife.org"))
                        "Events")
         "* Go to the %?\n%T\n\n:PROPERTIES:\n:LOCATION: %^{location|Anywhere|Home|Work|School}\n:END:")
        ("st" "Time Block" entry
         (file+headline (lambda () (concat org-directory "/Work.org"))
                        "Time Blocks")
         "* Work On %?\nSCHEDULED: %T\n")

        ;; Org Protocol
        ;; ("pc" "Cool Thing" entry
        ;;  (file+olp (lambda () (concat org-directory "/archive.org")) "Cool Projects")
        ;;  "* %^{Title}\n\n  %^g\nEntered on %U\n  Source: %u, %c\n\n  %i")
        ("p" "Thing I Learned" entry
         (file+olp (lambda () (concat org-directory "/archive.org")) "Knowledge")
         "* %^{Title}")

        ;; Email Stuff
        ("e" "Email Workflow")
        ("ef" "Follow Up" entry
         (file+olp (lambda () (concat org-directory "/Work.org")) "Follow Up")
         "* TODO Follow up with %:fromname on %a\n SCHEDULED:%t\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\n\n%i")
        ("er" "Read Later" entry
         (file+olp (lambda () (concat org-directory "/Work.org")) "Read Later")
         "* TODO Read %:subject\n SCHEDULED:%t\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\n\n%a\n%i")))

(setopt org-capture-templates-contexts
        ;; I repeat "m" since org mode only supports this format
        '(("e" "e" ((in-mode . "mu4e-view-mode")
                    (in-mode . "mu4e-compose-mode")))))

Integrating with other programs

Export Phone Calendar

  • Calendar support with org export to iCal
(setopt org-icalendar-use-scheduled '(todo-start event-if-not-todo))
(org-icalendar-combine-agenda-files)
(copy-file org-icalendar-combined-agenda-file "/ssh:user@server:/home/gavinfre/public_html/cal.ics" t)
  • ICSx5 on mobile to sync this calendar with mobile

Import From Calenders

If one does not support exporting the way you like give one of the others a go

You can get the public URL for your google calendar like so

#!/bin/sh
# Depends on https://github.com/msherry/ical2org

ERRORFILE=$(mktemp XXX.txt)
trap 'rm $ERRORFILE' EXIT TERM HUP

# Google Calendar
curl -s $(pass gcalics) 2>>$ERRORFILE |
    awk -f ~/.scripts/ical2org.awk > /home/gavinok/Documents/org/gcal.org 2>>$ERRORFILE
# Holidays
curl -s \
     https://calendar.google.com/calendar/embed?src=en.canadian%23holiday%40group.v.calendar.google.com&ctz=America%2FDawson_Creek 2>>$ERRORFILE |
    awk -f ~/.scripts/ical2org.awk > /home/gavinok/Documents/org/holidays.org 2>>$ERRORFILE

ERRORS=$(cat $ERRORFILE)

if [ -z $ERRORS ]
then notify-send "Google Calendar Has Been Added To Notes"
else notify-send "$ERRORS"
fi

Caldav

If you want a proper 2 way sync I recommend using caldav

This basically replaces the previous 2 approaches

You will need a server to be setup.

(use-package org-caldav
  :ensure t
  :custom
  (org-caldav-url "https://nextcloud.org/remote.php/dav/calendars/gavinfreeborn")
  (org-caldav-calendar-id "personal")
  (org-caldav-inbox "~/Documents/org/nextcloud-inbox.org")
  (org-caldav-files org-agenda-files)
  (org-icalendar-timezone "America/Vancouver")
  :commands (org-caldav-sync))

Notifications

How I manage Notifications

There are multiple approaches

As usual for an Emacs user I rolled my own (sorta)

Emacs comes with a built in package for notifying you of upcoming appointments

appt

I hacked on appt to use desktop notifications

In addition I also added support for sending notifications to my phone using

https://docs.ntfy.sh/

(use-package appt
  :demand t
  :defer 5
  :custom
  ((appt-announce-method 'appt-persistant-message-announce)
   (appt-message-warning-time 15)
   (appt-display-duration 360)
   (appt-disp-window-function #'my/appt-display)
   (appt-delete-window-function #'ignore))
  :init
  (defun my/appt-display (min-to-app _new-time appt-msg)
    (require 'notifications)

    ;; Close the last iteration of this notification
    (when-let ((repid (get-last-note appt-msg)))
      (notifications-close-notification repid))

    (let ((title  (pcase min-to-app
                    ("0" "Appointment Is Starting")
                    ("1" "Appointment in 1 Minute")
                    (min (concat " Appointment in " min " Minutes"))))
          (body appt-msg)
          (urgency (if (< (string-to-number min-to-app) 3)
                       'critical
                     'normal)))

      ;; Send a desktop notification for this appointment
      (store-last-note
       (funcall (if  (eq system-type 'android)
                    ;; support for android notifications
                    'android-notifications-notify
                  'notifications-notify)
                :title title
                :body body
                :urgency urgency
                :actions '("Open" "Open this appointment")
                :on-action (lambda (_id _key) (org-agenda 'd)))
       appt-msg)

      ;; Notify me on my phone
      (plz 'post "https://ntfy.sh/my-emacs-notifications"
        :headers `(("Title"  . ,title)
                   ("Priority" . ,(if (eql urgency 'critical)
                                      "high"
                                    "default"))
                   ("Tags" . "calendar")
                   ("Markdown" . "yes"))
        :body body)))
  ;; Check for if a notification needs to be sent
  (appt-activate +1)
  ;; generate appointments from my org agenda
  (org-agenda-to-appt)

  ;; Run when I am idle for a minute at a time
  ;; (usually when looking at my browser)
  (defvar appt-update-org-timer
    ;; wait till emacs is idle for 1 minute before processing org appointments
    (run-with-idle-timer 60 t #'org-agenda-to-appt)
    "Timer used to update appt to the current org events"))

Why did I make this?

It’s flexible and easy to understand.

I don’t need to manage a seperate list of files for my notifications and agenda since org-agenda-to-appt

Conflicts

I no longer use this but for those of you worried about conflicting times there is a great package called org-conflict that you can use to check that a time does not conflict with an existing events

Searching

While not schedule specific but important when you can’t remember the time of an event I use consult-recoll to search for any notes. I use consult’s category cycling to only look through the org results.

alternatively org-agenda supports a full text search however it is synchronous which causes the UI to lockup and it’s pretty slow.

Conclusion

I hope I have made it clear the multitude of different ways you can use org mode to boost your productivity when it comes to task and time management

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