Implement TODO statistics.

This uses the same cookies as Checkbox statistics, [%] and [/]
This commit is contained in:
Carsten Dominik 2008-05-19 14:11:47 +02:00
parent 24fd4650de
commit 2c0812caf1
5 changed files with 124 additions and 11 deletions

View File

@ -9,9 +9,40 @@
#+LINK_HOME: http://orgmode.org
* Version 6.04
:PROPERTIES:
:VISIBILITY: content
:END:
** Details
*** Statistics for TODO entries
The [/] and [%] cookies have already provided statistics for
checkboxes. Now they do the same also for TODO entries. So if a
headline contains either cookie, changing the TODO state of any
direct child will trigger an update of this cookie. Children
that are neither TODO nor DONE are ignored.
There have already been requests to automatically switch the
parent headline to DONE when all children are done. I am not
makeing this a default feature, because one needs to make many
decisions about which keyword to use etc. Instead of a complex
customization variable, I am providing a hook that can be used.
This hook will be called each time a TODO statistics cookie is
updated, with the cursor in the corresponding line. Each
function in the hook will receive two arguments, the number of
done entries, and the number of not-done entries. Here is a
example implementation:
#+begin_src emacs-lisp
(defun org-summary-todo (n-done n-not-done)
"Switch entry to DONE when all subentries are done, to TODO otherwise."
(let (org-log-done org-log-states) ; turn off logging
(org-todo (if (= n-not-done 0) "DONE" "TODO"))))
(add-hook 'org-after-todo-statistics-hook 'org-summary-todo)
#+end_src
*** iCalendar now defines proper UIDs for entries
This is necessary for synchronization services. The UIDs are
@ -36,9 +67,6 @@ but a synchronization program can still figure out from which
entry all the different instances originate.
* Version 6.03
:PROPERTIES:
:VISIBILITY: content
:END:
** Overview

View File

@ -3171,12 +3171,37 @@ priority):
@cindex tasks, breaking down
It is often advisable to break down large tasks into smaller, manageable
subtasks. You can do this by creating an outline tree below a TODO
item, with detailed subtasks on the tree@footnote{To keep subtasks out
of the global TODO list, see the
@code{org-agenda-todo-list-sublevels}.}. Another possibility is the use
of checkboxes to identify (a hierarchy of) a large number of subtasks
(@pxref{Checkboxes}).
subtasks. You can do this by creating an outline tree below a TODO item,
with detailed subtasks on the tree@footnote{To keep subtasks out of the
global TODO list, see the @code{org-agenda-todo-list-sublevels}.}. To keep
the overview over the fraction of subtasks that are already completed, insert
either @samp{[/]} or @samp{[%]} anywhere in the headline. These cookies will
be updates each time the todo status of a child changes. For example:
@example
* Organize Party [33%]
** TODO Call people [1/2]
*** TODO Peter
*** DONE Sarah
** TODO Buy food
** DONE Talk to neighbor
@end example
If you would like a TODO entry to automatically change to DONE when all
chilrden are done, you can use the following setup:
@example
(defun org-summary-todo (n-done n-not-done)
"Switch entry to DONE when all subentries are done, to TODO otherwise."
(let (org-log-done org-log-states) ; turn off logging
(org-todo (if (= n-not-done 0) "DONE" "TODO"))))
(add-hook 'org-after-todo-statistics-hook 'org-summary-todo)
@end example
Another possibility is the use of checkboxes to identify (a hierarchy of) a
large number of subtasks (@pxref{Checkboxes}).
@node Checkboxes, , Breaking down tasks, TODO Items

View File

@ -1,5 +1,7 @@
2008-05-19 Carsten Dominik <dominik@science.uva.nl>
* org.el (org-update-parent-todo-statistics): New function.
* org-exp.el (org-icalendar-store-UID): New option.
(org-icalendar-force-UID): Option removed.
(org-print-icalendar-entries): IMplement UIDs.

View File

@ -3860,7 +3860,7 @@ END:VEVENT\n"
UID: %s
%s
SUMMARY:%s%s%s
CATEGORIES:%s%s
CATEGORIES:%s
SEQUENCE:1
PRIORITY:%d
STATUS:%s

View File

