simfish:

Emacs/Learn

Workflow for learning and reviewing information efficiently

cover image

Introduction

This workflow is useful for breaking down study sessions into manageable time chunks using org-pomodoro. Each time chunk can be used to complete a topic. If the material requires a review, tag it with a TODO and schedule review drills using org-drill. Org agenda can be used to manage future review sessions.

Customization

Select where to place drill files

(defcustom learn:default-drill-dirs
  '( "~/Documents/KB" "~/Documents/research/notes")
  "specifies the default org-drill directory"
  :type '(file)
  :group 'org-drill)

Some advanced org-drill settings

(setq     org-drill-maximum-items-per-session 50
          org-drill-maximum-duration 20   ;(minutes)
          org-drill-use-visible-cloze-face-p t
          org-drill-add-random-noise-to-intervals-p t
          org-drill-hint-separator "||"
          org-drill-left-cloze-delimiter "["
          org-drill-right-cloze-delimiter "]"
          org-drill-learn-fraction 0.25
          org-drill-cram-hours 2
          org-drill-leech-method 'warn)

Key Bindings

  (kb::def :states 'normal
    "o"  '(:ignore t "Org")
    "od" '(learn::drills :which-key "start drills")
    "op" '(org-pomodoro  :which-key "pomodoro"))

Functions

Learning Drills: use org-drill to find and run drills. This involves two steps. First, list of org files containing drills are generated using async search

(defun learn::find-drills-async ( paths callback)
  (require 'async)
  (async-start
   ;;search command
   `(lambda ( )
      (require 'dash)
      (require 's)
      (require 'cl-lib)
      (cl-loop
       for path in ',paths
       append (delete
               ""
               (-non-nil
                (s-lines
                 (shell-command-to-string
                  (format "ag --org -l -U  \":drill:\" %s"
                          path)))))))

   ;; result callback
   callback))

Once the drill list is populated, org-drill can be run using:

(defun learn::drills ( )
  (interactive)
  (require 'gix-log)
  (require 'dash)
  (require 's)
  (require 'cl)
  (require 'org-drill)

  (let* ((drill-dirs      learn:default-drill-dirs))
    (log:info "scaning for org files...")
    (cond ( ;;found search dirs?
           (not (null drill-dirs))
           ;; summarize search directories
           (cl-loop
            for dir in drill-dirs
            do (log:sub-info "drills directory: %s" dir))
           ;; do search
           (learn::find-drills-async
            drill-dirs
            (lambda (result)
              (require 'org-drill)
              (setq org-drill-scope result)
              (cond (;; is scope valid?
                     (and (not (null org-drill-scope))
                          (not (zerop (length org-drill-scope))))
                     (log:sub-info "scope: %s\n" org-drill-scope)
                     (org-drill))
                    (t ;; invalid scope
                     (log:info "There are no drills found for now!"))))))
          (t
           (log:info      "No search path specified: <%s>" search-dirs)
           (log:sub-info  "Try using org-drill within project directory")
           (log:sub-info  "Also, try customizing `learn:default-drill-dirs'")))))

Pomodoro Tasks how can TODO task states be tracked efficiently? State of a TODO task can change for two reasons

  • Manually when the user changes the state, for example via Tab cycling
  • When a pomodoro timer starts or finishes a break

The first case can easily be addressed by using org-todo-working-hook, org-todo-paused-hook, and help:org-todo-done-hook hooks.

For the later case, org-pomodoro comes with a set of hooks that run when a pomodoro is, started paused, resumed, finished, and killed.

(defvar learn::pomodoro-count   nil)
(defvar learn::pomodoro-ignore-state-changed nil)

(defun learn::on-todo-state-working ( )
  "Start or resume org-pomodoro"
  (require 'org-pomodoro)
  (setq learn::pomodoro-count org-pomodoro-count)
  (unless  learn::pomodoro-ignore-state-changed
    (cond (;; is pomodoro started yet?
           (eq org-pomodoro-state :none)
           (org-pomodoro))
          (;; is pomodoro on short break?
           (eq org-pomodoro-state :break)
           (let ((org-pomodoro-keep-killed-pomodoro-time t))
             (org-pomodoro-killed)))
          (t ;; default do notthing
           ))))


(defun learn::on-todo-state-paused ( )
  "Pause active pomodoro(if any)"
  (setq learn::pomodoro-count org-pomodoro-count)
  (unless learn::pomodoro-ignore-state-changed
    (cond (;; any pomodoro running?
           (eq org-pomodoro-state :pomodoro)
           (let ((org-pomodoro-keep-killed-pomodoro-time t))
             (org-pomodoro-killed)))
          (;; any break in progress?
           (or (eq org-pomodoro-state :break)
               (eq org-pomodoro-state :long-break))
           (org-pomodoro-killed)))))


(defun learn::on-todo-state-done ( )
  "Stops any pomodoro activity."
  (setq learn::pomodoro-count org-pomodoro-count)
  (unless  learn::pomodoro-ignore-state-changed
    (when (org-pomodoro-active-p)
      (org-pomodoro-killed))))



(defun learn::pomodoro/on-break-finished ( )
  "Update task state to WORKING and resume pomodoro."
  (setq learn::pomodoro-count org-pomodoro-count)
  (let ((learn::pomodoro-ignore-state-changed t)
        (m (car org-clock-history)))
    (save-window-excursion
      (pop-to-buffer-same-window (marker-buffer m))
      (if (or (< m (point-min)) (> m (point-max))) (widen))
      (goto-char m)
      (cond ((yes-or-no-p "Ready for another pomodoro session?")
             (org-todo "TODO")
             (org-show-entry)
             (org-pomodoro))
            (t
             (org-todo "TODO")
             (org-show-entry))))))


(defun learn::pomodoro/on-finished ( )
  (setq learn::pomodoro-count org-pomodoro-count)
  (save-window-excursion
    (let (
          (m (car org-clock-history))
          (learn::pomodoro-ignore-state-changed t)
          )
      (pop-to-buffer-same-window (marker-buffer m))
      (when (or
             (< m (point-min))
             (> m (point-max)))
        (widen))
      (goto-char m)
      (org-todo "TODO")
      (org-show-entry))))


(defun learn::pomodoro/on-started ( )
  (setq learn::pomodoro-count org-pomodoro-count)
  (save-window-excursion
    (let ((m org-clock-marker)
          (learn::pomodoro-ignore-state-changed t))
      (pop-to-buffer-same-window (marker-buffer m))
      (when (or
             (< m (point-min))
             (> m (point-max)))
        (widen))
      (goto-char m)
      (org-todo "WORKING")
      (org-show-entry))))

Packages

Package: org-drill

  (use-package org-drill
    :guix (:name emacs-org-drill)
    :init
    <<org-drill-init>>
    :config
    <<org-drill-config>>)

Package: pomodoro

(use-package org-pomodoro
  :guix (:name emacs-org-pomodoro)
  :demand t
  :commands
  org-pomodoro
  :hook
  (org-todo-working            .  learn::on-todo-state-working)
  (org-todo-paused             .  learn::on-todo-state-paused)
  (org-todo-done               .  learn::on-todo-state-done)
  (org-pomodoro-finished       .  learn::pomodoro/on-finished)
  (org-pomodoro-break-finished .  learn::pomodoro/on-break-finished)
  (org-pomodoro-started        .  learn::pomodoro/on-started)
  :custom
  (org-pomodoro-ask-upon-killing nil)
  (org-pomodoro-format "%s") ;;     
  (org-pomodoro-short-break-format "%s")
  (org-pomodoro-long-break-format  "%s")
  (org-pomodoro-play-sounds t)
  :custom-face
  (org-pomodoro-mode-line ((t (:foreground "#E9967a"))))
  (org-pomodoro-mode-line-break   ((t (:foreground "#90ee90")))))

Package: sox

(use-package sox
  :guix (:name sox)
  :disabled)