From 80f013e60b6a0330c5f13402ef46b1b4a68ce6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Cadilhac?= Date: Thu, 7 Feb 2019 12:19:28 +0000 Subject: [PATCH 1/6] Introduce ways to bump overdue TODOs today, and warn of forthcoming deadlines. --- lisp/ox-icalendar.el | 99 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/lisp/ox-icalendar.el b/lisp/ox-icalendar.el index 3d6d0b263..2bf2cd89a 100644 --- a/lisp/ox-icalendar.el +++ b/lisp/ox-icalendar.el @@ -119,6 +119,20 @@ This is a list with possibly several symbols in it. Valid symbols are: (const :tag "SCHEDULED in TODO entries become start date" todo-start))) +(defcustom org-icalendar-bump-todos nil + "Non-nil means that pending TODO VEVENTs are bumped to today. +In addition, if non-nil, the number of late days is indicated in the summary." + :group 'org-export-icalendar + :type 'boolean) + +(defcustom org-icalendar-warn-deadlines nil + "Non-nil means that a new VEVENT is created today to warn for deadlines. +This only applies to TODOs that are not done. Relies on +`org-deadline-warning-days' for the number of days during which the warning +is created." + :group 'org-export-icalendar + :type 'boolean) + (defcustom org-icalendar-categories '(local-tags category) "Items that should be entered into the \"categories\" field. @@ -481,6 +495,28 @@ or subject for the event." (concat folded-line "\r\n " (substring line chunk-start)))))) (org-split-string s "\n") "\r\n"))) +(defun org-icalendar-today-timestamp () + "Return a TIMESTAMP object for today, at 00:00." + (let ((dt (decode-time))) + (list 'timestamp + (nconc (list :year-start (nth 5 dt) + :year-end (nth 5 dt) + :month-start (nth 4 dt) + :month-end (nth 4 dt) + :day-start (nth 3 dt) + :day-end (nth 3 dt)))))) + +(defun org-icalendar-days-until-timestamp (timestamp) + "Return the number of days until TIMESTAMP. + +If TIMESTAMP occurs today, return 0. +If TIMESTAMP occurs yesterday, return -1." + (floor + (/ (float-time + (time-subtract (org-timestamp--to-internal-time timestamp) + (apply 'encode-time + (append '(0 0 0) (nthcdr 3 (decode-time)))))) + (* 60 60 24)))) ;;; Filters @@ -567,19 +603,70 @@ inlinetask within the section." ;; "VEVENT" component from scheduled, deadline, or any ;; timestamp in the entry. (let ((deadline (org-element-property :deadline entry))) - (and deadline + (when (and deadline (memq (if todo-type 'event-if-todo 'event-if-not-todo) - org-icalendar-use-deadline) + org-icalendar-use-deadline)) + (let ((days-until-deadline + (org-icalendar-days-until-timestamp deadline))) + (cond + ;; Case 1: Just export the event if... + ((or + ;; Not a pending TODO. + (not (eq todo-type 'todo)) + ;; Is due today. + (= days-until-deadline 0) + ;; Overdue but no bumping. + (and (not org-icalendar-bump-todos) + (< days-until-deadline 0)) + ;; In the future but no warning. + (and (not org-icalendar-warn-deadlines) + (> days-until-deadline 0))) (org-icalendar--vevent entry deadline (concat "DL-" uid) - (concat "DL: " summary) loc desc cat tz class))) + (concat "DL: " summary) loc desc cat tz class)) + + ;; Case 2: pending TODO overdue and should bump. + ((and org-icalendar-bump-todos + (< days-until-deadline 0)) + (org-icalendar--vevent + entry (org-icalendar-today-timestamp) (concat "DL-" uid) + (concat "DL (" (number-to-string (- days-until-deadline)) + "x): " summary) + loc desc cat tz class)) + ;; Case 3: in the future and should warn. + (t + (concat + ;; If in the warning zone. + (when (<= days-until-deadline org-deadline-warning-days) + (org-icalendar--vevent + entry (org-icalendar-today-timestamp) (concat "DL-" uid) + (concat "DL (in " (number-to-string days-until-deadline) + "d.): " summary) + loc desc cat tz class)) + (org-icalendar--vevent + entry deadline (concat "DL-" uid) + (concat "DL: " summary) loc desc cat tz class))))))) (let ((scheduled (org-element-property :scheduled entry))) - (and scheduled + (when (and scheduled (memq (if todo-type 'event-if-todo 'event-if-not-todo) - org-icalendar-use-scheduled) + org-icalendar-use-scheduled)) + (let ((days-until-scheduled + (org-icalendar-days-until-timestamp scheduled))) + (cond + ;; Already done, due in the future or today, or no bumps. + ((or (not (eq todo-type 'todo)) + (>= days-until-scheduled 0) + (not org-icalendar-bump-todos)) (org-icalendar--vevent entry scheduled (concat "SC-" uid) - (concat "S: " summary) loc desc cat tz class))) + (concat "S: " summary) loc desc cat tz class)) + ;; Overdue and should bump. + (t + (org-icalendar--vevent + entry (org-icalendar-today-timestamp) (concat "SC-" uid) + (concat "S (" (number-to-string (- days-until-scheduled)) "x): " + summary) + loc desc cat tz class)))))) ;; When collecting plain timestamps from a headline and its ;; title, skip inlinetasks since collection will happen once ;; ENTRY is one of them. -- 2.19.2