Closed benthamite closed 3 years ago
To answer my own question: I simply replaced the two functions containing org-schedule
or :scheduled
with org-deadline
and :deadline
, respectively. The functions in question are org-gcal--update-entry
and ~org-gcal--back-to-heading
~ org-gcal--get-time-and-desc
. There may be a more elegant way of addressing the issue, but this worked for me. I did the replacement using el-patch.
Thanks - I haven't heard of el-patch before - looks interesting. Would you mind sending me your el-patch commands? I'd like to see if I can easily use it to replace some parts of my init file.
There is a hook for customizing updating the event entry (i.e., headline), org-gcal-after-update-entry-functions
. Unfortunately it was not documented in the README, so I fixed that in db3072a. For your use case, though, you have to customize how the time is read from the event as well (I assume you patched org-gcal--get-time-and-desc
, not org-gcal--back-to-heading
), so that hook wouldn't be enough, and el-patch looks like the best alternative.
Thanks for explaining that. And you are correct: I meant org-gcal--get-time-and-desc
; sorry for the confusion.
I'm an Elisp novice and this is actually the first time I use el-patch
, so I can't guarantee I'm doing things properly. Below I include my entire org-gcal
use-package
declaration for completeness (it assumes straight, general and hydra are installed):
(use-package org-gcal
:straight (org-gcal :type git :host github :repo "kidd/org-gcal.el")
:custom
(org-gcal-file-alist `((,ps/personal-gmail . ,ps/file-calendar)))
(org-gcal-client-id (auth-source-pass-get "host" "auth-sources/org-gcal"))
(org-gcal-client-secret (auth-source-pass-get 'secret "auth-sources/org-gcal"))
(org-gcal-up-days 7)
(org-gcal-down-days 7)
(org-gcal-recurring-events-mode 'nested)
(org-gcal-remove-api-cancelled-events nil) ; never remove cancelled events
(org-gcal-auto-archive nil)
:config
(defhydra hydra-org-gcal (:hint nil)
"
_f_etch all |_s_ync all |_p_ost at point |token _r_equest |_u_nlock sync
_F_etch buffer |_S_ync buffer |_d_elete at point |token _c_lear |_q_uit "
("f" org-gcal-fetch)
("F" org-gcal-fetch-buffer)
("s" org-gcal-sync)
("S" org-gcal-sync-buffer)
("p" org-gcal-post-at-point)
("d" org-gcal-delete-at-point)
("u" org-gcal--sync-unlock)
("r" org-gcal-request-token)
("c" org-gcal-sync-tokens-clear)
("q" nil))
:init/el-patch
(defun org-gcal--get-time-and-desc ()
"Get the timestamp and description of the event at point.
Return a plist with :start, :end, and :desc keys. The value for a key is nil if
not present."
(let (start end desc tobj elem)
(save-excursion
(org-gcal--back-to-heading)
(setq elem (org-element-at-point))
;; Parse :org-gcal: drawer for event time and description.
(when
(re-search-forward
(format "^[ \t]*:%s:[ \t]*$" org-gcal-drawer-name)
(save-excursion (outline-next-heading) (point))
'noerror)
;; First read any event time from the drawer if present. It's located
;; at the beginning of the drawer.
(save-excursion
(when
(re-search-forward "<[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]"
(save-excursion (outline-next-heading) (point))
'noerror)
(goto-char (match-beginning 0))
(setq tobj (org-element-timestamp-parser))))
;; Lines after the timestamp contain the description. Skip leading
;; blank lines.
(forward-line)
(beginning-of-line)
(re-search-forward
"\\(?:^[ \t]*$\\)*\\([^z-a]*?\\)\n?[ \t]*:END:"
(save-excursion (outline-next-heading) (point)))
(setq desc (match-string-no-properties 1))
(setq desc
(if (string-match-p "\\‘\n*\\’" desc)
nil
(replace-regexp-in-string
"^✱" "*"
(replace-regexp-in-string
"\\`\\(?: *<[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].*?>$\\)\n?\n?"
""
(replace-regexp-in-string
" *:PROPERTIES:\n *\\(.*\\(?:\n.*\\)*?\\) *:END:\n+"
""
desc)))))))
;; Prefer to read event time from the SCHEDULE property if present.
(setq tobj (or (org-element-property (el-patch-swap :scheduled :deadline) elem) tobj))
(when tobj
(when (plist-get (cadr tobj) :year-start)
(setq
start
(org-gcal--format-org2iso
(plist-get (cadr tobj) :year-start)
(plist-get (cadr tobj) :month-start)
(plist-get (cadr tobj) :day-start)
(plist-get (cadr tobj) :hour-start)
(plist-get (cadr tobj) :minute-start)
(when (plist-get (cadr tobj) :hour-start) t))))
(when (plist-get (cadr tobj) :year-end)
(setq
end
(org-gcal--format-org2iso
(plist-get (cadr tobj) :year-end)
(plist-get (cadr tobj) :month-end)
(plist-get (cadr tobj) :day-end)
(plist-get (cadr tobj) :hour-end)
(plist-get (cadr tobj) :minute-end)
(when (plist-get (cadr tobj) :hour-end) t)))))
(list :start start :end end :desc desc)))
(defun org-gcal--update-entry (calendar-id event &optional update-mode)
"Update the entry at the current heading with information from EVENT.
EVENT is parsed from the Calendar API JSON response using ‘org-gcal--json-read’.
CALENDAR-ID must be passed as well. Point must be located on an Org-mode heading
line or an error will be thrown. Point is not preserved.
If UPDATE-MODE is passed, then the functions in
‘org-gcal-after-update-entry-functions' are called in order with the same
arguments as passed to this function and the point moved to the beginning of the
heading."
(unless (org-at-heading-p)
(user-error "Must be on Org-mode heading."))
(let* ((smry (or (plist-get event :summary)
"busy"))
(desc (plist-get event :description))
(loc (plist-get event :location))
(_link (plist-get event :htmlLink))
(meet (plist-get event :hangoutLink))
(etag (plist-get event :etag))
(event-id (plist-get event :id))
(stime (plist-get (plist-get event :start)
:dateTime))
(etime (plist-get (plist-get event :end)
:dateTime))
(sday (plist-get (plist-get event :start)
:date))
(eday (plist-get (plist-get event :end)
:date))
(start (if stime (org-gcal--convert-time-to-local-timezone stime org-gcal-local-timezone) sday))
(end (if etime (org-gcal--convert-time-to-local-timezone etime org-gcal-local-timezone) eday))
(old-time-desc (org-gcal--get-time-and-desc))
(old-start (plist-get old-time-desc :start))
(old-end (plist-get old-time-desc :start))
(recurrence (plist-get event :recurrence))
(elem))
(when loc (replace-regexp-in-string "\n" ", " loc))
(org-edit-headline smry)
(org-entry-put (point) org-gcal-etag-property etag)
(when recurrence (org-entry-put (point) "recurrence" (format "%s" recurrence)))
(when loc (org-entry-put (point) "LOCATION" loc))
(when meet
(org-entry-put
(point)
"HANGOUTS"
(format "[[%s][%s]]"
meet
"Join Hangouts Meet")))
(org-entry-put (point) org-gcal-calendar-id-property calendar-id)
(org-gcal--put-id (point) calendar-id event-id)
;; Insert event time and description in :ORG-GCAL: drawer, erasing the
;; current contents.
(org-gcal--back-to-heading)
(setq elem (org-element-at-point))
(save-excursion
(when (re-search-forward
(format
"^[ \t]*:%s:[^z-a]*?\n[ \t]*:END:[ \t]*\n?"
(regexp-quote org-gcal-drawer-name))
(save-excursion (outline-next-heading) (point))
'noerror)
(replace-match "" 'fixedcase)))
(unless (re-search-forward ":PROPERTIES:[^z-a]*?:END:"
(save-excursion (outline-next-heading) (point))
'noerror)
(message "PROPERTIES not found: %s (%s) %d"
(buffer-name) (buffer-file-name) (point)))
(end-of-line)
(newline)
(insert (format ":%s:" org-gcal-drawer-name))
(newline)
;; Keep existing timestamps for parent recurring events.
(when (and recurrence old-start old-end)
(setq start old-start
end old-end))
(let*
((timestamp
(if (or (string= start end) (org-gcal--alldayp start end))
(org-gcal--format-iso2org start)
(if (and
(= (plist-get (org-gcal--parse-date start) :year)
(plist-get (org-gcal--parse-date end) :year))
(= (plist-get (org-gcal--parse-date start) :mon)
(plist-get (org-gcal--parse-date end) :mon))
(= (plist-get (org-gcal--parse-date start) :day)
(plist-get (org-gcal--parse-date end) :day)))
(format "<%s-%s>"
(org-gcal--format-date start "%Y-%m-%d %a %H:%M")
(org-gcal--format-date end "%H:%M"))
(format "%s--%s"
(org-gcal--format-iso2org start)
(org-gcal--format-iso2org
(if (< 11 (length end))
end
(org-gcal--iso-previous-day end))))))))
(if (org-element-property (el-patch-swap :scheduled :deadline) elem)
(unless (and recurrence old-start) ((el-patch-swap org-schedule org-deadline) nil timestamp))
(insert timestamp)
(newline)
(when desc (newline))))
;; Insert event description if present.
(when desc
(insert (replace-regexp-in-string "^\*" "✱" desc))
(insert (if (string= "\n" (org-gcal--safe-substring desc -1)) "" "\n")))
(insert ":END:")
(when (org-gcal--event-cancelled-p event)
(save-excursion
(org-back-to-heading t)
(org-gcal--handle-cancelled-entry)))
(when update-mode
(cl-dolist (f org-gcal-after-update-entry-functions)
(save-excursion
(org-back-to-heading t)
(funcall f calendar-id event update-mode))))))
:general
(org-mode-map
"s-g" 'hydra-org-gcal/body))
As you can see, the only difference between the patched function and the original one is to be found in the lines containing :schedule
and org-schedule
.
BTW, it may be possible to avoid having to copy the entire defun
, as discussed here, though I haven't yet investigated this.
When I create an event via
org-gcal-post-at-point
or by syncing an existing event on Google Calendar, org agenda displays it as an event with an ordinary time stamp. Is there a way to make org interpret this date as a deadline instead, i.e. as a date created usingorg-deadline
? In my workflow, I draw a clear distinction between events that are merely scheduled and events with a proper deadline: the latter involve an external commitment, and are shown with a different color and font. I have already missed a couple of appointments because of this, so I was wondering iforg-gcal
could be configured so as to address the issue. Thank you.