1 ;;; planner-timeclock-summary-proj.el --- timeclock project for the Emacs planner
3 ;; Copyright (C) 2004 Pascal Quesseveur
4 ;; Parts copyright (C) 2005 Free Software Foundation, Inc.
5 ;; Parts copyright (C) 2005 Trent Buck
7 ;; Author: Pascal Quesseveur <quesseveur@abaksystemes.fr>
8 ;; Time-stamp: <2004-12-17 18:39>
9 ;; Description: Summary timeclock of a project
11 ;; This file is part of Planner. It is not part of GNU Emacs.
13 ;; Planner is free software; you can redistribute it and/or modify it
14 ;; under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
18 ;; Planner is distributed in the hope that it will be useful, but
19 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 ;; General Public License for more details.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with Planner; see the file COPYING. If not, write to the
25 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 ;; Boston, MA 02110-1301, USA.
30 ;; planner-timeclock-summary-proj.el produces timeclock reports for planner
31 ;; files. That package uses `timeclock-project-alist' and
32 ;; `timeclock-seconds-to-string' from timeclock.
34 ;; To use, call `planner-timeclock-summary-proj-current' from a
35 ;; project page. The report is inserted at the current position in the
36 ;; buffer. The function `planner-timeclock-summary-proj-section' does
37 ;; the same but the report is inserted inside a section called "*
40 ;; You might want to add
42 ;; (planner-timeclock-summary-proj-insinuate)
44 ;; to your planner config, which will cause the timeclock summary to
45 ;; be updated every time a Planner file is written.
49 ;; Trent Buck overhauled the code for Planner-Muse.
53 (require 'planner-timeclock
)
54 (require 'planner-timeclock-summary
)
59 (defcustom planner-timeclock-workdays-per-week
5
60 "Number of work days per week."
62 :group
'planner-timeclock-summary
)
64 (defcustom planner-timeclock-workhours-per-day
8
65 "Number of work hours per day."
67 :group
'planner-timeclock-summary
)
69 (defcustom planner-timeclock-summary-proj-header
"Report"
70 "Header for the timeclock summary project section in a plan page."
72 :group
'planner-timeclock-summary
)
74 ;;;; User variables stop here
76 (defun planner-timeclock-summary-proj-section ()
77 "Update the secion corresponding with `planner-timeclock-summary-proj-header'
78 in the current plan page.
79 The section is updated only if it exists."
83 (when (planner-narrow-to-section planner-timeclock-summary-proj-header
)
84 (let ((thepage (planner-page-name)))
86 ;; Don't edit unless we're in a task page.
87 (not (string-match planner-date-regexp thepage
)))
88 (delete-region (point-min) (point-max))
89 (insert "* " planner-timeclock-summary-proj-header
"\n")
90 (planner-timeclock-summary-proj-report thepage
)
94 (defun planner-timeclock-summary-proj-all ()
95 "Insert time report for all projects in the current buffer."
97 (planner-timeclock-summary-proj-report nil
))
100 (defun planner-timeclock-summary-proj-current ()
101 "Insert time report for the current project in the current buffer."
103 (let ((project (planner-page-name)))
104 (planner-timeclock-summary-proj-report project
)))
107 (defun planner-timeclock-summary-proj-report (project)
108 "Insert time report for PROJECT in the current buffer."
109 (interactive "sProject: ")
110 (insert (planner-timeclock-proj-build-report
111 (planner-timeclock-proj-make-alist project
))))
113 (defun planner-timeclock-proj-build-report (proj-alist)
114 "Return time report for PROJ-ALIST.
115 The time report is formatted as
121 (let ((str "\n Duration || Task")
124 (let* ((proj-entry (car proj-alist
))
125 (duration (cdr proj-entry
)))
128 (format "%18s" (planner-timeclock-proj-seconds-to-string
131 (format "%s" (car proj-entry
))))
132 (setq total-duration
(+ duration total-duration
))
133 (setq proj-alist
(cdr proj-alist
))))
136 (format "%18s" (planner-timeclock-proj-seconds-to-string
141 (defun planner-timeclock-proj-make-alist (proj-name)
142 "Return an association list for PROJ-NAME.
143 Each association is of the form (TASK . DURATION). TASK is a task
144 name defined inside PROJ-NAME and DURATION is the total time
145 computed for that task. When PROJ-NAME is nil, each TASK is a
146 project name, and DURATION is the time spent on that project."
147 (let ((projects (planner-timeclock-proj-entries proj-name
))
149 ;; Looping on project data. The project is made of tasks, and for each
150 ;; task there can be several time intervals.
152 (let* ((entry (car projects
))
154 (task-data (cdr entry
))
156 ;; We compute the time spent on task TASK
159 (let ((task-entry (car task-data
)))
161 (setq task-time
(+ task-time
162 (timeclock-entry-length task-entry
)))
163 (setq task-data
(cdr task-data
)))))
165 (if (string-match ": *" task
)
166 (if (and (< (match-end 0) (length task
)) proj-name
)
167 (setq task
(substring task
(match-end 0)))
168 (setq task
(substring task
0 (match-beginning 0)))))
169 ;; record the cons (task . time)
172 (proj-data-cell (assoc task proj-alist
)))
175 (setq proj-time
(cdr proj-data-cell
))
176 (setcdr proj-data-cell
(+ task-time proj-time
)))
177 (add-to-list 'proj-alist
(cons task task-time
))))
178 (setq proj-alist
(list (cons task task-time
))))
179 (setq projects
(cdr projects
))))
182 (defun planner-timeclock-proj-entries (proj-name)
183 "Return entries from `timeclock-project-alist' for PROJ-NAME.
184 If PROJ-NAME is nil, return `timeclock-project-alist'."
186 (entry-list (timeclock-project-alist)))
187 ;; Looping on entries. Each entry is in the form (PROJECT (TASKS
188 ;; DATA)). We keep only entries for witch PROJECT-NAME matches
193 (let* ((proj (car entry-list
))
194 (proj-entry-name (car proj
)))
196 (string-match (concat "^\\[\\[" proj-name
"\\]\\]")
199 (add-to-list 'projects proj
)
200 (setq projects
(list proj
))))
201 (setq entry-list
(cdr entry-list
))))
204 (defun planner-timeclock-summary-proj-insinuate ()
205 "Insinuate planner-timeclock-summary-proj with the rest of Planner."
206 (add-hook 'planner-mode-hook
209 (if (boundp 'write-file-functions
)
210 'write-file-functions
212 'planner-timeclock-summary-proj-section nil t
))))
214 (defun planner-timeclock-proj-seconds-to-string (seconds)
215 "Convert the floating point number SECONDS to a string.
216 The string is in the form [WWw] [DDd] hh:ss."
217 (let* ((workday (* planner-timeclock-workhours-per-day
3600))
218 (days (floor (/ seconds workday
)))
219 (secs (floor (- seconds
(* days workday
)))))
220 (if (> days planner-timeclock-workdays-per-week
)
221 (let ((weeks (/ days planner-timeclock-workdays-per-week
))
222 (dys (% days planner-timeclock-workdays-per-week
)))
224 (format "%dw %dd %s" weeks dys
225 (timeclock-seconds-to-string secs
))
226 (format "%dw %s" weeks
227 (timeclock-seconds-to-string secs
))))
229 (format "%dd %s" days
230 (timeclock-seconds-to-string secs
))
231 (format "%s" (timeclock-seconds-to-string secs
))))))
233 (provide 'planner-timeclock-summary-proj
)
235 ;;; planner-timeclock-summary-proj.el ends here