aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2018-10-08 22:33:22 -0400
committerStefan Monnier2018-10-08 22:33:22 -0400
commit333f0bfe766185c66952c6fbd4796c6bb97c868d (patch)
tree0fc63cae1bac3c940ba388ea73522c96d8093c8c
parentcf1ebfa055fcd0749aa4ed2fc4c399470b9eb3de (diff)
downloademacs-333f0bfe766185c66952c6fbd4796c6bb97c868d.tar.gz
emacs-333f0bfe766185c66952c6fbd4796c6bb97c868d.zip
* lisp/calendar/timeclock.el: Use lexical-binding
Require cl-lib. Remove redundant :group args. (timeclock-status-string): Avoid 'setq'. (timeclock-ask-for-project, timeclock-ask-for-reason): Completionu tables can be simple lists of strings. (timeclock-read-moment): Doesn't deserve to be defsubst (most of the others don't either, admittedly). (timeclock-entry): New type. (timeclock-entry-begin, timeclock-entry-end, timeclock-entry-project) (timeclock-entry-comment): Define via 'cl-defstruct'. (timeclock-entry-list-projects, timeclock-day-list-projects): Avoid add-to-list on lexical vars. (timeclock-day-list): Use 'push'. (timeclock-log-data): Use 'pcase'. (timeclock-mean): Simplify. (timeclock-generate-report): Use dotimes.
-rw-r--r--lisp/calendar/timeclock.el344
1 files changed, 153 insertions, 191 deletions
diff --git a/lisp/calendar/timeclock.el b/lisp/calendar/timeclock.el
index ddc297604ec..646f5298fe4 100644
--- a/lisp/calendar/timeclock.el
+++ b/lisp/calendar/timeclock.el
@@ -1,4 +1,4 @@
1;;; timeclock.el --- mode for keeping track of how much you work 1;;; timeclock.el --- mode for keeping track of how much you work -*- lexical-binding:t -*-
2 2
3;; Copyright (C) 1999-2018 Free Software Foundation, Inc. 3;; Copyright (C) 1999-2018 Free Software Foundation, Inc.
4 4
@@ -62,7 +62,7 @@
62;; `timeclock-ask-before-exiting' to t using M-x customize (this is 62;; `timeclock-ask-before-exiting' to t using M-x customize (this is
63;; the default), or by adding the following to your init file: 63;; the default), or by adding the following to your init file:
64;; 64;;
65;; (add-hook 'kill-emacs-query-functions 'timeclock-query-out) 65;; (add-hook 'kill-emacs-query-functions #'timeclock-query-out)
66 66
67;; NOTE: If you change your timelog file without using timeclock's 67;; NOTE: If you change your timelog file without using timeclock's
68;; functions, or if you change the value of any of timeclock's 68;; functions, or if you change the value of any of timeclock's
@@ -75,6 +75,8 @@
75 75
76;;; Code: 76;;; Code:
77 77
78(require 'cl-lib)
79
78(defgroup timeclock nil 80(defgroup timeclock nil
79 "Keeping track of the time that gets spent." 81 "Keeping track of the time that gets spent."
80 :group 'data) 82 :group 'data)
@@ -84,13 +86,11 @@
84(defcustom timeclock-file (locate-user-emacs-file "timelog" ".timelog") 86(defcustom timeclock-file (locate-user-emacs-file "timelog" ".timelog")
85 "The file used to store timeclock data in." 87 "The file used to store timeclock data in."
86 :version "24.4" ; added locate-user-emacs-file 88 :version "24.4" ; added locate-user-emacs-file
87 :type 'file 89 :type 'file)
88 :group 'timeclock)
89 90
90(defcustom timeclock-workday (* 8 60 60) 91(defcustom timeclock-workday (* 8 60 60)
91 "The length of a work period in seconds." 92 "The length of a work period in seconds."
92 :type 'integer 93 :type 'integer)
93 :group 'timeclock)
94 94
95(defcustom timeclock-relative t 95(defcustom timeclock-relative t
96 "Whether to make reported time relative to `timeclock-workday'. 96 "Whether to make reported time relative to `timeclock-workday'.
@@ -100,24 +100,21 @@ Tuesday is twelve hours -- relative to an averaged work period of
100eight hours -- or eight hours, non-relative. So relative time takes 100eight hours -- or eight hours, non-relative. So relative time takes
101into account any discrepancy of time under-worked or over-worked on 101into account any discrepancy of time under-worked or over-worked on
102previous days. This only affects the timeclock mode line display." 102previous days. This only affects the timeclock mode line display."
103 :type 'boolean 103 :type 'boolean)
104 :group 'timeclock)
105 104
106(defcustom timeclock-get-project-function 'timeclock-ask-for-project 105(defcustom timeclock-get-project-function 'timeclock-ask-for-project
107 "The function used to determine the name of the current project. 106 "The function used to determine the name of the current project.
108When clocking in, and no project is specified, this function will be 107When clocking in, and no project is specified, this function will be
109called to determine what is the current project to be worked on. 108called to determine what is the current project to be worked on.
110If this variable is nil, no questions will be asked." 109If this variable is nil, no questions will be asked."
111 :type 'function 110 :type 'function)
112 :group 'timeclock)
113 111
114(defcustom timeclock-get-reason-function 'timeclock-ask-for-reason 112(defcustom timeclock-get-reason-function 'timeclock-ask-for-reason
115 "A function used to determine the reason for clocking out. 113 "A function used to determine the reason for clocking out.
116When clocking out, and no reason is specified, this function will be 114When clocking out, and no reason is specified, this function will be
117called to determine what is the reason. 115called to determine what is the reason.
118If this variable is nil, no questions will be asked." 116If this variable is nil, no questions will be asked."
119 :type 'function 117 :type 'function)
120 :group 'timeclock)
121 118
122(defcustom timeclock-get-workday-function nil 119(defcustom timeclock-get-workday-function nil
123 "A function used to determine the length of today's workday. 120 "A function used to determine the length of today's workday.
@@ -127,19 +124,17 @@ the return value is nil, or equal to `timeclock-workday', nothing special
127will be done. If it is a quantity different from `timeclock-workday', 124will be done. If it is a quantity different from `timeclock-workday',
128however, a record will be output to the timelog file to note the fact that 125however, a record will be output to the timelog file to note the fact that
129that day has a length that is different from the norm." 126that day has a length that is different from the norm."
130 :type '(choice (const nil) function) 127 :type '(choice (const nil) function))
131 :group 'timeclock)
132 128
133(defcustom timeclock-ask-before-exiting t 129(defcustom timeclock-ask-before-exiting t
134 "If non-nil, ask if the user wants to clock out before exiting Emacs. 130 "If non-nil, ask if the user wants to clock out before exiting Emacs.
135This variable only has effect if set with \\[customize]." 131This variable only has effect if set with \\[customize]."
136 :set (lambda (symbol value) 132 :set (lambda (symbol value)
137 (if value 133 (if value
138 (add-hook 'kill-emacs-query-functions 'timeclock-query-out) 134 (add-hook 'kill-emacs-query-functions #'timeclock-query-out)
139 (remove-hook 'kill-emacs-query-functions 'timeclock-query-out)) 135 (remove-hook 'kill-emacs-query-functions #'timeclock-query-out))
140 (set symbol value)) 136 (set symbol value))
141 :type 'boolean 137 :type 'boolean)
142 :group 'timeclock)
143 138
144(defvar timeclock-update-timer nil 139(defvar timeclock-update-timer nil
145 "The timer used to update `timeclock-mode-string'.") 140 "The timer used to update `timeclock-mode-string'.")
@@ -172,7 +167,7 @@ a positive argument to force an update."
172 (if (and currently-displaying 167 (if (and currently-displaying
173 (or (and value 168 (or (and value
174 (boundp 'display-time-hook) 169 (boundp 'display-time-hook)
175 (memq 'timeclock-update-mode-line 170 (memq #'timeclock-update-mode-line
176 display-time-hook)) 171 display-time-hook))
177 (and (not value) 172 (and (not value)
178 timeclock-update-timer))) 173 timeclock-update-timer)))
@@ -185,7 +180,6 @@ a positive argument to force an update."
185 ;; FIXME: The return value isn't used, AFAIK! 180 ;; FIXME: The return value isn't used, AFAIK!
186 value)) 181 value))
187 :type 'boolean 182 :type 'boolean
188 :group 'timeclock
189 :require 'time) 183 :require 'time)
190 184
191(defcustom timeclock-first-in-hook nil 185(defcustom timeclock-first-in-hook nil
@@ -194,40 +188,33 @@ Note that this hook is run before recording any events. Thus the
194value of `timeclock-hours-today', `timeclock-last-event' and the 188value of `timeclock-hours-today', `timeclock-last-event' and the
195return value of function `timeclock-last-period' are relative previous 189return value of function `timeclock-last-period' are relative previous
196to today." 190to today."
197 :type 'hook 191 :type 'hook)
198 :group 'timeclock)
199 192
200(defcustom timeclock-load-hook nil 193(defcustom timeclock-load-hook nil
201 "Hook that gets run after timeclock has been loaded." 194 "Hook that gets run after timeclock has been loaded."
202 :type 'hook 195 :type 'hook)
203 :group 'timeclock)
204 196
205(defcustom timeclock-in-hook nil 197(defcustom timeclock-in-hook nil
206 "A hook run every time an \"in\" event is recorded." 198 "A hook run every time an \"in\" event is recorded."
207 :type 'hook 199 :type 'hook)
208 :group 'timeclock)
209 200
210(defcustom timeclock-day-over-hook nil 201(defcustom timeclock-day-over-hook nil
211 "A hook that is run when the workday has been completed. 202 "A hook that is run when the workday has been completed.
212This hook is only run if the current time remaining is being displayed 203This hook is only run if the current time remaining is being displayed
213in the mode line. See the variable `timeclock-mode-line-display'." 204in the mode line. See the variable `timeclock-mode-line-display'."
214 :type 'hook 205 :type 'hook)
215 :group 'timeclock)
216 206
217(defcustom timeclock-out-hook nil 207(defcustom timeclock-out-hook nil
218 "A hook run every time an \"out\" event is recorded." 208 "A hook run every time an \"out\" event is recorded."
219 :type 'hook 209 :type 'hook)
220 :group 'timeclock)
221 210
222(defcustom timeclock-done-hook nil 211(defcustom timeclock-done-hook nil
223 "A hook run every time a project is marked as completed." 212 "A hook run every time a project is marked as completed."
224 :type 'hook 213 :type 'hook)
225 :group 'timeclock)
226 214
227(defcustom timeclock-event-hook nil 215(defcustom timeclock-event-hook nil
228 "A hook run every time any event is recorded." 216 "A hook run every time any event is recorded."
229 :type 'hook 217 :type 'hook)
230 :group 'timeclock)
231 218
232(defvar timeclock-last-event nil 219(defvar timeclock-last-event nil
233 "A list containing the last event that was recorded. 220 "A list containing the last event that was recorded.
@@ -294,12 +281,12 @@ display (non-nil means on)."
294 (or (memq 'timeclock-mode-string global-mode-string) 281 (or (memq 'timeclock-mode-string global-mode-string)
295 (setq global-mode-string 282 (setq global-mode-string
296 (append global-mode-string '(timeclock-mode-string)))) 283 (append global-mode-string '(timeclock-mode-string))))
297 (add-hook 'timeclock-event-hook 'timeclock-update-mode-line) 284 (add-hook 'timeclock-event-hook #'timeclock-update-mode-line)
298 (when timeclock-update-timer 285 (when timeclock-update-timer
299 (cancel-timer timeclock-update-timer) 286 (cancel-timer timeclock-update-timer)
300 (setq timeclock-update-timer nil)) 287 (setq timeclock-update-timer nil))
301 (if (boundp 'display-time-hook) 288 (if (boundp 'display-time-hook)
302 (remove-hook 'display-time-hook 'timeclock-update-mode-line)) 289 (remove-hook 'display-time-hook #'timeclock-update-mode-line))
303 (if timeclock-use-display-time 290 (if timeclock-use-display-time
304 (progn 291 (progn
305 ;; Update immediately so there is a visible change 292 ;; Update immediately so there is a visible change
@@ -308,15 +295,15 @@ display (non-nil means on)."
308 (timeclock-update-mode-line) 295 (timeclock-update-mode-line)
309 (message "Activate `display-time-mode' or turn off \ 296 (message "Activate `display-time-mode' or turn off \
310`timeclock-use-display-time' to see timeclock information")) 297`timeclock-use-display-time' to see timeclock information"))
311 (add-hook 'display-time-hook 'timeclock-update-mode-line)) 298 (add-hook 'display-time-hook #'timeclock-update-mode-line))
312 (setq timeclock-update-timer 299 (setq timeclock-update-timer
313 (run-at-time nil 60 'timeclock-update-mode-line)))) 300 (run-at-time nil 60 'timeclock-update-mode-line))))
314 (setq global-mode-string 301 (setq global-mode-string
315 (delq 'timeclock-mode-string global-mode-string)) 302 (delq 'timeclock-mode-string global-mode-string))
316 (remove-hook 'timeclock-event-hook 'timeclock-update-mode-line) 303 (remove-hook 'timeclock-event-hook #'timeclock-update-mode-line)
317 (if (boundp 'display-time-hook) 304 (if (boundp 'display-time-hook)
318 (remove-hook 'display-time-hook 305 (remove-hook 'display-time-hook
319 'timeclock-update-mode-line)) 306 #'timeclock-update-mode-line))
320 (when timeclock-update-timer 307 (when timeclock-update-timer
321 (cancel-timer timeclock-update-timer) 308 (cancel-timer timeclock-update-timer)
322 (setq timeclock-update-timer nil)))) 309 (setq timeclock-update-timer nil))))
@@ -365,7 +352,8 @@ discover the name of the project."
365 (if (not (= workday timeclock-workday)) 352 (if (not (= workday timeclock-workday))
366 (timeclock-log "h" (number-to-string 353 (timeclock-log "h" (number-to-string
367 (/ workday (if (zerop (% workday (* 60 60))) 354 (/ workday (if (zerop (% workday (* 60 60)))
368 60 60.0) 60)))))) 355 60 60.0)
356 60))))))
369 (timeclock-log "i" (or project 357 (timeclock-log "i" (or project
370 (and timeclock-get-project-function 358 (and timeclock-get-project-function
371 (or find-project 359 (or find-project
@@ -417,12 +405,11 @@ If SHOW-SECONDS is non-nil, display second resolution.
417If TODAY-ONLY is non-nil, the display will be relative only to time 405If TODAY-ONLY is non-nil, the display will be relative only to time
418worked today, ignoring the time worked on previous days." 406worked today, ignoring the time worked on previous days."
419 (interactive "P") 407 (interactive "P")
420 (let ((remainder (timeclock-workday-remaining 408 (let* ((remainder (timeclock-workday-remaining
421 (or today-only 409 (or today-only
422 (not timeclock-relative)))) 410 (not timeclock-relative))))
423 (last-in (equal (car timeclock-last-event) "i")) 411 (last-in (equal (car timeclock-last-event) "i"))
424 status) 412 (status
425 (setq status
426 (format "Currently %s since %s (%s), %s %s, leave at %s" 413 (format "Currently %s since %s (%s), %s %s, leave at %s"
427 (if last-in "IN" "OUT") 414 (if last-in "IN" "OUT")
428 (if show-seconds 415 (if show-seconds
@@ -435,7 +422,7 @@ worked today, ignoring the time worked on previous days."
435 (timeclock-seconds-to-string remainder show-seconds t) 422 (timeclock-seconds-to-string remainder show-seconds t)
436 (if (> remainder 0) 423 (if (> remainder 0)
437 "remaining" "over") 424 "remaining" "over")
438 (timeclock-when-to-leave-string show-seconds today-only))) 425 (timeclock-when-to-leave-string show-seconds today-only))))
439 (if (called-interactively-p 'interactive) 426 (if (called-interactively-p 'interactive)
440 (message "%s" status) 427 (message "%s" status)
441 status))) 428 status)))
@@ -623,7 +610,7 @@ arguments of `completing-read'."
623 (format "Clock into which project (default %s): " 610 (format "Clock into which project (default %s): "
624 (or timeclock-last-project 611 (or timeclock-last-project
625 (car timeclock-project-list))) 612 (car timeclock-project-list)))
626 (mapcar 'list timeclock-project-list) 613 timeclock-project-list
627 (or timeclock-last-project 614 (or timeclock-last-project
628 (car timeclock-project-list)))) 615 (car timeclock-project-list))))
629 616
@@ -632,7 +619,7 @@ arguments of `completing-read'."
632(defun timeclock-ask-for-reason () 619(defun timeclock-ask-for-reason ()
633 "Ask the user for the reason they are clocking out." 620 "Ask the user for the reason they are clocking out."
634 (timeclock-completing-read "Reason for clocking out: " 621 (timeclock-completing-read "Reason for clocking out: "
635 (mapcar 'list timeclock-reason-list))) 622 timeclock-reason-list))
636 623
637(define-obsolete-function-alias 'timeclock-update-modeline 624(define-obsolete-function-alias 'timeclock-update-modeline
638 'timeclock-update-mode-line "24.3") 625 'timeclock-update-mode-line "24.3")
@@ -700,7 +687,7 @@ being logged for. Normally only \"in\" events specify a project."
700 "\\([0-9]+\\)/\\([0-9]+\\)/\\([0-9]+\\)\\s-+" 687 "\\([0-9]+\\)/\\([0-9]+\\)/\\([0-9]+\\)\\s-+"
701 "\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)[ \t]*" "\\([^\n]*\\)")) 688 "\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)[ \t]*" "\\([^\n]*\\)"))
702 689
703(defsubst timeclock-read-moment () 690(defun timeclock-read-moment ()
704 "Read the moment under point from the timelog." 691 "Read the moment under point from the timelog."
705 (if (looking-at timeclock-moment-regexp) 692 (if (looking-at timeclock-moment-regexp)
706 (let ((code (match-string 1)) 693 (let ((code (match-string 1))
@@ -725,27 +712,19 @@ This is only provided for coherency when used by
725 (float-time (cadr timeclock-last-event))) 712 (float-time (cadr timeclock-last-event)))
726 timeclock-last-period)) 713 timeclock-last-period))
727 714
715(cl-defstruct (timeclock-entry
716 (:constructor nil) (:copier nil)
717 (:type list))
718 begin end project comment
719 ;; FIXME: Documented in docstring of timeclock-log-data, but I can't see
720 ;; where it's used in the code.
721 final-p)
722
728(defsubst timeclock-entry-length (entry) 723(defsubst timeclock-entry-length (entry)
729 "Return the length of ENTRY in seconds." 724 "Return the length of ENTRY in seconds."
730 (- (float-time (cadr entry)) 725 (- (float-time (cadr entry))
731 (float-time (car entry)))) 726 (float-time (car entry))))
732 727
733(defsubst timeclock-entry-begin (entry)
734 "Return the start time of ENTRY."
735 (car entry))
736
737(defsubst timeclock-entry-end (entry)
738 "Return the end time of ENTRY."
739 (cadr entry))
740
741(defsubst timeclock-entry-project (entry)
742 "Return the project of ENTRY."
743 (nth 2 entry))
744
745(defsubst timeclock-entry-comment (entry)
746 "Return the comment of ENTRY."
747 (nth 3 entry))
748
749(defsubst timeclock-entry-list-length (entry-list) 728(defsubst timeclock-entry-list-length (entry-list)
750 "Return the total length of ENTRY-LIST in seconds." 729 "Return the total length of ENTRY-LIST in seconds."
751 (let ((length 0)) 730 (let ((length 0))
@@ -771,14 +750,11 @@ This is only provided for coherency when used by
771 (- (timeclock-entry-list-span entry-list) 750 (- (timeclock-entry-list-span entry-list)
772 (timeclock-entry-list-length entry-list))) 751 (timeclock-entry-list-length entry-list)))
773 752
774(defsubst timeclock-entry-list-projects (entry-list) 753(defun timeclock-entry-list-projects (entry-list)
775 "Return a list of all the projects in ENTRY-LIST." 754 "Return a list of all the projects in ENTRY-LIST."
776 (let (projects proj) 755 (let (projects)
777 (dolist (entry entry-list) 756 (dolist (entry entry-list)
778 (setq proj (timeclock-entry-project entry)) 757 (cl-pushnew (timeclock-entry-project entry) projects :test #'equal))
779 (if projects
780 (add-to-list 'projects proj)
781 (setq projects (list proj))))
782 projects)) 758 projects))
783 759
784(defsubst timeclock-day-required (day) 760(defsubst timeclock-day-required (day)
@@ -854,9 +830,7 @@ This is only provided for coherency when used by
854 (let (projects) 830 (let (projects)
855 (dolist (day day-list) 831 (dolist (day day-list)
856 (dolist (proj (timeclock-day-projects day)) 832 (dolist (proj (timeclock-day-projects day))
857 (if projects 833 (cl-pushnew proj projects :test #'equal)))
858 (add-to-list 'projects proj)
859 (setq projects (list proj)))))
860 projects)) 834 projects))
861 835
862(defsubst timeclock-current-debt (&optional log-data) 836(defsubst timeclock-current-debt (&optional log-data)
@@ -871,7 +845,7 @@ This is only provided for coherency when used by
871 "Return a list of the cdrs of the date alist from LOG-DATA." 845 "Return a list of the cdrs of the date alist from LOG-DATA."
872 (let (day-list) 846 (let (day-list)
873 (dolist (date-list (timeclock-day-alist log-data)) 847 (dolist (date-list (timeclock-day-alist log-data))
874 (setq day-list (cons (cdr date-list) day-list))) 848 (push (cdr date-list) day-list))
875 day-list)) 849 day-list))
876 850
877(defsubst timeclock-project-alist (&optional log-data) 851(defsubst timeclock-project-alist (&optional log-data)
@@ -1022,54 +996,55 @@ See the documentation for the given function if more info is needed."
1022 (and beg (not last) 996 (and beg (not last)
1023 (setq last t event (list "o" now)))) 997 (setq last t event (list "o" now))))
1024 (setq line (1+ line)) 998 (setq line (1+ line))
1025 (cond ((equal (car event) "b") 999 (pcase (car event)
1026 (setcar log-data (string-to-number (nth 2 event)))) 1000 ("b"
1027 ((equal (car event) "h") 1001 (setcar log-data (string-to-number (nth 2 event))))
1028 (setq last-date-limited (timeclock-time-to-date (cadr event)) 1002 ("h"
1029 last-date-seconds (* (string-to-number (nth 2 event)) 1003 (setq last-date-limited (timeclock-time-to-date (cadr event))
1030 3600.0))) 1004 last-date-seconds (* (string-to-number (nth 2 event))
1031 ((equal (car event) "i") 1005 3600.0)))
1032 (if beg 1006 ("i"
1033 (error "Error in format of timelog file, line %d" line) 1007 (if beg
1034 (setq beg t)) 1008 (error "Error in format of timelog file, line %d" line)
1035 (setq entry (list (cadr event) nil 1009 (setq beg t))
1036 (and (> (length (nth 2 event)) 0) 1010 (setq entry (list (cadr event) nil
1037 (nth 2 event)))) 1011 (and (> (length (nth 2 event)) 0)
1038 (let ((date (timeclock-time-to-date (cadr event)))) 1012 (nth 2 event))))
1039 (if (and last-date 1013 (let ((date (timeclock-time-to-date (cadr event))))
1040 (not (equal date last-date))) 1014 (if (and last-date
1041 (progn 1015 (not (equal date last-date)))
1042 (setcar (cdr log-data) 1016 (progn
1043 (cons (cons last-date day) 1017 (setcar (cdr log-data)
1044 (cadr log-data))) 1018 (cons (cons last-date day)
1045 (setq day (list (and last-date-limited 1019 (cadr log-data)))
1046 last-date-seconds)))) 1020 (setq day (list (and last-date-limited
1047 (unless day 1021 last-date-seconds))))
1048 (setq day (list (and last-date-limited 1022 (unless day
1049 last-date-seconds))))) 1023 (setq day (list (and last-date-limited
1050 (setq last-date date 1024 last-date-seconds)))))
1051 last-date-limited nil))) 1025 (setq last-date date
1052 ((equal (downcase (car event)) "o") 1026 last-date-limited nil)))
1053 (if (not beg) 1027 ((or "o" "O")
1054 (error "Error in format of timelog file, line %d" line) 1028 (if (not beg)
1055 (setq beg nil)) 1029 (error "Error in format of timelog file, line %d" line)
1056 (setcar (cdr entry) (cadr event)) 1030 (setq beg nil))
1057 (let ((desc (and (> (length (nth 2 event)) 0) 1031 (setcar (cdr entry) (cadr event))
1058 (nth 2 event)))) 1032 (let ((desc (and (> (length (nth 2 event)) 0)
1059 (if desc 1033 (nth 2 event))))
1060 (nconc entry (list (nth 2 event)))) 1034 (if desc
1061 (if (equal (car event) "O") 1035 (nconc entry (list (nth 2 event))))
1062 (nconc entry (if desc 1036 (if (equal (car event) "O")
1063 (list t) 1037 (nconc entry (if desc
1064 (list nil t)))) 1038 (list t)
1065 (nconc day (list entry)) 1039 (list nil t))))
1066 (setq desc (nth 2 entry)) 1040 (nconc day (list entry))
1067 (let ((proj (assoc desc (nth 2 log-data)))) 1041 (setq desc (nth 2 entry))
1068 (if (null proj) 1042 (let ((proj (assoc desc (nth 2 log-data))))
1069 (setcar (cddr log-data) 1043 (if (null proj)
1070 (cons (cons desc (list entry)) 1044 (setcar (cddr log-data)
1071 (nth 2 log-data))) 1045 (cons (cons desc (list entry))
1072 (nconc (cdr proj) (list entry))))))) 1046 (nth 2 log-data)))
1047 (nconc (cdr proj) (list entry)))))))
1073 (forward-line)) 1048 (forward-line))
1074 (if day 1049 (if day
1075 (setcar (cdr log-data) 1050 (setcar (cdr log-data)
@@ -1185,14 +1160,12 @@ If optional argument TIME is non-nil, use that instead of the current time."
1185 1160
1186(defun timeclock-mean (l) 1161(defun timeclock-mean (l)
1187 "Compute the arithmetic mean of the values in the list L." 1162 "Compute the arithmetic mean of the values in the list L."
1188 (let ((total 0) 1163 (if (not (consp l))
1189 (count 0)) 1164 0
1190 (dolist (thisl l) 1165 (let ((total 0))
1191 (setq total (+ total thisl) 1166 (dolist (thisl l)
1192 count (1+ count))) 1167 (setq total (+ total thisl)))
1193 (if (zerop count) 1168 (/ total (length l)))))
1194 0
1195 (/ total count))))
1196 1169
1197(defun timeclock-generate-report (&optional html-p) 1170(defun timeclock-generate-report (&optional html-p)
1198 "Generate a summary report based on the current timelog file. 1171 "Generate a summary report based on the current timelog file.
@@ -1296,81 +1269,69 @@ HTML-P is non-nil, HTML markup is added."
1296 six-months-ago one-year-ago))) 1269 six-months-ago one-year-ago)))
1297 ;; collect statistics from complete timelog 1270 ;; collect statistics from complete timelog
1298 (dolist (day day-list) 1271 (dolist (day day-list)
1299 (let ((i 0) (l 5)) 1272 (dotimes (i 5)
1300 (while (< i l) 1273 (unless (time-less-p
1301 (unless (time-less-p 1274 (timeclock-day-begin day)
1302 (timeclock-day-begin day) 1275 (aref lengths i))
1303 (aref lengths i)) 1276 (let ((base (float-time
1304 (let ((base (float-time 1277 (timeclock-day-base
1305 (timeclock-day-base 1278 (timeclock-day-begin day)))))
1306 (timeclock-day-begin day))))) 1279 (nconc (aref time-in i)
1307 (nconc (aref time-in i) 1280 (list (- (float-time (timeclock-day-begin day))
1308 (list (- (float-time (timeclock-day-begin day)) 1281 base)))
1309 base))) 1282 (let ((span (timeclock-day-span day))
1310 (let ((span (timeclock-day-span day)) 1283 (len (timeclock-day-length day))
1311 (len (timeclock-day-length day)) 1284 (req (timeclock-day-required day)))
1312 (req (timeclock-day-required day))) 1285 ;; If the day's actual work length is less than
1313 ;; If the day's actual work length is less than 1286 ;; 70% of its span, then likely the exit time
1314 ;; 70% of its span, then likely the exit time 1287 ;; and break amount are not worthwhile adding to
1315 ;; and break amount are not worthwhile adding to 1288 ;; the statistic
1316 ;; the statistic 1289 (when (and (> span 0)
1317 (when (and (> span 0) 1290 (> (/ (float len) (float span)) 0.70))
1318 (> (/ (float len) (float span)) 0.70)) 1291 (nconc (aref time-out i)
1319 (nconc (aref time-out i) 1292 (list (- (float-time (timeclock-day-end day))
1320 (list (- (float-time (timeclock-day-end day)) 1293 base)))
1321 base))) 1294 (nconc (aref breaks i) (list (- span len))))
1322 (nconc (aref breaks i) (list (- span len)))) 1295 (if req
1323 (if req 1296 (setq len (+ len (- timeclock-workday req))))
1324 (setq len (+ len (- timeclock-workday req)))) 1297 (nconc (aref workday i) (list len)))))))
1325 (nconc (aref workday i) (list len)))))
1326 (setq i (1+ i)))))
1327 ;; average statistics 1298 ;; average statistics
1328 (let ((i 0) (l 5)) 1299 (dotimes (i 5)
1329 (while (< i l) 1300 (aset time-in i (timeclock-mean (cdr (aref time-in i))))
1330 (aset time-in i (timeclock-mean (cdr (aref time-in i)))) 1301 (aset time-out i (timeclock-mean (cdr (aref time-out i))))
1331 (aset time-out i (timeclock-mean (cdr (aref time-out i)))) 1302 (aset breaks i (timeclock-mean (cdr (aref breaks i))))
1332 (aset breaks i (timeclock-mean (cdr (aref breaks i)))) 1303 (aset workday i (timeclock-mean (cdr (aref workday i)))))
1333 (aset workday i (timeclock-mean (cdr (aref workday i))))
1334 (setq i (1+ i))))
1335 ;; Output the HTML table 1304 ;; Output the HTML table
1336 (insert "<tr>\n") 1305 (insert "<tr>\n")
1337 (insert "<td align=\"center\">Time in</td>\n") 1306 (insert "<td align=\"center\">Time in</td>\n")
1338 (let ((i 0) (l 5)) 1307 (dotimes (i 5)
1339 (while (< i l) 1308 (insert "<td align=\"right\">"
1340 (insert "<td align=\"right\">" 1309 (timeclock-seconds-to-string (aref time-in i))
1341 (timeclock-seconds-to-string (aref time-in i)) 1310 "</td>\n"))
1342 "</td>\n")
1343 (setq i (1+ i))))
1344 (insert "</tr>\n") 1311 (insert "</tr>\n")
1345 1312
1346 (insert "<tr>\n") 1313 (insert "<tr>\n")
1347 (insert "<td align=\"center\">Time out</td>\n") 1314 (insert "<td align=\"center\">Time out</td>\n")
1348 (let ((i 0) (l 5)) 1315 (dotimes (i 5)
1349 (while (< i l) 1316 (insert "<td align=\"right\">"
1350 (insert "<td align=\"right\">" 1317 (timeclock-seconds-to-string (aref time-out i))
1351 (timeclock-seconds-to-string (aref time-out i)) 1318 "</td>\n"))
1352 "</td>\n")
1353 (setq i (1+ i))))
1354 (insert "</tr>\n") 1319 (insert "</tr>\n")
1355 1320
1356 (insert "<tr>\n") 1321 (insert "<tr>\n")
1357 (insert "<td align=\"center\">Break</td>\n") 1322 (insert "<td align=\"center\">Break</td>\n")
1358 (let ((i 0) (l 5)) 1323 (dotimes (i 5)
1359 (while (< i l) 1324 (insert "<td align=\"right\">"
1360 (insert "<td align=\"right\">" 1325 (timeclock-seconds-to-string (aref breaks i))
1361 (timeclock-seconds-to-string (aref breaks i)) 1326 "</td>\n"))
1362 "</td>\n")
1363 (setq i (1+ i))))
1364 (insert "</tr>\n") 1327 (insert "</tr>\n")
1365 1328
1366 (insert "<tr>\n") 1329 (insert "<tr>\n")
1367 (insert "<td align=\"center\">Workday</td>\n") 1330 (insert "<td align=\"center\">Workday</td>\n")
1368 (let ((i 0) (l 5)) 1331 (dotimes (i 5)
1369 (while (< i l) 1332 (insert "<td align=\"right\">"
1370 (insert "<td align=\"right\">" 1333 (timeclock-seconds-to-string (aref workday i))
1371 (timeclock-seconds-to-string (aref workday i)) 1334 "</td>\n"))
1372 "</td>\n")
1373 (setq i (1+ i))))
1374 (insert "</tr>\n")) 1335 (insert "</tr>\n"))
1375 (insert "<tfoot> 1336 (insert "<tfoot>
1376<td colspan=\"6\" align=\"center\"> 1337<td colspan=\"6\" align=\"center\">
@@ -1393,6 +1354,7 @@ HTML-P is non-nil, HTML markup is added."
1393;; make sure we know the list of reasons, projects, and have computed 1354;; make sure we know the list of reasons, projects, and have computed
1394;; the last event and current discrepancy. 1355;; the last event and current discrepancy.
1395(if (file-readable-p timeclock-file) 1356(if (file-readable-p timeclock-file)
1357 ;; FIXME: Loading a file should not have these kinds of side-effects.
1396 (timeclock-reread-log)) 1358 (timeclock-reread-log))
1397 1359
1398;;; timeclock.el ends here 1360;;; timeclock.el ends here