I am looking for an elisp function that will return the total EFFORT of the subtree at point.

By "total EFFORT" I mean the "Sum times" value that would be produced by giving the subtree a :COLUMNS: property of %EFFORT{:}, and then switching the subtree to column view, and looking at the column value.

So in the example below, if point is on headline 'test' and I call hypothetical function get-subtree-effort, the function would return "0:14".

* test
** e1
:Effort:   0:07
** e2
:Effort:   0:07

Here's one way:

    (defun to-integer-mins (s)
      "Convert a string in HH:MM format to integer minutes."
     (if s
      (seq-reduce (lambda (x y) (+ y (* x 60)))
             (mapcar #'string-to-number (split-string s ":")) 0)

    (defun int-to-hh-mm (n)
      "Convert an integer to an HH:MM format string."
      (let* ((h (/ n 60))
         (m (- n (* h 60))))
       (format "%02d:%02d" h m)))

    (defun get-effort()
      (org-entry-get (point) "Effort"))

    (defun get-total-effort()
       (seq-reduce #'+
           (mapcar #'to-integer-mins
               (org-map-entries #'get-effort t 'tree))

If you do ESC ESC : (get-total-effort) RET with point at the "test" headline, you will get 00:14.

The main point here is the call to org-map-entries:

 (org-map-entries #'get-effort t 'tree)

which will traverse the subtree, apply the get-effort function on each headline and accumulate the results in a list. The get-effort function checks for an Effort property and returns it if found (if not, it returns nil). If you run the above call to org-map-entries at the top-level headline with ESC ESC : (org-map-entries #'get-effort t 'tree) RET you will get:

(nil "0:07" "0:07")

the sequence of Effort values at each headline of the tree.

All you have to do now is add them all together, but that is a bit messier than it sounds: you have to convert the strings to numbers (the number of minutes for each one), add them together to get the total number of minutes and then convert the numeric result to a string in HH:MM format. There are many ways to do that, but they have nothing to do with Org mode: they are generic data manipulations using standard emacs-lisp functions. I chose to implement them in functional style using mapcar and seq-reduce but they can be implemented using a more imperative style using loops instead.

| improve this answer | |

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.