@ -174,6 +174,7 @@ to add the symbol `xyz', and the package must have a call to
(const :tag " mouse: Additional mouse support" org-mouse)
(const :tag "C annotate-file: Annotate a file with org syntax" org-annotate-file)
(const :tag "C annotation-helper: Call Remeber directly from Browser" org-annotation-helper)
(const :tag "C bookmark: Org links to bookmarks" org-bookmark)
(const :tag "C depend: TODO dependencies for Org-mode" org-depend)
(const :tag "C elisp-symbol: Org links to emacs-lisp symbols" org-elisp-symbol)
@ -1388,6 +1389,13 @@ by a letter in parenthesis, like TODO(t)."
(const :tag "By default" t)
(const :tag "Only with C-u C-c C-t" prefix)))
(defcustom org-provide-todo-statistics t
"Non-nil means, update todo statistics after insert and toggle.
When this is set, todo statistics is updated in the parent of the current
entry each time a todo state is changed."
:group 'org-todo
:type 'boolean)
(defcustom org-after-todo-state-change-hook nil
"Hook which is run after the state of a TODO item was changed.
The new state (a string with a TODO keyword, or nil) is available in the
@ -2367,6 +2375,7 @@ If TABLE-TYPE is non-nil, also check for table.el-type tables."
org-replace-region-by-html org-export-region-as-html
org-export-as-html org-export-icalendar-this-file
org-export-icalendar-all-agenda-files
org-table-clean-before-export
org-export-icalendar-combine-agenda-files org-export-as-xoxo)))
;; Declare and autoload functions from org-exp.el
@ -4546,7 +4555,9 @@ state (TODO by default). Also with prefix arg, force first state."
(not (match-beginning 2))
(member (match-string 2) org-done-keywords))
(insert (car org-todo-keywords-1) " ")
(insert (match-string 2) " "))))
(insert (match-string 2) " "))
(when org-provide-todo-statistics
(org-update-parent-todo-statistics))))
(defun org-insert-subheading (arg)
"Insert a new subheading and demote it.
@ -8186,6 +8197,8 @@ For calling through lisp, arg is also interpreted in the following way:
(org-add-log-setup 'state state 'findpos dolog)))
;; Fixup tag positioning
(and org-auto-align-tags (not org-setting-tags) (org-set-tags nil t))
(when org-provide-todo-statistics
(org-update-parent-todo-statistics))
(run-hooks 'org-after-todo-state-change-hook)
(if (and arg (not (member state org-done-keywords)))
(setq head (org-get-todo-sequence-head state)))
@ -8205,6 +8218,51 @@ For calling through lisp, arg is also interpreted in the following way:
(save-excursion
(run-hook-with-args 'org-trigger-hook change-plist)))))))
(defun org-update-parent-todo-statistics ()
"Update any statistics cookie in the parent of the current headline."
(interactive)
(let ((box-re "\\(\\(\\[[0-9]*%\\]\\)\\|\\(\\[[0-9]*/[0-9]*\\]\\)\\)")
level (cnt-all 0) (cnt-done 0) is-percent kwd)
(catch 'exit
(save-excursion
(setq level (org-up-heading-safe))
(unless (and level
(re-search-forward box-re (point-at-eol) t))
(throw 'exit nil))
(setq is-percent (match-end 2))
(save-match-data
(unless (outline-next-heading) (throw 'exit nil))
(while (looking-at org-todo-line-regexp)
(setq kwd (match-string 2))
(and kwd (setq cnt-all (1+ cnt-all)))
(and (member kwd org-done-keywords)
(setq cnt-done (1+ cnt-done)))
(condition-case nil
(outline-forward-same-level 1)
(error (end-of-line 1)))))
(replace-match
(if is-percent
(format "[%d%%]" (/ (* 100 cnt-done) (max 1 cnt-all)))
(format "[%d/%d]" cnt-done cnt-all)))
(run-hook-with-args 'org-after-todo-statistics-hook
cnt-done (- cnt-all cnt-done))))))
(defvar org-after-todo-statistics-hook nil
"Hook that is called after a TODO statistics cookie has been updated.
Each function is called with two arguments: the number of not-done entries
and the number of done entries.
For example, the following function, when added to this hook, will switch
an entry to DONE when all children are done, and back to TODO when new
entries are set to a TODO status. Note that this hook is only called
when there is a statistics cookie in the headline!
(defun org-summary-todo (n-done n-not-done)
\"Switch entry to DONE when all subentries are done, to TODO otherwise.\"
(let (org-log-done org-log-states) ; turn off logging
(org-todo (if (= n-not-done 0) \"DONE\" \"TODO\"))))
")
(defun org-local-logging (value)
"Get logging settings from a property VALUE."
(let* (words w a)