summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarsten Dominik <carsten.dominik@gmail.com>2010-04-23 21:24:19 +0200
committerCarsten Dominik <carsten.dominik@gmail.com>2010-04-23 21:24:19 +0200
commitc59da3a3dd5ec282f955994e309d8fe008acb171 (patch)
treef968f7aa918da327586ca0c213a331ce65031d9e
parent07198e34f6b9895498cdf0aa840c5dddaef070e0 (diff)
downloadorg-mode-c59da3a3dd5ec282f955994e309d8fe008acb171.tar.gz
Add tags matching to clock tables
Adam Elliott writes: > I have attached a git patch against master that implements a new > parameter to clock tables, "tags". This parameter is a tags-query as a > string and is used to filter the headlines which are consulted when > building the clock table. > > In my search of the archives to see if this feature already existed, I > found a reference here: > http://article.gmane.org/gmane.emacs.orgmode/17304 > suggesting it was difficult. The patch is not so large, though, so > perhaps I am missing something. > > My rationale in implementing this feature was to keep track of the > occasional task item that is not billable, yet still makes sense to > include in the overall project structure. Of course I could just avoid > clocking the task item, or manually delete clock lines before generating > a report, but this feature reduces the chance for error; no doubt there > are other workflows enabled with this feature as well. I don't make > significant use of tags myself, but I know many do. > > In order to maintain a sensible report, headlines that don't match the > tag filter may be included if they have descendants that do. Any time > clocked directly on non-matching headlines, however, is excluded. > > Specifying even a simple filter noticeably slows down clock table > generation for non-toy reports, particularly for clock table reports > with :step. If there is no filter, though, there is no degradation in > performance. > > Tag filter syntax is the standard one, as described at: > http://orgmode.org/manual/Matching-tags-and-properties.html > Only tags are considered at the moment, although I suspect querying > against all properties would be possible (if even slower). > > Examples: > > * development > CLOCK: => 1:00 > *** task 1 > CLOCK: => 1:00 > *** task 2 :must: > ***** task 2a > CLOCK: => 1:00 > ***** task 2b :mustnot: > CLOCK: => 1:00 > > Note I am using an unconventional but legal(ish) clock format for > brevity. Clock tables are also pruned to only relevant lines. > > [1] #+BEGIN: clocktable > | | *Total time* | *4:00* | | | > |---+--------------+--------+------+------| > | 1 | development | 4:00 | | | > | 2 | task 1 | | 1:00 | | > | 2 | task 2 | | 2:00 | | > | 3 | task 2a | | | 1:00 | > | 3 | task 2b | | | 1:00 | > > [2] #+BEGIN: clocktable :tags "must" > | | *Total time* | *2:00* | | | > |---+--------------+--------+------+------| > | 1 | development | 2:00 | | | > | 2 | task 2 | | 2:00 | | > | 3 | task 2a | | | 1:00 | > | 3 | task 2b | | | 1:00 | > > [3] #+BEGIN: clocktable :tags "-mustnot" > | | *Total time* | *3:00* | | | > |---+--------------+--------+------+------| > | 1 | development | 3:00 | | | > | 2 | task 1 | | 1:00 | | > | 2 | task 2 | | 1:00 | | > | 3 | task 2a | | | 1:00 | > > [4] #+BEGIN: clocktable :tags "must-mustnot" > | | *Total time* | *1:00* | | | > |---+--------------+--------+------+------| > | 1 | development | 1:00 | | | > | 2 | task 2 | | 1:00 | | > | 3 | task 2a | | | 1:00 | > > [5] #+BEGIN: clocktable :tags "must+mustnot" > | | *Total time* | *1:00* | | | > |---+--------------+--------+------+------| > | 1 | development | 1:00 | | | > | 2 | task 2 | | 1:00 | | > | 3 | task 2b | | | 1:00 | > > As you can see, in examples 2, 4, and 5, the time clocked on > "development" itself is being removed. Example 2 illustrates the effect > of tag inheritance. > > Adam
-rw-r--r--doc/org.texi1
-rwxr-xr-xlisp/ChangeLog4
-rwxr-xr-x[-rw-r--r--]lisp/org-clock.el59
3 files changed, 51 insertions, 13 deletions
diff --git a/doc/org.texi b/doc/org.texi
index 69e23e0..5c13e07 100644
--- a/doc/org.texi
+++ b/doc/org.texi
@@ -5582,6 +5582,7 @@ new table. The @samp{BEGIN} line can specify options:
:tend @r{A time string specifying when to stop considering times.}
:step @r{@code{week} or @code{day}, to split the table into chunks.}
@r{To use this, @code{:block} or @code{:tstart}, @code{:tend} are needed.}
+:tags @r{A tags match to select entries that should contribute}
:link @r{Link the item headlines in the table to their origins.}
:formula @r{Content of a @code{#+TBLFM} line to be added and evaluated.}
@r{As a special case, @samp{:formula %} adds a column with % time.}
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index ad6d7c6..ea730e3 100755
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,5 +1,9 @@
2010-04-23 Carsten Dominik <carsten.dominik@gmail.com>
+ * org-clock.el (org-clock-sum): New argument HEADLINE-FILTER.
+ (org-clock-sum): Add property to selected headlines.
+ (org-dblock-write:clocktable): Make tags matcher.
+
* org.el (org-set-autofill-regexps): XEmacs compatibility.
* org-latex.el (org-export-latex-set-initial-vars): Allow "-"
diff --git a/lisp/org-clock.el b/lisp/org-clock.el
index d1805e6..c8bf6cc 100644..100755
--- a/lisp/org-clock.el
+++ b/lisp/org-clock.el
@@ -1311,10 +1311,13 @@ With prefix arg SELECT, offer recently clocked tasks for selection."
"Holds the file total time in minutes, after a call to `org-clock-sum'.")
(make-variable-buffer-local 'org-clock-file-total-minutes)
-(defun org-clock-sum (&optional tstart tend)
+(defun org-clock-sum (&optional tstart tend headline-filter)
"Sum the times for each subtree.
Puts the resulting times in minutes as a text property on each headline.
-TSTART and TEND can mark a time range to be considered."
+TSTART and TEND can mark a time range to be considered. HEADLINE-FILTER is a
+zero-arg function that, if specified, is called for each headline in the time
+range with point at the headline. Headlines for which HEADLINE-FILTER returns
+nil are excluded from the clock summation."
(interactive)
(let* ((bmp (buffer-modified-p))
(re (concat "^\\(\\*+\\)[ \t]\\|^[ \t]*"
@@ -1330,7 +1333,9 @@ TSTART and TEND can mark a time range to be considered."
(if (stringp tend) (setq tend (org-time-string-to-seconds tend)))
(if (consp tstart) (setq tstart (org-float-time tstart)))
(if (consp tend) (setq tend (org-float-time tend)))
- (remove-text-properties (point-min) (point-max) '(:org-clock-minutes t))
+ (remove-text-properties (point-min) (point-max)
+ '(:org-clock-minutes t
+ :org-clock-force-headline-inclusion t))
(save-excursion
(goto-char (point-max))
(while (re-search-backward re nil t)
@@ -1359,15 +1364,34 @@ TSTART and TEND can mark a time range to be considered."
(let ((time (floor (- (org-float-time)
(org-float-time org-clock-start-time)) 60)))
(setq t1 (+ t1 time))))
- (setq level (- (match-end 1) (match-beginning 1)))
- (when (or (> t1 0) (> (aref ltimes level) 0))
- (loop for l from 0 to level do
- (aset ltimes l (+ (aref ltimes l) t1)))
- (setq t1 0 time (aref ltimes level))
- (loop for l from level to (1- lmax) do
- (aset ltimes l 0))
- (goto-char (match-beginning 0))
- (put-text-property (point) (point-at-eol) :org-clock-minutes time)))))
+ (let* ((headline-forced
+ (get-text-property (point)
+ :org-clock-force-headline-inclusion))
+ (headline-included
+ (or (null headline-filter)
+ (save-excursion
+ (save-match-data (funcall headline-filter))))))
+ (setq level (- (match-end 1) (match-beginning 1)))
+ (when (or (> t1 0) (> (aref ltimes level) 0))
+ (when (or headline-included headline-forced)
+ (if headline-included
+ (loop for l from 0 to level do
+ (aset ltimes l (+ (aref ltimes l) t1))))
+ (setq time (aref ltimes level))
+ (goto-char (match-beginning 0))
+ (put-text-property (point) (point-at-eol) :org-clock-minutes time)
+ (if headline-filter
+ (save-excursion
+ (save-match-data
+ (while
+ (> (funcall outline-level) 1)
+ (outline-up-heading 1 t)
+ (put-text-property
+ (point) (point-at-eol)
+ :org-clock-force-headline-inclusion t))))))
+ (setq t1 0)
+ (loop for l from level to (1- lmax) do
+ (aset ltimes l 0)))))))
(setq org-clock-file-total-minutes (aref ltimes 0)))
(set-buffer-modified-p bmp)))
@@ -1687,6 +1711,8 @@ the currently selected interval size."
(te (plist-get params :tend))
(block (plist-get params :block))
(link (plist-get params :link))
+ (tags (plist-get params :tags))
+ (matcher (if tags (cdr (org-make-tags-matcher tags))))
ipos time p level hlc hdl tsp props content recalc formula pcol
cc beg end pos tbl tbl1 range-text rm-file-column scope-is-list st)
(setq org-clock-file-total-minutes nil)
@@ -1768,7 +1794,14 @@ the currently selected interval size."
(goto-char pos)
(unless scope-is-list
- (org-clock-sum ts te)
+ (org-clock-sum ts te
+ (unless (null matcher)
+ (lambda ()
+ (let ((tags-list
+ (org-split-string
+ (or (org-entry-get (point) "ALLTAGS") "")
+ ":")))
+ (eval matcher)))))
(goto-char (point-min))
(setq st t)
(while (or (and (bobp) (prog1 st (setq st nil))