about summary refs log tree commit diff
path: root/emacs/.emacs.d/wpc/todo.el
diff options
context:
space:
mode:
Diffstat (limited to 'emacs/.emacs.d/wpc/todo.el')
-rw-r--r--emacs/.emacs.d/wpc/todo.el293
1 files changed, 293 insertions, 0 deletions
diff --git a/emacs/.emacs.d/wpc/todo.el b/emacs/.emacs.d/wpc/todo.el
new file mode 100644
index 000000000000..236912c086fd
--- /dev/null
+++ b/emacs/.emacs.d/wpc/todo.el
@@ -0,0 +1,293 @@
+;;; 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