summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <mail@nicolasgoaziou.fr>2016-02-17 21:35:34 +0100
committerNicolas Goaziou <mail@nicolasgoaziou.fr>2016-02-17 22:49:19 +0100
commitc158bf2f1679b1a3d6ab777784ee69e339f382d6 (patch)
treedab0be5cbc69c4a7f4b276f7befd1c174d86d553
parentc8e7d93bbd990f3879128829957b2d2afcb75924 (diff)
downloadorg-mode-c158bf2f1679b1a3d6ab777784ee69e339f382d6.tar.gz
org-colview: Refactor low-high estimates
* lisp/org-colview.el (org-columns-string-to-number): (org-columns-number-to-string): Handle estimates. (org-columns-estimate-combine): Rename this to... (org-columns--estimate-combine): ... this. (org-columns-compile-map): (org-columns-compute): Apply renaming. (org-estimate-mean-and-var): (org-estimate-print): (org-string-to-estimate): Remove functions. * testing/lisp/test-org-colview.el (test-org-colview/columns-summary): Add tests.
-rw-r--r--lisp/org-colview.el91
-rw-r--r--testing/lisp/test-org-colview.el31
2 files changed, 68 insertions, 54 deletions
diff --git a/lisp/org-colview.el b/lisp/org-colview.el
index 4042218..c5bf011 100644
--- a/lisp/org-colview.el
+++ b/lisp/org-colview.el
@@ -770,7 +770,7 @@ When COLUMNS-FMT-STRING is non-nil, use it as the column format."
("@min" min_age min)
("@max" max_age max)
("@mean" mean_age (lambda (&rest x) (/ (apply '+ x) (float (length x)))))
- ("est+" estimate org-estimate-combine))
+ ("est+" estimate org-columns--estimate-combine))
"Operator <-> format,function map.
Used to compile/uncompile columns format and completing read in
interactive function `org-columns-new'.
@@ -970,19 +970,21 @@ display, or in the #+COLUMNS line of the current buffer."
level last-level)
level (org-outline-level)
val (org-entry-get nil property)
- valflag (and val (string-match "\\S-" val)))
+ valflag (org-string-nw-p val))
(cond
((< level last-level)
;; Put the sum of lower levels here as a property. If
- ;; values are estimate, use an appropriate sum function.
- (setq sum (funcall
- (if (eq fun 'org-estimate-combine) #'org-estimate-combine
- #'+)
- (if (and (/= last-level inminlevel)
- (aref lvals last-level))
- (apply fun (aref lvals last-level)) 0)
- (if (aref lvals inminlevel)
- (apply fun (aref lvals inminlevel)) 0))
+ ;; values are estimates, use an appropriate sum function.
+ (setq sum (funcall (if (eq fun 'org-columns--estimate-combine)
+ #'org-columns--estimate-combine
+ #'+)
+ (if (and (/= last-level inminlevel)
+ (aref lvals last-level))
+ (apply fun (aref lvals last-level))
+ 0)
+ (if (aref lvals inminlevel)
+ (apply fun (aref lvals inminlevel))
+ 0))
flag (or (aref lflag last-level) ; any valid entries from children?
(aref lflag inminlevel)) ; or inline tasks?
str (org-columns-number-to-string sum format printf)
@@ -1037,7 +1039,9 @@ display, or in the #+COLUMNS line of the current buffer."
FMT is a symbol describing the summary type. Optional argument
PRINTF, when non-nil, is a format string used to print N."
(cond
- ((eq fmt 'estimate) (org-estimate-print n printf))
+ ((eq fmt 'estimate)
+ (let ((fmt (or printf "%.0f")))
+ (mapconcat (lambda (n) (format fmt n)) (if (consp n) n (list n n)) "-")))
((not (numberp n)) "")
((memq fmt '(add_times max_times min_times mean_times))
(org-hours-to-clocksum-string n))
@@ -1057,6 +1061,22 @@ PRINTF, when non-nil, is a format string used to print N."
(format-seconds "%dd %.2hh %mm %ss" n))
(t (number-to-string n))))
+(defun org-columns--estimate-combine (&rest estimates)
+ "Combine a list of estimates, using mean and variance.
+The mean and variance of the result will be the sum of the means
+and variances (respectively) of the individual estimates."
+ (let ((mean 0)
+ (var 0))
+ (dolist (e estimates)
+ (pcase e
+ (`(,low ,high)
+ (let ((m (/ (+ low high) 2.0)))
+ (cl-incf mean m)
+ (cl-incf var (- (/ (+ (* low low) (* high high)) 2.0) (* m m)))))
+ (value (cl-incf mean value))))
+ (let ((sd (sqrt var)))
+ (list (- mean sd) (+ mean sd)))))
+
(defun org-columns-string-to-number (s fmt)
"Convert a column value S to a number.
FMT is a symbol describing the summary type."
@@ -1081,7 +1101,11 @@ FMT is a symbol describing the summary type."
(setq sum (+ (string-to-number n) (/ sum 60))))))
((memq fmt '(checkbox checkbox-n-of-m checkbox-percent))
(if (equal s "[X]") 1. 0.000001))
- ((eq fmt 'estimate) (org-string-to-estimate s))
+ ((eq fmt 'estimate)
+ (if (not (string-match "\\(.*\\)-\\(.*\\)" s))
+ (string-to-number s)
+ (list (string-to-number (match-string 1 s))
+ (string-to-number (match-string 2 s)))))
((string-match-p org-columns--fractional-duration-re s)
(let ((s (concat "0:" (org-duration-string-to-minutes s t)))
(sum 0.0))
@@ -1495,47 +1519,6 @@ This will add overlays to the date lines, to show the summary for each day."
(equal (nth 4 a) (nth 4 fm)))
(org-columns-compute (car fm)))))))))))
-(defun org-estimate-mean-and-var (v)
- "Return the mean and variance of an estimate."
- (let* ((v (cond ((consp v) v)
- ((numberp v) (list v v))
- (t (error "Invalid estimate type"))))
- (low (float (car v)))
- (high (float (cadr v)))
- (mean (/ (+ low high) 2.0))
- (var (/ (+ (expt (- mean low) 2.0) (expt (- high mean) 2.0)) 2.0)))
- (list mean var)))
-
-(defun org-estimate-combine (&rest el)
- "Combine a list of estimates, using mean and variance.
-The mean and variance of the result will be the sum of the means
-and variances (respectively) of the individual estimates."
- (let ((mean 0)
- (var 0))
- (mapc (lambda (e)
- (let ((stats (org-estimate-mean-and-var e)))
- (setq mean (+ mean (car stats)))
- (setq var (+ var (cadr stats)))))
- el)
- (let ((stdev (sqrt var)))
- (list (- mean stdev) (+ mean stdev)))))
-
-(defun org-estimate-print (e &optional fmt)
- "Prepare a string representation of an estimate.
-This formats these numbers as two numbers with a \"-\" between them."
- (let ((fmt (or fmt "%.0f"))
- (e (cond ((consp e) e)
- ((numberp e) (list e e))
- (t (error "Invalid estimate type")))))
- (format "%s" (mapconcat (lambda (n) (format fmt n)) e "-"))))
-
-(defun org-string-to-estimate (s)
- "Convert a string to an estimate.
-The string should be two numbers joined with a \"-\"."
- (if (string-match "\\(.*\\)-\\(.*\\)" s)
- (list (string-to-number (match-string 1 s))
- (string-to-number(match-string 2 s)))
- (list (string-to-number s) (string-to-number s))))
(provide 'org-colview)
diff --git a/testing/lisp/test-org-colview.el b/testing/lisp/test-org-colview.el
index 75c3e2e..642d372 100644
--- a/testing/lisp/test-org-colview.el
+++ b/testing/lisp/test-org-colview.el
@@ -452,6 +452,37 @@
:A: 5d 3h
:END:"
(let ((org-columns-default-format "%A{@min}")) (org-columns))
+ (get-char-property (point) 'org-columns-value-modified))))
+ ;; {est+} gives a low-high estimate using mean and standard
+ ;; deviation.
+ (should
+ (equal
+ "3-17"
+ (org-test-with-temp-text
+ "* H
+** S1
+:PROPERTIES:
+:A: 0-10
+:END:
+** S1
+:PROPERTIES:
+:A: 0-10
+:END:"
+ (let ((org-columns-default-format "%A{est+}")) (org-columns))
+ (get-char-property (point) 'org-columns-value-modified))))
+ ;; When using {est+} summary, a single number is understood as
+ ;; a degenerate range.
+ (should
+ (equal
+ "4-4"
+ (org-test-with-temp-text
+ "* H
+** S1
+:PROPERTIES:
+:A: 4
+:END:
+"
+ (let ((org-columns-default-format "%A{est+}")) (org-columns))
(get-char-property (point) 'org-columns-value-modified)))))