Org mode capture - emacs

I'm trying to make a 'class' template for org-capture.
What I want is to easily make this kind of entry:
* TODO <course>: Week <week> Lecture <number>
SCHEDULED: %^T
** TODO prepare for class: <course>-<week>-<number>
SCHEDULED: <two days before T> DEADLINE: <one day before T>
** TODO review class: <course>-<week>-<number>
SCHEDULED: <one day after T> DEADLINE: <two days after T>
Currently, I have this template.
(setq org-capture-templates
'(
("c" "Class" entry (file "~/sydbox/personal/workflow/class.txt")
"* TODO %^{Course}: Week %^{Week} Lecture %^{Number}\n SCHEDULED: %(org-insert-time-stamp (org-read-date nil t nil nil nil \" \"))\n location: %^{location} %?\n** TODO %\\1: prepare lecture %\\3 from week %\\2\n DEADLINE: %(org-insert-time-stamp (org-read-date nil t \"-1d\")) SCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"-2d\"))\n** TODO %\\1: review lecture %\\3 from week %\\2\n DEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\")) SCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+1d\"))\n")
("e" "Exercise session" entry (file "~/sydbox/personal/workflow/class.txt")
))
However, now I have no idea how to input the dates. The date and time of the course should be prompted for (_only_once_).

