From aa2db034a0edbc60f006e5be60faf9d2ca68575d Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Wed, 17 Feb 2016 15:16:22 +0100 Subject: [PATCH] org-colview: Fix age related summary * lisp/org-colview.el (org-columns-time): Rename to... (org--columns-time): ... this. (org-columns--fractional-duration-re): New variable. (org-columns-edit-value): (org-columns): (org-columns-compute-all): (org-agenda-columns): Use new variable. (org-columns-compile-map): Remove unneeded "calc" attribute. (org-columns-number-to-string): (org-columns-string-to-number): Fix ages set as a duration (e.g., "1d 12h"). Compare ages as seconds, not number of days. * doc/org.texi (Column attributes): Document syntax for "age". --- doc/org.texi | 7 ++- lisp/org-colview.el | 161 +++++++++++++++++++++++++--------------------------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/doc/org.texi b/doc/org.texi index ed84836e7..1365b06fa 100644 --- a/doc/org.texi +++ b/doc/org.texi @@ -5636,7 +5636,12 @@ optional. The individual parts have the following meaning: @{:min@} @r{Smallest time value in column.} @{:max@} @r{Largest time value.} @{:mean@} @r{Arithmetic mean of time values.} - @{@@min@} @r{Minimum age (in days/hours/mins/seconds).} + @{@@min@} @r{Minimum age (in + days/hours/mins/seconds@footnote{Days, hours, minutes and + seconds are represented with, respectively, @samp{d}, + @samp{h}, @samp{m} and @samp{s} suffixes, e.g., @samp{13h + 10s}. Alternatively, an age can be defined as a duration + since a given time-stamp (@pxref{Timestamps}).}).} @{@@max@} @r{Maximum age (in days/hours/mins/seconds).} @{@@mean@} @r{Arithmetic mean of ages (in days/hours/mins/seconds).} @{est+@} @r{Add @samp{low-high} estimates.} diff --git a/lisp/org-colview.el b/lisp/org-colview.el index 8fe35dfe6..de5c0d913 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -60,7 +60,8 @@ or nil if the normal value should be used." (defvar org-columns-overlays nil "Holds the list of current column overlays.") -(defvar org-columns-time) +(defvar org-columns--time 0.0 + "Number of seconds since the epoch, as a floating point number.") (defvar-local org-columns-current-fmt nil "Local variable, holds the currently active column format.") @@ -74,6 +75,10 @@ This is the compiled version of the format.") (defvar org-columns-top-level-marker (make-marker) "Points to the position where current columns region starts.") +(defconst org-columns--fractional-duration-re + (concat "[0-9.]+ *" (regexp-opt (mapcar #'car org-effort-durations))) + "Regexp matching a duration.") + (defvar org-columns-map (make-sparse-keymap) "The keymap valid in column display.") @@ -471,7 +476,7 @@ Where possible, use the standard interface for changing this line." (bol (point-at-bol)) (eol (point-at-eol)) (pom (or (get-text-property bol 'org-hd-marker) (point))) ; keep despite of compiler waring - (org-columns-time (time-to-number-of-days (current-time))) + (org-columns--time (float-time (current-time))) nval eval allowed) (cond ((equal key "CLOCKSUM") @@ -718,39 +723,39 @@ When COLUMNS-FMT-STRING is non-nil, use it as the column format." (org-columns-goto-top-level) ;; Initialize `org-columns-current-fmt' and ;; `org-columns-current-fmt-compiled'. - (let ((org-columns-time (time-to-number-of-days (current-time)))) - (org-columns-get-format columns-fmt-string)) - (unless org-columns-inhibit-recalculation (org-columns-compute-all)) - (save-excursion - (save-restriction - (when (and (not global) (org-at-heading-p)) - (narrow-to-region (point) (org-end-of-subtree t t))) - (when (assoc-string "CLOCKSUM" org-columns-current-fmt-compiled t) - (org-clock-sum)) - (when (assoc-string "CLOCKSUM_T" org-columns-current-fmt-compiled t) - (org-clock-sum-today)) - (let ((cache - ;; Collect contents of columns ahead of time so as to - ;; compute their maximum width. - (org-map-entries - (lambda () (cons (point) (org-columns--collect-values))) - nil nil (and org-columns-skip-archived-trees 'archive)))) - (when cache - (setq-local org-columns-current-maxwidths - (org-columns--autowidth-alist cache)) - (org-columns--display-here-title) - (when (setq-local org-columns-flyspell-was-active - (org-bound-and-true-p flyspell-mode)) - (flyspell-mode 0)) - (unless (local-variable-p 'org-colview-initial-truncate-line-value) - (setq-local org-colview-initial-truncate-line-value - truncate-lines)) - (setq truncate-lines t) - (dolist (entry cache) - (goto-char (car entry)) - (org-columns--display-here (cdr entry)))))))) - -(defvar org-columns-compile-map + (let ((org-columns--time (float-time (current-time)))) + (org-columns-get-format columns-fmt-string) + (unless org-columns-inhibit-recalculation (org-columns-compute-all)) + (save-excursion + (save-restriction + (when (and (not global) (org-at-heading-p)) + (narrow-to-region (point) (org-end-of-subtree t t))) + (when (assoc-string "CLOCKSUM" org-columns-current-fmt-compiled t) + (org-clock-sum)) + (when (assoc-string "CLOCKSUM_T" org-columns-current-fmt-compiled t) + (org-clock-sum-today)) + (let ((cache + ;; Collect contents of columns ahead of time so as to + ;; compute their maximum width. + (org-map-entries + (lambda () (cons (point) (org-columns--collect-values))) + nil nil (and org-columns-skip-archived-trees 'archive)))) + (when cache + (setq-local org-columns-current-maxwidths + (org-columns--autowidth-alist cache)) + (org-columns--display-here-title) + (when (setq-local org-columns-flyspell-was-active + (org-bound-and-true-p flyspell-mode)) + (flyspell-mode 0)) + (unless (local-variable-p 'org-colview-initial-truncate-line-value) + (setq-local org-colview-initial-truncate-line-value + truncate-lines)) + (setq truncate-lines t) + (dolist (entry cache) + (goto-char (car entry)) + (org-columns--display-here (cdr entry))))))))) + +(defconst org-columns-compile-map '(("none" none +) (":" add_times +) ("+" add_numbers +) @@ -760,17 +765,13 @@ When COLUMNS-FMT-STRING is non-nil, use it as the column format." ("X%" checkbox-percent +) ("max" max_numbers max) ("min" min_numbers min) - ("mean" mean_numbers - (lambda (&rest x) (/ (apply '+ x) (float (length x))))) + ("mean" mean_numbers (lambda (&rest x) (/ (apply '+ x) (float (length x))))) (":max" max_times max) (":min" min_times min) - (":mean" mean_times - (lambda (&rest x) (/ (apply '+ x) (float (length x))))) - ("@min" min_age min (lambda (x) (- org-columns-time x))) - ("@max" max_age max (lambda (x) (- org-columns-time x))) - ("@mean" mean_age - (lambda (&rest x) (/ (apply '+ x) (float (length x)))) - (lambda (x) (- org-columns-time x))) + (":mean" mean_times (lambda (&rest x) (/ (apply '+ x) (float (length x))))) + ("@min" min_age min) + ("@max" max_age max) + ("@mean" mean_age (lambda (&rest x) (/ (apply '+ x) (float (length x))))) ("est+" estimate org-estimate-combine)) "Operator <-> format,function,calc map. Used to compile/uncompile columns format and completing read in @@ -916,7 +917,7 @@ display, or in the #+COLUMNS line of the current buffer." "Compute all columns that have operators defined." (org-with-silent-modifications (remove-text-properties (point-min) (point-max) '(org-summaries t))) - (let ((org-columns-time (time-to-number-of-days (current-time)))) + (let ((org-columns--time (float-time (current-time)))) (dolist (spec org-columns-current-fmt-compiled) (pcase spec (`(,property ,_ ,_ ,operator . ,_) @@ -1062,46 +1063,40 @@ PRINTF, when non-nil, is a format string used to print N." (printf (format printf n)) ((eq fmt 'currency) (format "%.2f" n)) ((memq fmt '(min_age max_age mean_age)) - (let* ((days (floor n)) - (frac-hours (* 24 (- n days))) - (hours (floor frac-hours)) - (minutes (floor (* 60 (- frac-hours hours)))) - (seconds (floor (* 60 (- (* 60 (- frac-hours hours)) minutes))))) - (format "%dd %02dh %02dm %02ds" days hours minutes seconds))) + (format-seconds "%dd %.2hh %mm %ss" n)) (t (number-to-string n)))) (defun org-columns-string-to-number (s fmt) - "Convert a column value to a number that can be used for column computing." - (if s - (cond - ((memq fmt '(min_age max_age mean_age)) - (cond ((string= s "") org-columns-time) - ((string-match - "\\([0-9]+\\)d \\([0-9]+\\)h \\([0-9]+\\)m \\([0-9]+\\)s" - s) - (+ (* 60 (+ (* 60 (+ (* 24 (string-to-number (match-string 1 s))) - (string-to-number (match-string 2 s)))) - (string-to-number (match-string 3 s)))) - (string-to-number (match-string 4 s)))) - (t (time-to-number-of-days (apply 'encode-time - (org-parse-time-string s t)))))) - ((string-match ":" s) - (let ((l (nreverse (org-split-string s ":"))) (sum 0.0)) - (while l - (setq sum (+ (string-to-number (pop l)) (/ sum 60)))) - sum)) - ((memq fmt '(checkbox checkbox-n-of-m checkbox-percent)) - (if (equal s "[X]") 1. 0.000001)) - ((memq fmt '(estimate)) (org-string-to-estimate s)) - ((string-match (concat "\\([0-9.]+\\) *\\(" - (regexp-opt (mapcar 'car org-effort-durations)) - "\\)") s) - (setq s (concat "0:" (org-duration-string-to-minutes s t))) - (let ((l (nreverse (org-split-string s ":"))) (sum 0.0)) - (while l - (setq sum (+ (string-to-number (pop l)) (/ sum 60)))) - sum)) - (t (string-to-number s))))) + "Convert a column value S to a number. +FMT is a symbol describing the summary type." + (cond + ((not s) nil) + ((memq fmt '(min_age max_age mean_age)) + (cond + ((string= s "") org-columns--time) + ((string-match "\\`\\(?: *\\([0-9]+\\)d\\)?\\(?: *\\([0-9]+\\)h\\)?\ +\\(?: *\\([0-9]+\\)m\\)?\\(?: *\\([0-9]+\\)s\\)?\\'" s) + (let ((d (if (match-end 1) (string-to-number (match-string 1 s)) 0)) + (h (if (match-end 2) (string-to-number (match-string 2 s)) 0)) + (m (if (match-end 3) (string-to-number (match-string 3 s)) 0)) + (s (if (match-end 4) (string-to-number (match-string 4 s)) 0))) + (+ (* 60 (+ (* 60 (+ (* 24 d) h)) m)) s))) + (t + (- org-columns--time + (float-time (apply #'encode-time (org-parse-time-string s))))))) + ((string-match-p ":" s) ;Interpret HH:MM:SS. + (let ((sum 0.0)) + (dolist (n (nreverse (split-string s ":")) sum) + (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)) + ((string-match-p org-columns--fractional-duration-re s) + (let ((s (concat "0:" (org-duration-string-to-minutes s t))) + (sum 0.0)) + (dolist (n (nreverse (split-string s ":")) sum) + (setq sum (+ (string-to-number n) (/ sum 60)))))) + (t (string-to-number s)))) (defun org-columns-uncompile-format (cfmt) "Turn the compiled columns format back into a string representation." @@ -1365,7 +1360,7 @@ PARAMS is a property list of parameters: (interactive) (org-columns-remove-overlays) (move-marker org-columns-begin-marker (point)) - (let ((org-columns-time (time-to-number-of-days (current-time))) + (let ((org-columns--time (float-time (current-time))) (fmt (cond ((org-bound-and-true-p org-agenda-overriding-columns-format)) -- 2.11.4.GIT