I'm trying to build custom agenda block which would sort completed tasks by CLOSED property. org-agenda-sorting-strategy only supports ordering by SCHEDULED and DEADLINE properties, so I guess only way to implement this is to use custom sorting function with org-agenda-cmp-user-defined. Any help would be greatly appreciated.

  ;; custom sorting for closed items (does not work)
  (defun my/org-custom-cmp (a b)
    (org-cmp-ts a b "closed")

  (setq org-agenda-cmp-user-defined 'my/org-custom-cmp)

  (setq org-agenda-custom-commands
        '(("z" "Tasks Overview"
           ((tags "+TODO=\"DONE\"+CLOSED>\"<-3d>\"" ((org-agenda-cmp-user-defined 'my/org-custom-cmp)
                                                     (org-agenda-sorting-strategy '(user-defined-up))
                                                     (org-agenda-max-entries 15)
                                                     (org-agenda-overriding-header "Recently Completed Tasks")))

;; ...
  • Please place your cursor on an org-agenda result that contains a closed entry and M-x eval-expression RET (message "%s" (text-properties-at (point)) RET and see if there are any properties that indicate the closed date, and then either update your question or post a comment with the results. I added text properties for many items in a custom version of org-mode, and I sent in a feature request to the org-mode team a couple of years ago to increase the sorting abilities. The org-team responding by stating that fixes/enhancements were in the works. What version of org-mode are you using? – lawlist Aug 17 '16 at 20:31
  • I'm using org-mode version 8.3.5, I've tried using text-properties-at (and M-x describe-text-properties), but couldn't find anything related to closed timestamp there. – David Senkus Aug 19 '16 at 10:57

As @lawlist said the entry's properties are stored in text properties of the corresponding lines in the agenda. We just have to extract those using get-text-property. Not every property makes it into the agenda, but there is an org-marker property telling us were the original data is. We can use that to extract whatever we like using org-entry-get.

Here's a function that provides a comparator based on any timestamp property:

(defun cmp-date-property (prop)
  "Compare two `org-mode' agenda entries, `A' and `B', by some date property.

If a is before b, return -1. If a is after b, return 1. If they
are equal return t."
  (lexical-let ((prop prop))
  #'(lambda (a b)

    (let* ((a-pos (get-text-property 0 'org-marker a))
           (b-pos (get-text-property 0 'org-marker b))
           (a-date (or (org-entry-get a-pos prop)
                       (format "<%s>" (org-read-date t nil "now"))))
           (b-date (or (org-entry-get b-pos prop)
                       (format "<%s>" (org-read-date t nil "now"))))
           (cmp (compare-strings a-date nil nil b-date nil nil))
      (if (eq cmp t) nil (signum cmp))

Just set org-agenda-cmp-user-defined to (cmp-date-property "CLOSED") and it should work:

(add-to-list 'org-agenda-custom-commands
             '("z" "Tasks Overview"
               tags "+TODO=\"DONE\"+CLOSED>\"<-3d>\""
               ((org-agenda-cmp-user-defined (cmp-date-property
                (org-agenda-sorting-strategy '(user-defined-up)))))

Two things to note: it compares the timestamps as strings which is basically fine using the default timestamp format, less fine otherwise; if the property in question doesn't exist it's value defaults to <now>.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.