From ba9edc5f12fcfff8b003a79c64c0fb55df59107f Mon Sep 17 00:00:00 2001 From: Daniel Borchmann Date: Sun, 19 Mar 2023 20:56:15 +0100 Subject: [PATCH] Display remaining effort of items in workload reports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is rather experimental, but seems to work fine so far. The most complicated part is to compute the overall clocked time of an item. Any clocktime modifiers like “today” or “total” are currently ignored. --- site-lisp/db-org.el | 60 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/site-lisp/db-org.el b/site-lisp/db-org.el index 33f8a62..7a13231 100644 --- a/site-lisp/db-org.el +++ b/site-lisp/db-org.el @@ -394,6 +394,53 @@ should not be clocked." (interactive) (message org-clock-current-task)) +(defun db/org-clocked-time-for-current-item () + "Return all clocked time for Org item at point. + +Also includes the currently running clock when the current item +is clocked in." + + (unless (derived-mode-p 'org-mode) + (user-error "Not in Org mode buffer, aborting")) + + (let ((at-current-clock-p (and (markerp org-clock-hd-marker) + (save-mark-and-excursion ; from `org-clock-in' + (org-back-to-heading t) + (and (eq (marker-buffer org-clock-hd-marker) + (org-base-buffer (current-buffer))) + (= (point) (marker-position org-clock-hd-marker)) + (equal org-clock-current-task (org-get-heading t t t t))))))) + + (+ (org-clock-sum-current-item) + (if at-current-clock-p + ;; From `org-clock-get-clocked-time' + (floor (org-time-convert-to-integer + (org-time-since org-clock-start-time)) + 60) + 0)))) + +(defun db/org-remaining-effort-of-current-item () + "Return remaining effort of Org item at point, as duration. + +The remaining effort is computed as the planned effort minus the +already clocked time. If this result is negative, return zero. + +If no effort is specified, return nil." + + (if (derived-mode-p 'org-agenda-mode) + + ;; XXX: is this the right way to do it? + (org-agenda-with-point-at-orig-entry + nil (db/org-remaining-effort-of-current-item)) + + (unless (derived-mode-p 'org-mode) + (user-error "Not in Org mode buffer, aborting")) + + (when-let ((effort (org-entry-get (point) "Effort"))) + (org-duration-from-minutes + (max 0 (- (org-duration-to-minutes effort) + (db/org-clocked-time-for-current-item))))))) + ;;; Task Management @@ -406,9 +453,10 @@ scheduled, have an active timestamp, or are deadline in the given time range. The result has the form (TOTAL-TIME . TASKS), where TASKS is a -list of cons cells (ID . EFFORT). The total time is given as an -Org mode time string of the form hh:mm, as are all EFFORT -entries. +list of cons cells (ID . REMAINING-EFFORT). REMAINING-EFFORT is +computed as per `db/org-remaining-effort-of-current-item', which +see. The total time is given as an Org mode time string of the +form hh:mm, as is REMAINING-EFFORT entries. When ORG-QL-MATCH, an org-ql sexp, is given, filter the list of tasks in range by this expression. When ORG-QL-MATCH is not @@ -436,7 +484,7 @@ imposed on the respective time range." :from (org-agenda-files) :select '(cons (org-id-get-create) - (org-entry-get (point) "Effort")) + (db/org-remaining-effort-of-current-item)) :where `(and ,(or org-ql-match '(todo)) ;; Is this redundant? Could we just stick with `ts-active'? (or (scheduled ,@start-date-expr ,@end-date-expr) @@ -477,8 +525,8 @@ PARAMS is a property list of the following parameters: `timestamp', `scheduled', or `deadline'. All tasks between `:start-date' and `:end-date' will be collected -and their effort summed up. The date format is everything -understood by `org-read-date'." +and their remaining effort summed up. The date format is +everything understood by `org-read-date'." (let* ((start-date (plist-get params :start-date)) (end-date (plist-get params :end-date)) (org-ql-match (plist-get params :org-ql-match))