I'm trying to track how much time I need for a project. Individual tasks all have a time estimate (for example below, one hour each), so my initial estimate was 6 hours for the whole project. But once I finish a task and its estimate turns wrong (according to the clocked time), I would like to have an updated estimate for the full project: if a task took more time than estimated, the total project time should increase, and the opposite.
Inspired by another answer, I used org-columns-modify-value-for-display-function
to get an updated effort value for individual tasks, but the summation in parent headlines doesn't work.
Here is an example org file:
* Complete project
:PROPERTIES:
:COLUMNS: %ITEM %TODO %EFFORT{:} %CLOCKSUM %EFFORT(Updated effort){:}
:ID: 20230203T142140.528627
:END:
** Part 1
*** DONE 1a
:PROPERTIES:
:Effort: 1h
:END:
:LOGBOOK:
CLOCK: [2023-02-03 Fri 13:20]--[2023-02-03 Fri 13:40] => 0:20
:END:
*** DONE 1b
:PROPERTIES:
:Effort: 1h
:END:
:LOGBOOK:
CLOCK: [2023-02-03 Fri 13:20]--[2023-02-03 Fri 15:40] => 2:20
:END:
*** TODO 1c
:PROPERTIES:
:Effort: 1h
:END:
** Part 2
*** DONE 2a
:PROPERTIES:
:Effort: 1h
:END:
*** TODO 2b
:PROPERTIES:
:Effort: 1h
:END:
:LOGBOOK:
CLOCK: [2023-02-03 Fri 13:22]--[2023-02-03 Fri 15:22] => 2:00
:END:
*** TODO 2c
:PROPERTIES:
:Effort: 1h
:END:
:LOGBOOK:
CLOCK: [2023-02-03 Fri 13:22]--[2023-02-03 Fri 13:42] => 0:20
:END:
And the corresponding columns view:
#+BEGIN: columnview :hlines 1 :id "20230203T142140.528627"
| ITEM | TODO | EFFORT | CLOCKSUM | Updated effort |
|------------------+------+--------+----------+----------------|
| Complete project | | 6:00 | 5:00 | 6:00 |
| Part 1 | | 3:00 | 2:40 | 3:00 |
| 1a | DONE | 1h | 0:20 | 0:20 |
| 1b | DONE | 1h | 2:20 | 2:20 |
| 1c | TODO | 1h | | 1:00 |
| Part 2 | | 3:00 | 2:20 | 3:00 |
| 2a | DONE | 1h | | 1:00 |
| 2b | TODO | 1h | 2:00 | 2:00 |
| 2c | TODO | 1h | 0:20 | 1:00 |
#+END:
1a and 1b are marked done, so the updated effort is the clocked time. 2a is done but I didn't clock in, so it keeps the value for the effort. 1c is not started yet, and 2c has been worked on for less than the time estimate, so their effort stays the same. 2b has been worked on for more than the time estimate, so the updated effort is the clocked time.
My problem is now that the times are not adding up correctly: if I don't exclude headlines without effort estimate, the updated effort for Part 1, Part 2 and Complete project follow the same rules as all other headlines. If I do, then they fall back to their original value, in this case the effort. Instead, I would like them to be the sum of their children (so Part 1 should be 3:40, Part 2 4:00, and Complete project 7:40), and I'm not sure how to implement that.
This is my code:
(setq org-columns-modify-value-for-display-function
'(lambda (column-title value)
"Apply function associated with COLUMN-TITLE on VALUE, as defined in `column-display-alist'."
(let ((fun (cdr-safe (assoc-string column-title column-display-alist))))
(when (functionp fun)
(funcall fun value)))))
(setq column-display-alist '(("Updated effort" . effort-or-clock)))
(defun effort-or-clock (effort)
"Get an updated effort if the task is finished or takes longer."
(if (org-entry-get (point) "Effort") ; only if tasks itself, not parent
(let* ((isdone (org-entry-is-done-p))
(clocked-time (org-clock-sum-current-item (org-clock-get-sum-start)))
(time (cond ((and isdone (> clocked-time 0)) ; done: clocked time
clocked-time)
((and effort (> clocked-time 0)) ; not done: max of clocked and effort
(max (org-duration-to-minutes effort) clocked-time))
(t ; others: effort
(org-duration-to-minutes effort)))))
(org-duration-from-minutes time))
nil))