aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorJohn Wiegley2001-03-16 21:39:31 +0000
committerJohn Wiegley2001-03-16 21:39:31 +0000
commit9329ea14c5864787bf425da59d3a503b72a8dea5 (patch)
treea8c0b45207186bd8a3ac2c317edb2c6dc3cd2729 /lisp
parentdbee590bf68749c303ec2952b9dd7d811e9416ec (diff)
downloademacs-9329ea14c5864787bf425da59d3a503b72a8dea5.tar.gz
emacs-9329ea14c5864787bf425da59d3a503b72a8dea5.zip
see ChangeLog
Diffstat (limited to 'lisp')
-rw-r--r--lisp/calendar/timeclock.el409
1 files changed, 318 insertions, 91 deletions
diff --git a/lisp/calendar/timeclock.el b/lisp/calendar/timeclock.el
index 773c131a24f..550214c6c29 100644
--- a/lisp/calendar/timeclock.el
+++ b/lisp/calendar/timeclock.el
@@ -431,7 +431,7 @@ Returns the new value of `timeclock-discrepancy'."
431 (interactive) 431 (interactive)
432 (setq timeclock-discrepancy nil) 432 (setq timeclock-discrepancy nil)
433 (timeclock-find-discrep) 433 (timeclock-find-discrep)
434 (if timeclock-modeline-display 434 (if (and timeclock-discrepancy timeclock-modeline-display)
435 (timeclock-update-modeline)) 435 (timeclock-update-modeline))
436 timeclock-discrepancy) 436 timeclock-discrepancy)
437 437
@@ -913,7 +913,7 @@ See the documentation for the given function if more info is needed."
913 (now (current-time)) 913 (now (current-time))
914 (todays-date (timeclock-time-to-date now)) 914 (todays-date (timeclock-time-to-date now))
915 last-date-limited last-date-seconds last-date 915 last-date-limited last-date-seconds last-date
916 (line 0) last beg day entry) 916 (line 0) last beg day entry event)
917 (with-temp-buffer 917 (with-temp-buffer
918 (insert-file-contents (or filename timeclock-file)) 918 (insert-file-contents (or filename timeclock-file))
919 (when recent-only 919 (when recent-only
@@ -940,11 +940,15 @@ See the documentation for the given function if more info is needed."
940 (let ((date (timeclock-time-to-date (cadr event)))) 940 (let ((date (timeclock-time-to-date (cadr event))))
941 (if (and last-date 941 (if (and last-date
942 (not (equal date last-date))) 942 (not (equal date last-date)))
943 (setcar (cdr log-data) 943 (progn
944 (cons (cons last-date day) 944 (setcar (cdr log-data)
945 (cadr log-data))) 945 (cons (cons last-date day)
946 (setq day (list (and last-date-limited 946 (cadr log-data)))
947 last-date-seconds)))) 947 (setq day (list (and last-date-limited
948 last-date-seconds))))
949 (unless day
950 (setq day (list (and last-date-limited
951 last-date-seconds)))))
948 (setq last-date date 952 (setq last-date date
949 last-date-limited nil))) 953 last-date-limited nil)))
950 ((equal (downcase (car event)) "o") 954 ((equal (downcase (car event)) "o")
@@ -963,7 +967,7 @@ See the documentation for the given function if more info is needed."
963 (nconc day (list entry)) 967 (nconc day (list entry))
964 (setq desc (nth 2 entry)) 968 (setq desc (nth 2 entry))
965 (let ((proj (assoc desc (nth 2 log-data)))) 969 (let ((proj (assoc desc (nth 2 log-data))))
966 (if (not proj) 970 (if (null proj)
967 (setcar (cddr log-data) 971 (setcar (cddr log-data)
968 (cons (cons desc (list entry)) 972 (cons (cons desc (list entry))
969 (car (cddr log-data)))) 973 (car (cddr log-data))))
@@ -983,90 +987,313 @@ identical to what would be return if `timeclock-relative' were nil."
983 ;; This is not implemented in terms of the functions above, because 987 ;; This is not implemented in terms of the functions above, because
984 ;; it's a bit wasteful to read all of that data in, just to throw 988 ;; it's a bit wasteful to read all of that data in, just to throw
985 ;; away more than 90% of the information afterwards. 989 ;; away more than 90% of the information afterwards.
986 (let* ((now (current-time)) 990 (when (file-readable-p timeclock-file)
987 (todays-date (timeclock-time-to-date now)) 991 (let* ((now (current-time))
988 (first t) (accum 0) 992 (todays-date (timeclock-time-to-date now))
989 event beg last-date avg 993 (first t) (accum 0)
990 last-date-limited last-date-seconds) 994 event beg last-date avg
991 (unless timeclock-discrepancy 995 last-date-limited last-date-seconds)
992 (setq timeclock-project-list nil 996 (unless timeclock-discrepancy
993 timeclock-last-project nil 997 (setq timeclock-project-list nil
994 timeclock-reason-list nil 998 timeclock-last-project nil
995 timeclock-elapsed 0) 999 timeclock-reason-list nil
996 (with-temp-buffer 1000 timeclock-elapsed 0)
997 (insert-file-contents timeclock-file) 1001 (with-temp-buffer
998 (goto-char (point-max)) 1002 (insert-file-contents timeclock-file)
999 (unless (re-search-backward "^b\\s-+" nil t) 1003 (goto-char (point-max))
1000 (goto-char (point-min))) 1004 (unless (re-search-backward "^b\\s-+" nil t)
1001 (while (setq event (timeclock-read-moment)) 1005 (goto-char (point-min)))
1002 (cond ((equal (car event) "b") 1006 (while (setq event (timeclock-read-moment))
1003 (setq accum (string-to-number (nth 2 event)))) 1007 (cond ((equal (car event) "b")
1004 ((equal (car event) "h") 1008 (setq accum (string-to-number (nth 2 event))))
1005 (setq last-date-limited 1009 ((equal (car event) "h")
1006 (timeclock-time-to-date (cadr event)) 1010 (setq last-date-limited
1007 last-date-seconds 1011 (timeclock-time-to-date (cadr event))
1008 (* (string-to-number (nth 2 event)) 3600.0))) 1012 last-date-seconds
1009 ((equal (car event) "i") 1013 (* (string-to-number (nth 2 event)) 3600.0)))
1010 (when (and (nth 2 event) 1014 ((equal (car event) "i")
1011 (> (length (nth 2 event)) 0)) 1015 (when (and (nth 2 event)
1012 (add-to-list 'timeclock-project-list (nth 2 event)) 1016 (> (length (nth 2 event)) 0))
1013 (setq timeclock-last-project (nth 2 event))) 1017 (add-to-list 'timeclock-project-list (nth 2 event))
1014 (let ((date (timeclock-time-to-date (cadr event)))) 1018 (setq timeclock-last-project (nth 2 event)))
1015 (if (and timeclock-relative 1019 (let ((date (timeclock-time-to-date (cadr event))))
1016 (if last-date 1020 (if (and timeclock-relative
1017 (not (equal date last-date)) 1021 (if last-date
1018 first)) 1022 (not (equal date last-date))
1019 (setq first nil 1023 first))
1020 accum (- accum 1024 (setq first nil
1021 (if last-date-limited 1025 accum (- accum
1022 last-date-seconds 1026 (if last-date-limited
1023 timeclock-workday)))) 1027 last-date-seconds
1024 (setq last-date date 1028 timeclock-workday))))
1025 last-date-limited nil) 1029 (setq last-date date
1026 (if beg 1030 last-date-limited nil)
1027 (error "Error in format of timelog file!") 1031 (if beg
1028 (setq beg (timeclock-time-to-seconds (cadr event))))))
1029 ((equal (downcase (car event)) "o")
1030 (if (and (nth 2 event)
1031 (> (length (nth 2 event)) 0))
1032 (add-to-list 'timeclock-reason-list (nth 2 event)))
1033 (if (or timeclock-relative
1034 (equal last-date todays-date))
1035 (if (not beg)
1036 (error "Error in format of timelog file!") 1032 (error "Error in format of timelog file!")
1037 (setq timeclock-last-period 1033 (setq beg (timeclock-time-to-seconds (cadr event))))))
1038 (- (timeclock-time-to-seconds (cadr event)) beg) 1034 ((equal (downcase (car event)) "o")
1039 accum (+ timeclock-last-period accum) 1035 (if (and (nth 2 event)
1040 beg nil))) 1036 (> (length (nth 2 event)) 0))
1041 (if (equal last-date todays-date) 1037 (add-to-list 'timeclock-reason-list (nth 2 event)))
1042 (setq timeclock-elapsed 1038 (if (or timeclock-relative
1043 (+ timeclock-last-period timeclock-elapsed))))) 1039 (equal last-date todays-date))
1044 (setq timeclock-last-event event 1040 (if (not beg)
1045 timeclock-last-event-workday 1041 (error "Error in format of timelog file!")
1046 (if (equal (timeclock-time-to-date now) 1042 (setq timeclock-last-period
1047 last-date-limited) 1043 (- (timeclock-time-to-seconds (cadr event))
1048 last-date-seconds 1044 beg)
1049 timeclock-workday)) 1045 accum (+ timeclock-last-period accum)
1050 (forward-line)) 1046 beg nil)))
1051 (setq timeclock-discrepancy accum))) 1047 (if (equal last-date todays-date)
1052 (setq accum (if today-only 1048 (setq timeclock-elapsed
1053 timeclock-elapsed 1049 (+ timeclock-last-period timeclock-elapsed)))))
1054 timeclock-discrepancy)) 1050 (setq timeclock-last-event event
1055 (if timeclock-last-event 1051 timeclock-last-event-workday
1056 (if (equal (car timeclock-last-event) "i") 1052 (if (equal (timeclock-time-to-date now)
1057 (setq accum (+ accum (timeclock-last-period now))) 1053 last-date-limited)
1058 (if (not (equal (timeclock-time-to-date 1054 last-date-seconds
1059 (cadr timeclock-last-event)) 1055 timeclock-workday))
1060 (timeclock-time-to-date now))) 1056 (forward-line))
1061 (setq accum (- accum timeclock-last-event-workday))))) 1057 (setq timeclock-discrepancy accum)))
1062 (setq accum 1058 (setq accum (if today-only
1063 (- accum 1059 timeclock-elapsed
1064 (if (and timeclock-last-event 1060 timeclock-discrepancy))
1065 (equal (timeclock-time-to-date 1061 (if timeclock-last-event
1066 (cadr timeclock-last-event)) 1062 (if (equal (car timeclock-last-event) "i")
1067 (timeclock-time-to-date now))) 1063 (setq accum (+ accum (timeclock-last-period now)))
1068 timeclock-last-event-workday 1064 (if (not (equal (timeclock-time-to-date
1069 timeclock-workday))))) 1065 (cadr timeclock-last-event))
1066 (timeclock-time-to-date now)))
1067 (setq accum (- accum timeclock-last-event-workday)))))
1068 (setq accum
1069 (- accum
1070 (if (and timeclock-last-event
1071 (equal (timeclock-time-to-date
1072 (cadr timeclock-last-event))
1073 (timeclock-time-to-date now)))
1074 timeclock-last-event-workday
1075 timeclock-workday))))))
1076
1077;;; A reporting function that uses timeclock-log-data
1078
1079(defun timeclock-time-less-p (t1 t2)
1080 "Say whether time T1 is less than time T2."
1081 (or (< (car t1) (car t2))
1082 (and (= (car t1) (car t2))
1083 (< (nth 1 t1) (nth 1 t2)))))
1084
1085(defun timeclock-day-base (&optional time)
1086 "Given a time within a day, return 0:0:0 within that day."
1087 (let ((decoded (decode-time (or time (current-time)))))
1088 (setcar (nthcdr 0 decoded) 0)
1089 (setcar (nthcdr 1 decoded) 0)
1090 (setcar (nthcdr 2 decoded) 0)
1091 (apply 'encode-time decoded)))
1092
1093(defun timeclock-geometric-mean (l)
1094 "Compute the geometric mean of the list L."
1095 (let ((total 0)
1096 (count 0))
1097 (while l
1098 (setq total (+ total (car l))
1099 count (1+ count)
1100 l (cdr l)))
1101 (if (> count 0)
1102 (/ total count)
1103 0)))
1104
1105(defun timeclock-generate-report (&optional html-p)
1106 "Generate a summary report based on the current timelog file."
1107 (interactive)
1108 (let ((log (timeclock-log-data))
1109 (today (timeclock-day-base)))
1110 (if html-p (insert "<p>"))
1111 (insert "Currently ")
1112 (let ((project (nth 2 timeclock-last-event))
1113 (begin (nth 1 timeclock-last-event))
1114 done)
1115 (if (timeclock-currently-in-p)
1116 (insert "IN")
1117 (if (or (null project) (= (length project) 0))
1118 (progn (insert "Done Working Today")
1119 (setq done t))
1120 (insert "OUT")))
1121 (unless done
1122 (insert " since " (format-time-string "%Y/%m/%d %-I:%M %p" begin))
1123 (if html-p
1124 (insert "<br>\n<b>")
1125 (insert "\n*"))
1126 (if (timeclock-currently-in-p)
1127 (insert "Working on "))
1128 (if html-p
1129 (insert "</b><br>\n")
1130 (insert project "*\n"))
1131 (let ((proj-data (cdr (assoc project (timeclock-project-alist log))))
1132 (two-weeks-ago (timeclock-seconds-to-time
1133 (- (timeclock-time-to-seconds today)
1134 (* 2 7 24 60 60))))
1135 two-week-len today-len)
1136 (while proj-data
1137 (if (not (timeclock-time-less-p
1138 (timeclock-entry-begin (car proj-data)) today))
1139 (setq today-len (timeclock-entry-list-length proj-data)
1140 proj-data nil)
1141 (if (and (null two-week-len)
1142 (not (timeclock-time-less-p
1143 (timeclock-entry-begin (car proj-data))
1144 two-weeks-ago)))
1145 (setq two-week-len (timeclock-entry-list-length proj-data)))
1146 (setq proj-data (cdr proj-data))))
1147 (if (null two-week-len)
1148 (setq two-week-len today-len))
1149 (if html-p (insert "<p>"))
1150 (insert "\nTime spent on this task today: "
1151 (timeclock-seconds-to-string today-len)
1152 ". In the last two weeks: "
1153 (timeclock-seconds-to-string two-week-len))
1154 (if html-p (insert "<br>"))
1155 (insert "\n"
1156 (timeclock-seconds-to-string (timeclock-workday-elapsed))
1157 " worked today, "
1158 (timeclock-seconds-to-string (timeclock-workday-remaining))
1159 " remaining, done at "
1160 (timeclock-when-to-leave-string) "\n")))
1161 (if html-p (insert "<p>"))
1162 (insert "\nThere have been "
1163 (number-to-string
1164 (length (timeclock-day-alist log)))
1165 " days of activity, starting "
1166 (caar (last (timeclock-day-alist log))))
1167 (if html-p (insert "</p>"))
1168 (when html-p
1169 (insert "<p>
1170<table>
1171<td width=\"25\"><br></td><td>
1172<table border=1 cellpadding=3>
1173<tr><th><i>Statistics</i></th>
1174 <th>Entire</th>
1175 <th>-30 days</th>
1176 <th>-3 mons</th>
1177 <th>-6 mons</th>
1178 <th>-1 year</th>
1179</tr>")
1180 (let* ((day-list (timeclock-day-list))
1181 (thirty-days-ago (timeclock-seconds-to-time
1182 (- (timeclock-time-to-seconds today)
1183 (* 30 24 60 60))))
1184 (three-months-ago (timeclock-seconds-to-time
1185 (- (timeclock-time-to-seconds today)
1186 (* 90 24 60 60))))
1187 (six-months-ago (timeclock-seconds-to-time
1188 (- (timeclock-time-to-seconds today)
1189 (* 180 24 60 60))))
1190 (one-year-ago (timeclock-seconds-to-time
1191 (- (timeclock-time-to-seconds today)
1192 (* 365 24 60 60))))
1193 (time-in (vector (list t) (list t) (list t) (list t) (list t)))
1194 (time-out (vector (list t) (list t) (list t) (list t) (list t)))
1195 (breaks (vector (list t) (list t) (list t) (list t) (list t)))
1196 (workday (vector (list t) (list t) (list t) (list t) (list t)))
1197 (lengths (vector '(0 0) thirty-days-ago three-months-ago
1198 six-months-ago one-year-ago)))
1199 ;; collect statistics from complete timelog
1200 (while day-list
1201 (let ((i 0) (l 5))
1202 (while (< i l)
1203 (unless (timeclock-time-less-p
1204 (timeclock-day-begin (car day-list))
1205 (aref lengths i))
1206 (let ((base (timeclock-time-to-seconds
1207 (timeclock-day-base
1208 (timeclock-day-begin (car day-list))))))
1209 (nconc (aref time-in i)
1210 (list (- (timeclock-time-to-seconds
1211 (timeclock-day-begin (car day-list)))
1212 base)))
1213 (let ((span (timeclock-day-span (car day-list)))
1214 (len (timeclock-day-length (car day-list)))
1215 (req (timeclock-day-required (car day-list))))
1216 ;; If the day's actual work length is less than
1217 ;; 70% of its span, then likely the exit time
1218 ;; and break amount are not worthwhile adding to
1219 ;; the statistic
1220 (when (and (> span 0)
1221 (> (/ (float len) (float span)) 0.70))
1222 (nconc (aref time-out i)
1223 (list (- (timeclock-time-to-seconds
1224 (timeclock-day-end (car day-list)))
1225 base)))
1226 (nconc (aref breaks i) (list (- span len))))
1227 (if req
1228 (setq len (+ len (- timeclock-workday req))))
1229 (nconc (aref workday i) (list len)))))
1230 (setq i (1+ i))))
1231 (setq day-list (cdr day-list)))
1232 ;; average statistics
1233 (let ((i 0) (l 5))
1234 (while (< i l)
1235 (aset time-in i (timeclock-geometric-mean
1236 (cdr (aref time-in i))))
1237 (aset time-out i (timeclock-geometric-mean
1238 (cdr (aref time-out i))))
1239 (aset breaks i (timeclock-geometric-mean
1240 (cdr (aref breaks i))))
1241 (aset workday i (timeclock-geometric-mean
1242 (cdr (aref workday i))))
1243 (setq i (1+ i))))
1244 ;; Output the HTML table
1245 (insert "<tr>\n")
1246 (insert "<td align=\"center\">Time in</td>\n")
1247 (let ((i 0) (l 5))
1248 (while (< i l)
1249 (insert "<td align=\"right\">"
1250 (timeclock-seconds-to-string (aref time-in i))
1251 "</td>\n")
1252 (setq i (1+ i))))
1253 (insert "</tr>\n")
1254
1255 (insert "<tr>\n")
1256 (insert "<td align=\"center\">Time out</td>\n")
1257 (let ((i 0) (l 5))
1258 (while (< i l)
1259 (insert "<td align=\"right\">"
1260 (timeclock-seconds-to-string (aref time-out i))
1261 "</td>\n")
1262 (setq i (1+ i))))
1263 (insert "</tr>\n")
1264
1265 (insert "<tr>\n")
1266 (insert "<td align=\"center\">Break</td>\n")
1267 (let ((i 0) (l 5))
1268 (while (< i l)
1269 (insert "<td align=\"right\">"
1270 (timeclock-seconds-to-string (aref breaks i))
1271 "</td>\n")
1272 (setq i (1+ i))))
1273 (insert "</tr>\n")
1274
1275 (insert "<tr>\n")
1276 (insert "<td align=\"center\">Workday</td>\n")
1277 (let ((i 0) (l 5))
1278 (while (< i l)
1279 (insert "<td align=\"right\">"
1280 (timeclock-seconds-to-string (aref workday i))
1281 "</td>\n")
1282 (setq i (1+ i))))
1283 (insert "</tr>\n"))
1284 (insert "<tfoot>
1285<td colspan=\"6\" align=\"center\">
1286 <i>These are approximate figures</i></td>
1287</tfoot>
1288</table>
1289</td></table>")))))
1290
1291;;; A helpful little function
1292
1293(defun timeclock-visit-timelog ()
1294 "Open up the .timelog file in another window."
1295 (interactive)
1296 (find-file-other-window timeclock-file))
1070 1297
1071(provide 'timeclock) 1298(provide 'timeclock)
1072 1299