Moving all of my Emacs-related files into their own directory at the root of this repository.
		
			
				
	
	
		
			293 lines
		
	
	
	
		
			8.7 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
	
		
			8.7 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
;;; todo.el --- Bespoke task management system -*- lexical-binding: t -*-
 | 
						|
;; Author: William Carroll <wpcarro@gmail.com>
 | 
						|
 | 
						|
;;; Commentary:
 | 
						|
;; Marriage of my personal task-management system, which I've been using for 18
 | 
						|
;; months and is a mixture of handwritten notes, iOS notes, and org-mode files,
 | 
						|
;; with Emacs's famous `org-mode'.
 | 
						|
;;
 | 
						|
;; For me, I'd like a live, reactive state management system.  I'd like
 | 
						|
;; `org-mode' to be a nice way of rendering my TODOs, but I think the
 | 
						|
;; relationship with `org-mode' ends there.
 | 
						|
;;
 | 
						|
;; Intended to supplement my org-mode workflow.
 | 
						|
;;
 | 
						|
;; Wish-list:
 | 
						|
;; - Daily emails for standups
 | 
						|
;; - Templates for commonly occurring tasks
 | 
						|
 | 
						|
;; Dependencies
 | 
						|
(require 'dash)
 | 
						|
(require 'f)
 | 
						|
(require 'macros)
 | 
						|
 | 
						|
;;; Code:
 | 
						|
 | 
						|
;; TODO: Classify habits as 'daily, 'weekly, 'monthly, 'yearly, 'event-driven
 | 
						|
 | 
						|
;; TODO: Consider serving these values up to a React webpage in Chrome.
 | 
						|
 | 
						|
;; TODO: Classify meetings as either 'recurrent or 'ad-hoc.
 | 
						|
 | 
						|
;; TODO: Support sorting by `type'.
 | 
						|
 | 
						|
;; TODO: Support work-queue idea for "Tomorrow's todos."
 | 
						|
 | 
						|
;; TODO: Support macro to generate all possible predicates for todo types.
 | 
						|
 | 
						|
;; TODO: Support export to org-mode file
 | 
						|
 | 
						|
;; TODO: Support generic way to quickly render a list
 | 
						|
 | 
						|
(defcustom todo/install-kbds? t
 | 
						|
  "When t, install the keybindings.")
 | 
						|
 | 
						|
;; TODO: Add documentation.
 | 
						|
(cl-defstruct todo type label)
 | 
						|
 | 
						|
;; TODO: Consider keeping this in Dropbox.
 | 
						|
;; TODO: Support whether or not the todo is done.
 | 
						|
(defconst todo/org-file-path "~/Dropbox/org/today.org")
 | 
						|
 | 
						|
;; TODO: Support remaining function for each type.
 | 
						|
;; TODO: Support completed function for each type.
 | 
						|
 | 
						|
(defun todo/completed? (x)
 | 
						|
  "Return t is `X' is marked complete."
 | 
						|
  (todo-complete x))
 | 
						|
 | 
						|
;; TODO: Prefer `new-{task,habit,meeting}'.
 | 
						|
 | 
						|
(defun todo/completed (xs)
 | 
						|
  "Return the todo items in `XS' that are marked complete."
 | 
						|
  (->> xs
 | 
						|
       (-filter #'todo/completed?)))
 | 
						|
 | 
						|
(defun todo/remaining (xs)
 | 
						|
  "Return the todo items in `XS' that are not marked complete."
 | 
						|
  (->> xs
 | 
						|
       (-reject #'todo/completed?)))
 | 
						|
 | 
						|
(defun todo/task (label)
 | 
						|
  "Convenience function for creating a task todo with `LABEL'."
 | 
						|
  (make-todo
 | 
						|
   :type 'task
 | 
						|
   :label label))
 | 
						|
 | 
						|
(defun todo/meeting (label)
 | 
						|
  "Convenience function for creating a meeting todo with `LABEL'."
 | 
						|
  (make-todo
 | 
						|
   :type 'meeting
 | 
						|
   :label label))
 | 
						|
 | 
						|
(defun todo/habit (label)
 | 
						|
  "Convenience function for creating a habit todo with `LABEL'."
 | 
						|
  (make-todo
 | 
						|
   :type 'habit
 | 
						|
   :label label))
 | 
						|
 | 
						|
(defun todo/task? (x)
 | 
						|
  "Return t if `X' is a task."
 | 
						|
  (equal 'task (todo-type x)))
 | 
						|
 | 
						|
(defun todo/habit? (x)
 | 
						|
  "Return t if `X' is a habit."
 | 
						|
  (equal 'habit (todo-type x)))
 | 
						|
 | 
						|
(defun todo/meeting? (x)
 | 
						|
  "Return t if `X' is a meeting."
 | 
						|
  (equal 'meeting (todo-type x)))
 | 
						|
 | 
						|
(defun todo/label (x)
 | 
						|
  "Return the label of `X'."
 | 
						|
  (todo-label x))
 | 
						|
 | 
						|
;; TODO: Support moving todos between todo/{today,tomorrow}.
 | 
						|
;; TODO: Consider modelling todo/{today,tomorrow} as queues instead of lists so that I can
 | 
						|
;; append cheaply.
 | 
						|
 | 
						|
;; TODO: Find an Elisp date library.
 | 
						|
 | 
						|
;; TODO: type-driven development of this habit tree.
 | 
						|
;; TODO: Create this tree on a whiteboard first.
 | 
						|
;; (defconst todo/habits
 | 
						|
;;   '(:beginning-of-month
 | 
						|
;;     '("Create habit template for current month"
 | 
						|
;;       "Post mortem of previous month")
 | 
						|
;;     :monday    '("Jiu Jitsu")
 | 
						|
;;     :tuesday   '("Jiu Jitsu")
 | 
						|
;;     :wednesday '("Jiu Jitsu")
 | 
						|
;;     :thursday  '("Salsa class")
 | 
						|
;;     :friday    '("Jiu Jitsu")
 | 
						|
;;     :saturday  '("Borough market")
 | 
						|
;;     :sunday    '("Shave")
 | 
						|
;;     :weekday '(:arrive-at-work
 | 
						|
;;                '("Breakfast"
 | 
						|
;;                  "Coffee"
 | 
						|
;;                  "Placeholder")
 | 
						|
;;                :before-lunch
 | 
						|
;;                '("Lock laptop"
 | 
						|
;;                  "Placeholder")
 | 
						|
;;                :home->work
 | 
						|
;;                '("Michel Thomas Italian lessons"))
 | 
						|
;;     :daily '(:morning
 | 
						|
;;              '("Meditate"
 | 
						|
;;                "Stretch")
 | 
						|
;;              :)))
 | 
						|
 | 
						|
;; overlay weekday with specific weekdays (e.g. BJJ is only on M,T,W)
 | 
						|
 | 
						|
;; TODO: Extend the record type to support duration estimations for AFK, K
 | 
						|
;; calculations.
 | 
						|
 | 
						|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
						|
;; Habits
 | 
						|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
						|
 | 
						|
;; TODO: Should I be writing this in ReasonML and Haskell?
 | 
						|
 | 
						|
(defconst todo/monthly-habit-challenge
 | 
						|
  "InterviewCake.com"
 | 
						|
  "The monthly habit challenge I do for fifteen minutes each day.")
 | 
						|
 | 
						|
(defconst todo/daily-habits
 | 
						|
  (->> (list "Meditate"
 | 
						|
             todo/monthly-habit-challenge)
 | 
						|
       (-map #'todo/habit)))
 | 
						|
 | 
						|
(defconst todo/first-of-the-month-stack
 | 
						|
  '("Create habit template for current month"
 | 
						|
    "Reserve two dinners in London for dates"
 | 
						|
    "Post mortem of previous month"
 | 
						|
    "Create monthly financial budget in Google Sheets")
 | 
						|
  "A stack of habits that I do at the beginning of each month.")
 | 
						|
 | 
						|
(defconst todo/adhoc-habits
 | 
						|
  (->> (list/concat
 | 
						|
        todo/first-of-the-month-stack)
 | 
						|
       (-map #'todo/habit))
 | 
						|
  "Habits that I have no classification for at the moment.")
 | 
						|
 | 
						|
;; TODO: Model this as a function.
 | 
						|
(defconst todo/habits
 | 
						|
  (list/concat todo/daily-habits
 | 
						|
               todo/adhoc-habits)
 | 
						|
  "My habits for today.")
 | 
						|
 | 
						|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
						|
;; Meetings
 | 
						|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
						|
 | 
						|
;; TODO: Define "meeting".
 | 
						|
 | 
						|
(defconst todo/daily-meetings
 | 
						|
  (->> '("Standup"
 | 
						|
         "Lunch")
 | 
						|
       (-map #'todo/meeting))
 | 
						|
  "Daily, recurrent meetings.")
 | 
						|
 | 
						|
 | 
						|
(defconst todo/day-of-week-meetings
 | 
						|
  '(:Monday    '("Lunch")
 | 
						|
    :Tuesday   '("Lunch")
 | 
						|
    :Wednesday '("Team Lunch")
 | 
						|
    :Thursday  '("Lunch")
 | 
						|
    :Friday    '("Lunch")
 | 
						|
    :Satuday   '()
 | 
						|
    :Sunday    '())
 | 
						|
  "Meetings that occur depending on the current day of the week.")
 | 
						|
 | 
						|
(parse-time-string "today")
 | 
						|
 | 
						|
;; TODO: Support recurrent, non-daily meetings.
 | 
						|
 | 
						|
(defconst todo/adhoc-meetings
 | 
						|
  (->> '("WSE Weekly Standup"
 | 
						|
         "Team Lunch"
 | 
						|
         "Karisa Explains It All")
 | 
						|
       (-map #'todo/meeting))
 | 
						|
  "Non-recurrent meetings.")
 | 
						|
 | 
						|
(defconst todo/meetings
 | 
						|
  (list/concat todo/daily-meetings
 | 
						|
               todo/adhoc-meetings)
 | 
						|
  "My meetings for today.")
 | 
						|
 | 
						|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
						|
;; Tasks
 | 
						|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
						|
 | 
						|
(defconst todo/tasks
 | 
						|
  (->> '("GetEmailCase"
 | 
						|
         "Async node workflow"
 | 
						|
         "Support C-c in EXWM"
 | 
						|
         "Post-its for bathroom mirror"
 | 
						|
         "Visit AtomicHabit.com/scorecard"
 | 
						|
         "Visit AtomicHabit.com/habitstacking"
 | 
						|
         "Create GraphViz for Carpe Diem cirriculum"
 | 
						|
         "Create CitC client for local browsing of CE codebase"
 | 
						|
         "Respond to SRE emails")
 | 
						|
       (-map #'todo/task)))
 | 
						|
 | 
						|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
						|
;; Work queues (today, tomorrow, someday)
 | 
						|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
						|
 | 
						|
;; TODO: Generate standup documents from DONE items in the state.
 | 
						|
 | 
						|
;; TODO: Learn how to create a gen-server style of live, reactive state.
 | 
						|
;; TODO: This should probably be `defconst' and a reference to the live state.
 | 
						|
(defconst todo/today
 | 
						|
  (list/concat
 | 
						|
   todo/habits
 | 
						|
   todo/meetings
 | 
						|
   todo/tasks))
 | 
						|
 | 
						|
(defconst todo/tomorrow
 | 
						|
  '())
 | 
						|
 | 
						|
(defconst todo/someday
 | 
						|
  '())
 | 
						|
 | 
						|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
						|
;; View functions
 | 
						|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
						|
 | 
						|
(defun todo/to-org (xs)
 | 
						|
  "Map `XS' into a string with `org-mode' syntax."
 | 
						|
  ;; TODO: Create function to DRY this code up.
 | 
						|
  (let ((meetings (->> xs
 | 
						|
                       (-filter #'todo/meeting?)
 | 
						|
                       (-map (lambda (x)
 | 
						|
                               (s-concat "** TODO " (todo/label x))))
 | 
						|
                       (s-join "\n")))
 | 
						|
        (tasks (->> xs
 | 
						|
                    (-filter #'todo/task?)
 | 
						|
                    (-map (lambda (x)
 | 
						|
                            (s-concat "** TODO " (todo/label x))))
 | 
						|
                    (s-join "\n")))
 | 
						|
        (habits (->> xs
 | 
						|
                     (-filter #'todo/habit?)
 | 
						|
                     (-map (lambda (x)
 | 
						|
                             (s-concat "** TODO " (todo/label x))))
 | 
						|
                     (s-join "\n"))))
 | 
						|
    (s-join "\n" (list
 | 
						|
                  (s-concat "* Meetings\n" meetings)
 | 
						|
                  (s-concat "* Tasks\n" tasks)
 | 
						|
                  (s-concat "* Habits\n" habits)))))
 | 
						|
 | 
						|
(defun todo/export-to-org (xs)
 | 
						|
  "Export `XS' to `todo/org-file-path'."
 | 
						|
  (f-write-text (->> xs
 | 
						|
                     todo/to-org)
 | 
						|
                'utf-8
 | 
						|
                todo/org-file-path))
 | 
						|
 | 
						|
(defun todo/orgify-today ()
 | 
						|
  "Exports today's todos to an org file."
 | 
						|
  (interactive)
 | 
						|
  (todo/export-to-org todo/today)
 | 
						|
  (alert (string/concat  "Exported today's TODOs to: " todo/org-file-path)))
 | 
						|
 | 
						|
(provide 'todo)
 | 
						|
;;; todo.el ends here
 |