问题
What is the best way, please, to test for the existence of a specific todo state and a deadline that is not equal to today?
org-state
is a "[h]ook which is run after the state of a TODO item was changed". So unless I'm actually changing the state of a todo, I don't think I can use (string-equal org-state "Next Action")
. Each of my string-equal
lines of code listed below are rejected: Symbol's value as variable is void:
org-todo
and org-deadline
. The deadline is a line that appears underneath the todo state, so that may also pose a problem when testing for the existence of both conditions.
(defun if-next-action-not-today-then-promote ()
(interactive)
(goto-char (point-min))
(while
(re-search-forward "^\*\* Next Action" nil t)
(when (and (string-equal org-todo "Next Action") (not (string-equal org-deadline "<%<%Y-%m-%d %a>>")) )
(message "You have satisfied the two conditions . . . proceeding.")
(org-todo "Active") ;; change state to active
(org-deadline nil "<%<%Y-%m-%d %a>>") ;; change deadline to today
)
)
)
Sample *.org configuration file.
* TASKS
** Active [#A] First task due today. :lawlist:
DEADLINE: <2013-07-11 Thu >
** Active [#A] Second task due today. :lawlist:
DEADLINE: <2013-07-11 Thu >
** Next Action [#E] Test One -- make Active with deadline today. :lawlist:
DEADLINE: <2013-07-31 Wed >
** Next Action [#E] Test Two -- make Active with deadline today. :lawlist:
DEADLINE: <2013-07-31 Wed >
EDIT: The following is a modification of the function proposed by Jonathan Leech-Pepin in his answer further down below. In the first draft of the answer, I was unable to obtain anything but nil
from the variable deadline
due to a problem with (org-element-timestamp-interpreter . . . nil)
-- completely eliminating that reference appears to correct the issue. I set up messages along the way so that I could better understand what was happening. I used if
instead of unless
because I wanted an else
message to let me know that the conditions were not met but that the function was nevertheless working correctly. I've run several tests, including wrapping it into an re-search-forward
type function and it works very nicely -- a wrapped function can be done several ways, but that is beyond the scope of this thread -- e.g., (&optional from-state to-state)
and (interactive)
and then (setq . . .)
further down; or (from-state to-state)
and (interactive (list (setq . . .)
.
(defun zin/org-test-deadline (from-state to-state)
"Change headline from FROM-STATE to TO-STATE if the deadline is not already set to today."
(interactive "sChange from state: \nsChange to state: ")
(unless (org-at-heading-p)
(org-back-to-heading))
(let* (
(element (org-element-at-point))
(todo-state (org-element-property :todo-keyword element))
;; "ignore-errors" avoids throwing an error message if there is no deadline.
(deadline
(ignore-errors
(time-to-days
(org-time-string-to-time
(org-element-property :deadline element) ))))
(today (time-to-days (current-time))) )
(message "This is the element variable: %s" element)
(message "This is the today variable: %s" today)
(message "This is the deadline variable: %s" deadline)
(message "This is the todo-state variable: %s" todo-state)
(message "This is the from-state variable: %s" from-state)
(message "This is the to-state variable: %s" to-state)
(if (not (eq today deadline))
(message "The deadline is not today.")
(message "Today is the deadline."))
(if (and
(not (eq today deadline)) ;; condition -- deadline not equal to today
(string= todo-state from-state) ) ;; condition -- todo-state equals from-state
(progn ;; Process following list if conditions were met.
(org-todo to-state)
(org-deadline nil ".")
(message "The conditions were met, so we did everything that was required.") )
(message "The conditions were not met, so nothing has been done."))
))
回答1:
As long as you are using Org 7.9.2
or greater the following should work. I tested it as well as I could by adjusting deadlines and todo states.
(defun zin/org-test-deadline (from-state to-state)
"Change headline from FROM-STATE to TO-STATE if the deadline is
not already set to today."
(interactive "sChange from state: \nsChange to state: ")
(unless (org-at-heading-p)
(org-back-to-heading))
(let* ((element (org-element-at-point))
(todo-state (org-element-property :todo-keyword
element))
;; Do not give an error if there is no deadline
(deadline (ignore-errors
(time-to-days
(org-time-string-to-time
(org-element-timestamp-interpreter
(org-element-property :deadline
element) nil)))))
(today (time-to-days (current-time))))
(unless (or
;; Is there a deadline
(not deadline)
;; Is the deadline today
(eq today deadline)
;; Is the todo state the wrong one
(not (string= todo-state from-state)))
(org-todo to-state)
(org-deadline nil "."))))
You can then bind this or wrap it in a function that defines from-state
and to-state
. It will also prompt for the two states if called interactively.
来源:https://stackoverflow.com/questions/17602498/how-to-test-for-org-todo-state-xyz-with-deadline-not-equal-to-today