Browse Source

Support modifiers in effort durations (was: Re: Does Effort support hours only?)

Luke Crook wrote:
> Is it possible to specify estimated effort in something other
> than hours (0.5, or 0:30)?

> For example 1w, 1m, 2d etc?

Here's a cleaned up patch that allows user-specified modifiers
for effort strings.  The new variable `org-effort-durations'
lists modifiers, and their mapping to minutes (words, as well as
single-letter modifiers, are supported).  The default value is:

(("h" . 60)
 ("d" . 480) ; 8 hours
 ("w" . 2400) ; five days
 ("m" . 9600) ; 4 weeks
 ("y" . 96000)) ; 40 weeks

But you can change this.

Old effort strings (HH:MM) are still interpreted correctly.  See
the docstrings of `org-effort-durations' and
`org-duration-string-to-minutes' for more details.

>From a0e24b14755eb4087d9c47bb4eea11eb9151efcf Mon Sep 17 00:00:00 2001
From: Lawrence Mitchell <wence@gmx.li>
Date: Fri, 18 Feb 2011 11:01:46 +0000
Subject: [PATCH] Allow human-readable effort durations
To: emacs-orgmode@gnu.org

* lisp/org.el (org-effort-durations): New variable.
* lisp/org.el (org-duration-string-to-minutes): New function.
* lisp/org-agenda.el (org-agenda-filter-effort-form)
(org-format-agenda-item): Use it.
* lisp/org-clock.el (org-clock-notify-once-if-expired)
(org-clock-modify-effort-estimate, org-clock-get-clock-string): Use it.

Specifying large effort durations in hours and minutes is difficult.
Is 130:25 more than two weeks effort?  More than three?  This patch
allows specification of an effort duration as a friendly string.  For
example 2w 5d is two weeks and five days of effort.  Existing H:MM
entries will still be recognised correctly.
Lawrence Mitchell 14 years ago
parent
commit
75a1b98cfb
3 changed files with 47 additions and 6 deletions
  1. 2 2
      lisp/org-agenda.el
  2. 4 4
      lisp/org-clock.el
  3. 41 0
      lisp/org.el

+ 2 - 2
lisp/org-agenda.el

@@ -5334,7 +5334,7 @@ Any match of REMOVE-RE will be removed from TXT."
 		       (get-text-property 0 'org-marker txt)))
 		       (get-text-property 0 'org-marker txt)))
 		(error nil)))
 		(error nil)))
 	(when effort
 	(when effort
-	  (setq neffort (org-hh:mm-string-to-minutes effort)
+	  (setq neffort (org-duration-string-to-minutes effort)
 		effort (setq effort (concat "[" effort "]" )))))
 		effort (setq effort (concat "[" effort "]" )))))
 
 
       (when remove-re
       (when remove-re
@@ -6055,7 +6055,7 @@ E looks like \"+<2:25\"."
 		   ((equal op ??) op)
 		   ((equal op ??) op)
 		   (t '=)))
 		   (t '=)))
     (list 'org-agenda-compare-effort (list 'quote op)
     (list 'org-agenda-compare-effort (list 'quote op)
-	  (org-hh:mm-string-to-minutes e))))
+	  (org-duration-string-to-minutes e))))
 
 
 (defun org-agenda-compare-effort (op value)
 (defun org-agenda-compare-effort (op value)
   "Compare the effort of the current line with VALUE, using OP.
   "Compare the effort of the current line with VALUE, using OP.

+ 4 - 4
lisp/org-clock.el

@@ -487,7 +487,7 @@ If not, show simply the clocked time like 01:50."
 	 (m (- clocked-time (* 60 h))))
 	 (m (- clocked-time (* 60 h))))
     (if org-clock-effort
     (if org-clock-effort
 	(let* ((effort-in-minutes
 	(let* ((effort-in-minutes
-		(org-hh:mm-string-to-minutes org-clock-effort))
+		(org-duration-string-to-minutes org-clock-effort))
 	       (effort-h (floor effort-in-minutes 60))
 	       (effort-h (floor effort-in-minutes 60))
 	       (effort-m (- effort-in-minutes (* effort-h 60)))
 	       (effort-m (- effort-in-minutes (* effort-h 60)))
 	       (work-done-str
 	       (work-done-str
@@ -561,10 +561,10 @@ the mode line."
        ;; A string.  See if it is a delta
        ;; A string.  See if it is a delta
        (setq sign (string-to-char value))
        (setq sign (string-to-char value))
        (if (member sign '(?- ?+))
        (if (member sign '(?- ?+))
-	   (setq current (org-hh:mm-string-to-minutes current)
+	   (setq current (org-duration-string-to-minutes current)
 		 value (substring value 1))
 		 value (substring value 1))
 	 (setq current 0))
 	 (setq current 0))
-       (setq value (org-hh:mm-string-to-minutes value))
+       (setq value (org-duration-string-to-minutes value))
        (if (equal ?- sign)
        (if (equal ?- sign)
 	   (setq value (- current value))
 	   (setq value (- current value))
 	 (if (equal ?+ sign) (setq value (+ current value)))))
 	 (if (equal ?+ sign) (setq value (+ current value)))))
@@ -581,7 +581,7 @@ the mode line."
   "Show notification if we spent more time than we estimated before.
   "Show notification if we spent more time than we estimated before.
 Notification is shown only once."
 Notification is shown only once."
   (when (org-clocking-p)
   (when (org-clocking-p)
-    (let ((effort-in-minutes (org-hh:mm-string-to-minutes org-clock-effort))
+    (let ((effort-in-minutes (org-duration-string-to-minutes org-clock-effort))
 	  (clocked-time (org-clock-get-clocked-time)))
 	  (clocked-time (org-clock-get-clocked-time)))
       (if (setq org-task-overrun
       (if (setq org-task-overrun
 		(if (or (null effort-in-minutes) (zerop effort-in-minutes))
 		(if (or (null effort-in-minutes) (zerop effort-in-minutes))

+ 41 - 0
lisp/org.el

@@ -15524,6 +15524,47 @@ If no number is found, the return value is 0."
     (string-to-number (match-string 1 s)))
     (string-to-number (match-string 1 s)))
    (t 0)))
    (t 0)))
 
 
+(defcustom org-effort-durations
+  `(("h" . 60)
+    ("d" . ,(* 60 8))
+    ("w" . ,(* 60 8 5))
+    ("m" . ,(* 60 8 5 4))
+    ("y" . ,(* 60 8 5 40)))
+  "Conversion factor to minutes for an effort modifier.
+
+Each entry has the form (MODIFIER . MINUTES).
+
+In an effort string, a number followed by MODIFIER is multiplied
+by the specified number of MINUTES to obtain an effort in
+minutes.
+
+For example, if the value of this variable is ((\"hours\" . 60)), then an
+effort string \"2hours\" is equivalent to 120 minutes."
+  :group 'org-agenda
+  :type '(alist :key-type (string :tag "Modifier")
+		:value-type (number :tag "Minutes")))
+
+(defun org-duration-string-to-minutes (s)
+  "Convert a duration string S to minutes.
+
+A bare number is interpreted as minutes, modifiers can be set by
+customizing `org-effort-durations' (which see).
+
+Entries containing a colon are interpreted as H:MM by
+`org-hh:mm-string-to-minutes'."
+  (let ((result 0)
+	(regex (rx-to-string (group (1+ (any "0-9")))
+			     (0+ (syntax whitespace))
+			     (group
+			      (eval (cons 'or
+					  (mapcar 'car org-effort-durations)))))))
+    (while (string-match regex s)
+      (incf result (* (cdr (assoc (match-string 2 s) org-effort-durations))
+		      (string-to-number (match-string 1 s))))
+      (setq s (replace-match "" nil t s)))
+    (incf result (org-hh:mm-string-to-minutes s))
+    result))
+
 ;;;; Files
 ;;;; Files
 
 
 (defun org-save-all-org-buffers ()
 (defun org-save-all-org-buffers ()