1 ;;; planner-report.el --- create a timely status report based on planner pages
3 ;; Copyright (C) 2004, 2006 Andrew J. Korty <ajk@iu.edu>
4 ;; Parts copyright (C) 2005 Free Software Foundation, Inc.
6 ;; Emacs Lisp Archive Entry
7 ;; Filename: planner-report.el
8 ;; Version: $Revision: 1.11 $
9 ;; Keywords: hypermedia
10 ;; Author: Andrew J. Korty <ajk@iu.edu>
11 ;; Maintainer: Andrew J. Korty <ajk@iu.edu>
12 ;; Description: Create a timely status report based on planner pages
13 ;; URL: http://www.plannerlove.com/
14 ;; Compatibility: Emacs21
16 ;; This file is part of Planner. It is not part of GNU Emacs.
18 ;; Planner is free software; you can redistribute it and/or modify it
19 ;; under the terms of the GNU General Public License as published by
20 ;; the Free Software Foundation; either version 2, or (at your option)
23 ;; Planner is distributed in the hope that it will be useful, but
24 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
25 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 ;; General Public License for more details.
28 ;; You should have received a copy of the GNU General Public License
29 ;; along with Planner; see the file COPYING. If not, write to the
30 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
31 ;; Boston, MA 02110-1301, USA.
35 ;; This library creates a status report for a given timespan. The
36 ;; report itself is just another Planner page in your planner
37 ;; directory. Once generated, it contains tasks and notes culled from
38 ;; active project pages. Tasks are only shown if they are incomplete
39 ;; or were completed within the timespan. Notes are shown if they
40 ;; were created during the timespan. Tasks and notes are grouped
41 ;; together under a heading for their corresponding project.
43 ;; The idea is you have one of these status reports generated
44 ;; periodically (say, every couple of weeks). Perhaps you use cron to
45 ;; run them automatically and then mail you a reminder that they've
46 ;; been done. Then you can edit the page, adding verbiage where it is
47 ;; needed and removing irrelevant items. This editing process is as
48 ;; easy as editing any other Planner page. Finally, you can publish
49 ;; the page along with the rest of your planner using M-x
50 ;; muse-project-publish.
52 ;; If you use planner-authz.el, you can tell planner-report.el only to
53 ;; consult project pages that a given list of users
54 ;; (planner-report-authz) can access when generating the report. For
55 ;; example, if you're preparing a status report for your boss, add
56 ;; yourself and him to planner-report-authz. The resulting status
57 ;; report will only contain information the two of you are supposed to
58 ;; have access to, and the report itself will be similarly restricted.
60 ;; * Startup and Usage
62 ;; Once this file is installed, add the following to your .emacs file:
64 ;; (require 'planner-report)
66 ;; Then you can use the following command to generate a status report:
68 ;; M-x planner-report-generate
70 ;; You will be prompted for a beginning and ending date, and then the
71 ;; status report will be generated. You can then edit it to your
72 ;; liking and publish it just like you would the rest of your planner.
76 ;; All user-serviceable options can be customized with
77 ;; M-x customize-group RET planner-report RET.
81 ;; Seth Falcon (sethfalcon AT gmail DOT com) helped port this to
84 ;; Yann Hodique helped port this to Muse.
86 ;; $Id: planner-report.el,v 1.11 2004/12/06 02:39:00 ajk Exp $
93 (defgroup planner-report nil
94 "A planner.el extension for generating timely status reports
95 based on planner pages."
97 :prefix
"planner-report")
99 (defcustom planner-report-authz nil
100 "List of users a status report should be restricted to.
101 When status reports are generated, only planner pages accessible
102 by these users will be consulted, and the resulting status report
103 will be similarly restricted."
104 :group
'planner-report
105 :type
'(repeat string
))
107 (defcustom planner-report-pretty-print-plan-pages t
108 "If non-nil, pretty print plan pages.
109 If nil, leave page names as-is.
110 This requires muse-wiki.el to work properly."
111 :group
'planner-report
114 (defcustom planner-report-remove-task-numbers t
115 "Remove task numbers when generating status reports."
116 :group
'planner-report
119 (defcustom planner-report-replace-note-numbers
"**"
120 "If non-nil, a string with which to replace note numbers when
121 generating status reports."
122 :group
'planner-report
125 (defcustom planner-report-unfinished-offset nil
126 "If non-nil, the offset in days from the current date of
127 unfinished tasks to include in the status report. If nil,
128 include all unfinished tasks."
129 :group
'planner-report
130 :type
'(choice (integer :tag
"Number of days")
131 (const :tag
"Include all unifinished tasks" nil
)))
133 (defvar planner-report-version
"$Revision: 1.11 $"
134 "Version of of planner-report.el.")
137 (defun planner-report-generate (begin end
)
138 "Generate a status report spanning a period from BEGIN to END.
139 BEGIN and END are in the format YYYY.MM.DD."
141 (let ((planner-expand-name-favor-future-p
142 (or planner-expand-name-favor-future-p
143 planner-task-dates-favor-future-p
)))
144 (list (planner-read-date "Start date")
145 (planner-read-date "End date"))))
146 (save-some-buffers nil
(lambda () (planner-derived-mode-p 'planner-mode
)))
147 (cd (planner-directory))
148 (let ((filename (concat "StatusReport" end
)))
149 (when (and muse-file-extension
(not (string= muse-file-extension
"")))
150 (setq filename
(concat filename
"." muse-file-extension
)))
152 (when planner-report-authz
153 (require 'planner-authz
)
155 (mapconcat 'identity planner-report-authz
" ") "\n"))
156 (insert "#title Status report for " begin
" to " end
"\n")
157 (let* ((unsorted-pages
158 (if planner-report-authz
159 (planner-authz-file-alist planner-report-authz
)
160 (planner-file-alist)))
162 (sort unsorted-pages
(lambda (a b
) (string< (car a
) (car b
)))))
166 ;; Add only project pages, and skip other status reports
167 (unless (or (string-match "^StatusReport" (caar pages
))
168 (string-match planner-date-regexp
(caar pages
)))
171 (insert-file-contents-literally (cdar pages
))
173 (planner-report-find-tasks (caar pages
) begin end
))
175 (planner-report-find-notes (caar pages
) begin end
)))))
176 ;; Insert a linked heading if we found anything
178 (insert "\n* [[" (caar pages
) "]["
179 (if (and planner-report-pretty-print-plan-pages
180 (fboundp 'muse-wiki-publish-pretty-title
))
181 (muse-wiki-publish-pretty-title (caar pages
))
185 (insert tasks
"\n\n")
190 (setq pages
(cdr pages
))))
191 (write-file filename t
))
192 (find-file filename
)))
194 (defun planner-report-find-notes (page begin end
)
195 "Find notes on PAGE that were created between BEGIN and END.
196 BEGIN and END are formatted as YYYY.MM.DD."
197 (goto-char (point-min))
199 (while (re-search-forward "^\\.#[0-9]+\\s-+" nil t
)
202 (planner-line-beginning-position)
204 ;; Find the end of this note (maybe EOF)
205 (re-search-forward "^\\(\\.#[0-9]+\\s-+\\|\\*\\*?\\s-+\\)"
207 (goto-char (planner-line-beginning-position))
209 (info (planner-current-note-info)))
211 (let* ((link (planner-note-link info
))
212 (date (if link
(planner-link-base link
))))
213 ;; Snarf if note is associated with a date that is in range
215 (not (string< date begin
))
216 (not (string< end date
))
218 (if planner-report-replace-note-numbers
220 (planner-replace-regexp-in-string
222 planner-report-replace-note-numbers
224 (setq result
(if result
(concat note result
) note
))))))))
227 (defun planner-report-find-tasks (page begin end
)
228 "Find cancelled or completed tasks on PAGE with a date between
229 BEGIN and END and any unfinished tasks with a date constrained by
230 `planner-report-unfinished-offset'. BEGIN and END are formatted
232 (goto-char (point-min))
234 (while (re-search-forward "^#[A-C]" nil t
)
235 (let* ((task (buffer-substring (planner-line-beginning-position)
236 (planner-line-end-position)))
237 (info (planner-task-info-from-string page task
)))
239 (let ((date (planner-task-date info
)))
240 ;; If the task isn't cancelled nor completed and has a
241 ;; date less than or equal to planner-report-unfinished
242 ;; away, snarf. If it has been cancelled or completed and
243 ;; the date is in range, snarf.
245 (or (and (not (or (equal (planner-task-status info
) "C")
246 (equal (planner-task-status info
) "X")))
247 (or (null planner-report-unfinished-offset
)
249 (planner-calculate-date-from-day-offset
250 (planner-date-to-filename
251 (decode-time (current-time)))
252 planner-report-unfinished-offset
)
254 (and (not (string< date begin
))
255 (not (string< end date
))))
257 (if planner-report-remove-task-numbers
258 (setq task
(planner-replace-regexp-in-string
259 "^\\(#[A-C]\\)\\([0-9]+ +\\)"
262 (if result
(concat result
"\n" task
) task
))))))))
265 (provide 'planner-report
)
267 ;;; planner-report.el ends here