The following code works for me. It first defines a custom function to create the template (org-capture-class), which calculates the date as necessary and is easier on the eyes than the in-line string (note that it relies on cl-lib, so make sure that's loaded). It then calls that function in the template itself, niling out if any of the fields are empty (updated in response to comment):
(defun org-capture-class ()
"Capture a class template for org-capture."
(cl-labels ((update (date days)
(format-time-string
(car org-time-stamp-formats)
(seconds-to-time (+ (time-to-seconds date)
(* days 86400))))))
(let ((course (read-string "Course: " nil nil '(nil)))
(week (read-string "Week: " nil nil '(nil)))
(lecture (read-string "Lecture No.: " nil nil '(nil)))
(date (org-read-date nil t))
(location (read-string "Location: " nil nil '(nil))))
(when (and course week lecture date location)
(concat (format "* TODO %s: Week %s Lecture %s\n"
course week lecture)
(format " SCHEDULED: %s\n" (update date 0))
(format " Location: %s %%?\n" location)
(format "** TODO %s: prepare lecture %s from week %s\n"
course lecture week)
(format " DEADLINE: %s SCHEDULED: %s\n"
(update date -1) (update date -2))
(format "** TODO %s: review lecture %s from week %s\n"
course lecture week)
(format " DEADLINE: %s SCHEDULED: %s\n"
(update date 2) (update date 1)))))))
(setq org-capture-templates
'(("c" "Class" entry
(file "~/sydbox/personal/workflow/class.txt")
#'org-capture-class)
("e" "Exercise session" entry
(file "~/sydbox/personal/workflow/class.txt"))))

Related

How to get a Calendar prompt and insert date

I'm writing a function that takes two dates as arguments. (something like 2016-8-1)
I would like to get the dates from the beautiful emacs calendar view.
(defun my-fun (date1 date2)
(setq date1 (get-date-from-calendar))
)
I know in Org mode there is
C-c . (org-time-stamp)
But when used in elisp code, this function expect arguments.
Any ideas?
The argument is used to pass a prefix option if you want to avoid prompting the user. You can just pass nil and it should work (org-time-stamp nil)
(defun org-read-date-example ()
"Doc-string."
(require 'org)
(let ((date-in-string-format (org-read-date nil nil nil "Date #1: "))
(date-in-time-format (org-read-date nil 'to-time nil "Date #2: ")))
(message "Date #1: %s | Date #2: %s" date-in-string-format date-in-time-format)))

How to decode name of month into an integer

I'm trying to write a function that will let me choose (interactively) the month and year for org-agenda-month-view -- which uses a format of 201312 (without double-quotes) for December 2013 . I would like to use either words (December) or numbers (12) for the month, but need some assistance (please) to decode the name of the month into an integer -- something like (if (not (integer-p month)) (setq month (decode-name-of-month month))).
Also, any pointers on how to consolidate my two read-string  let bindings into just one action would be greatly appreciated -- i.e., be able to manually input January 2014 or 01 2014; instead of first inputting January and then inputting 2014 in two separate actions.
(let* (
(year (read-string "Insert the year: "))
(month (read-string "Insert the month: ")))
(org-agenda nil "1")
(org-agenda-month-view (read (format "%s%02s" year month))))
EDIT (December 11, 2013):  First working draft based upon the helpful answers by #Drew and #Sean Perry. Added variable org-agenda-start-day, which permits (org-agenda nil "1") to display [at the outset] the correct month based upon the user input -- the function org-agenda-month-view is no longer needed. Updated variable lawlist-parse-time-months to fix a typo and include additional possibilities -- e.g., 01, 02, etc.
(defvar lawlist-parse-time-months '(
("1" . "01")
("01" . "01")
("jan" . "01")
("january" . "01")
("2" . "02")
("02" . "02")
("feb" . "02")
("february". "02")
("3" . "03")
("03" . "03")
("mar" . "03")
("march" . "03")
("4" . "04")
("04" . "04")
("apr" . "04")
("april" . "04")
("5" . "05")
("05" . "05")
("may" . "05")
("6" . "06")
("06" . "06")
("jun" . "06")
("june" . "06")
("7" . "07")
("07" . "07")
("jul" . "07")
("july" . "07")
("8" . "08")
("08" . "08")
("aug" . "08")
("august" . "08")
("9" . "09")
("09" . "09")
("sep" . "09")
("september" . "09")
("10" . "10")
("oct" . "10")
("october" . "10")
("11" . "11")
("nov" . "11")
("november" . "11")
("12" . "12")
("dec" . "12")
("december" . "12")))
(defun encode-name-of-month (month_name)
(cdr (assoc (downcase month_name) lawlist-parse-time-months)) )
(defun decode-name-of-month (month)
(car (rassoc month lawlist-parse-time-months)))
(defun foo (month year)
(interactive
(let* ((mon+yr (split-string
(read-string "Month and year: " nil nil
(format-time-string "%B %Y"(current-time)))
nil 'TRIM))
(mon (car mon+yr))
(yr (cadr mon+yr))
(m2 (condition-case nil (format "%d" mon) (error nil))))
(list (or m2 mon) yr)))
(setq org-agenda-start-day (concat year "-" (encode-name-of-month month) "-" "01"))
(org-agenda nil "1") )
(require 'parse-time) ;; in standard lib
(defun encode-name-of-month (month_name)
(cdr (assoc (downcase month_name) parse-time-months))
)
(defun decode-name-of-month (month)
(car (rassoc month parse-time-months))
)
That should get you started.
(defun foo (month year)
(interactive
(let* ((mon+yr (split-string
(read-string "Month and year: " nil nil
(format-time-string "%B %Y"(current-time)))
nil 'TRIM))
(mon (car mon+yr))
(yr (cadr mon+yr))
(m2 (condition-case nil (format "%d" mon) (error nil))))
(list (or m2 mon) yr)))
(message "Month: %s, Year: %s" month year))
M-x foo 12 2013 ==> Month: 12, Year: 2013
M-x foo December 2013 ==> Month: December, Year: 2013
M-x foo Dec 2013 ==> Month: Dec, Year: 2013
M-x foo Nebraska 200000 ==> Month: Nebraska, Year: 200000
But you get the idea. (Check for valid month names and month & year numbers.)

How to convert (day month year) to ( month day year)

My e-mails in Wanderlust have a header that looks like this:
Date: Wed, 23 Oct 2013 12:18:15 -0700
I would like to modify the beginning of my print-to-pdf function so that it searches the current buffer for the first date it finds (usually the first line of the buffer) and converts it into a proposed pdf-file-name that looks like this:
10_23_2013.pdf
The beginning of my print-to-pdf function looks like this:
(defun print-to-pdf (pdf-file-name)
"Print the current buffer to the given file."
(interactive (list
(ns-read-file-name "Write PDF file: " "/Users/HOME/.0.data/" nil ".pdf")))
(cond (
(not (equal pdf-file-name nil))
***
Can anyone think of a way to search for the date and turn it into a proposed pdf-file-name?
EDIT:  Here are some of the date string functions I found by grepping the Wanderlust code:
(defun wl-make-date-string ()
(let ((system-time-locale "C"))
(format-time-string "%a, %d %b %Y %T %z")))
(defsubst wl-get-date-iso8601 (date)
(or (get-text-property 0 'wl-date date)
(let* ((d1 (timezone-fix-time date nil nil))
(time (format "%04d%02d%02dT%02d%02d%02d"
(aref d1 0) (aref d1 1) (aref d1 2)
(aref d1 3) (aref d1 4) (aref d1 5))))
(put-text-property 0 1 'wl-date time date)
time)))
(defun wl-make-date-string ()
(let ((s (current-time-string)))
(string-match "\\`\\([A-Z][a-z][a-z]\\) +[A-Z][a-z][a-z] +[0-9][0-9]? *[0-9][0-9]?:[0-9][0-9]:[0-9][0-9] *[0-9]?[0-9]?[0-9][0-9]"
s)
(concat (wl-match-string 1 s) ", "
(timezone-make-date-arpa-standard s (current-time-zone)))))
(defun wl-date-iso8601 (date)
"Convert the DATE to YYMMDDTHHMMSS."
(condition-case ()
(wl-get-date-iso8601 date)
(error "")))
Here's the function.
If you can find a way to extract Wed, 23 Oct 2013 12:18:15 -0700, it will produce
10_23_2013.pdf.
(defun transform-date (s &optional shift)
(let ((time (apply 'encode-time
(org-read-date-analyze
s nil
(decode-time (current-time))))))
(when shift
(setq time (time-add time (days-to-time shift))))
(format-time-string "%m_%d_%Y.pdf" time)))
Here's a simple finder for the date:
(defun find-and-transform ()
(goto-char (point-min))
(when (re-search-forward "Date: \\([^:]*?\\)[0-9]+:")
(transform-date
(match-string-no-properties 1))))

Custom function for next / previous month in agenda view and the calendar

I'm looking for a way to select next or previous month in both agenda view and the calendar. I've written a concept/prototype function (below), but it doesn't calculate the next or previous months and I would also need to rewrite the function every month.
The date format for the org-agenda-month-view is different than the date format for calendar-other-month. Further down below are some functions that are related to what I'm trying to accomplish -- e.g., calendar already has the ability to move forward or backward by month.
I think what may be needed is a function that identifies the month being viewed and then adds plus-or-minus one month (in the proper format) when hitting the next or previous button.
(defun lawlist-org-agenda-view-mode-dispatch ()
"Select the month in agenda view."
(interactive)
(message "View: [7] JUL | [8] AUG | [9] SEP | [o]CT | [n]OV | [d]EC ")
(let ((a (read-char-exclusive)))
(case a
(?7
(org-agenda nil "a")
(org-agenda-month-view 201307)
(calendar)
(calendar-other-month 7 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?8
(org-agenda nil "a")
(org-agenda-month-view 201308)
(calendar)
(calendar-other-month 8 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?9
(org-agenda nil "a")
(org-agenda-month-view 201309)
(calendar)
(calendar-other-month 9 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?o
(org-agenda nil "a")
(org-agenda-month-view 201310)
(calendar)
(calendar-other-month 10 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?n
(org-agenda nil "a")
        (org-agenda-month-view 201311)
(calendar)
(calendar-other-month 11 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?d
(org-agenda nil "a")
(org-agenda-month-view 201312)
(calendar)
(calendar-other-month 12 2013)
(lawlist-org-agenda-view-mode-dispatch))
(?q (message "Abort"))
(otherwise (error "Either press \"q\" to quit, or select another option." )))))
Here are some related functions I've extracted from cal-move.el and calendar.el:
(defun calendar-other-month (month year &optional event)
"Display a three-month calendar centered around MONTH and YEAR.
EVENT is an event like `last-nonmenu-event'."
(interactive (let ((event (list last-nonmenu-event)))
(append (calendar-read-date 'noday) event)))
(save-selected-window
(and event
(setq event (event-start event))
(select-window (posn-window event)))
(unless (and (= month displayed-month)
(= year displayed-year))
(let ((old-date (calendar-cursor-to-date))
(today (calendar-current-date)))
(calendar-generate-window month year)
(calendar-cursor-to-visible-date
(cond
((calendar-date-is-visible-p old-date) old-date)
((calendar-date-is-visible-p today) today)
(t (list month 1 year))))))))
;;;###cal-autoload
(defun calendar-forward-month (arg)
"Move the cursor forward ARG months.
Movement is backward if ARG is negative."
(interactive "p")
(calendar-cursor-to-nearest-date)
(let* ((cursor-date (calendar-cursor-to-date t))
(month (calendar-extract-month cursor-date))
(day (calendar-extract-day cursor-date))
(year (calendar-extract-year cursor-date))
(last (progn
(calendar-increment-month month year arg)
(calendar-last-day-of-month month year)))
(day (min last day))
;; Put the new month on the screen, if needed, and go to the new date.
(new-cursor-date (list month day year)))
(if (not (calendar-date-is-visible-p new-cursor-date))
(calendar-other-month month year))
(calendar-cursor-to-visible-date new-cursor-date))
(run-hooks 'calendar-move-hook))
;;;###cal-autoload
(defun calendar-backward-month (arg)
"Move the cursor backward by ARG months.
Movement is forward if ARG is negative."
(interactive "p")
(calendar-forward-month (- arg)))
;;;###cal-autoload
(defun calendar-forward-year (arg)
"Move the cursor forward by ARG years.
Movement is backward if ARG is negative."
(interactive "p")
(calendar-forward-month (* 12 arg)))
;;;###cal-autoload
(defun calendar-backward-year (arg)
"Move the cursor backward ARG years.
Movement is forward is ARG is negative."
(interactive "p")
(calendar-forward-month (* -12 arg)))
;;;###cal-autoload
(defun calendar-scroll-left (&optional arg event)
"Scroll the displayed calendar left by ARG months.
If ARG is negative the calendar is scrolled right. Maintains the relative
position of the cursor with respect to the calendar as well as possible.
EVENT is an event like `last-nonmenu-event'."
(interactive (list (prefix-numeric-value current-prefix-arg)
last-nonmenu-event))
(unless arg (setq arg 1))
(save-selected-window
;; Nil if called from menu-bar.
(if (setq event (event-start event)) (select-window (posn-window event)))
(calendar-cursor-to-nearest-date)
(unless (zerop arg)
(let ((old-date (calendar-cursor-to-date))
(today (calendar-current-date))
(month displayed-month)
(year displayed-year))
(calendar-increment-month month year arg)
(calendar-generate-window month year)
(calendar-cursor-to-visible-date
(cond
((calendar-date-is-visible-p old-date) old-date)
((calendar-date-is-visible-p today) today)
(t (list month 1 year))))))
(run-hooks 'calendar-move-hook)))
(define-obsolete-function-alias
'scroll-calendar-left 'calendar-scroll-left "23.1")
;;;###cal-autoload
(defun calendar-scroll-right (&optional arg event)
"Scroll the displayed calendar window right by ARG months.
If ARG is negative the calendar is scrolled left. Maintains the relative
position of the cursor with respect to the calendar as well as possible.
EVENT is an event like `last-nonmenu-event'."
(interactive (list (prefix-numeric-value current-prefix-arg)
last-nonmenu-event))
(calendar-scroll-left (- (or arg 1)) event))
(define-obsolete-function-alias
'scroll-calendar-right 'calendar-scroll-right "23.1")
Here's my take:
(defvar lawlist-month)
(defun lawlist-org-agenda-view-mode-dispatch ()
"Select the month in agenda view."
(interactive)
(message "View: [1-9] [o]CT [n]OV [d]EC, j(next), k(prev).")
(let* ((a (read-char-exclusive))
(month (case a
(?o 10)
(?n 11)
(?d 12)
(?j (or (and lawlist-month (mod (1+ lawlist-month) 12)) 1))
(?k (or (and lawlist-month (mod (1- lawlist-month) 12)) 1))
(t (and (> a ?0) (<= a ?9) (- a ?0))))))
(if (setq lawlist-month month)
(let ((year (nth 5 (decode-time (current-time)))))
(org-agenda nil "a")
(org-agenda-month-view
(read (format "%d%02d" year month)))
(calendar)
(calendar-other-month month year)
(lawlist-org-agenda-view-mode-dispatch))
(message "Aborted"))))
It still misses some functionality like saving the window configuration and
recovering on abort.
UPD
The updated code can be found in this gist.
I've added other years besides current, with support for j/k, as well as h/l for years.

How to test for org-todo state "xyz" with deadline not equal to today

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."))
))
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.