report - display date and total time per day

This commit is contained in:
Kashish Sharma 2018-09-11 17:06:16 +05:30
parent b306659c86
commit bf250126ac
3 changed files with 83 additions and 49 deletions

View File

@ -41,6 +41,9 @@
;; - maybe use advice?
;; 2. Can't have multi-line headers
;; TODO - use variables instead of hardcoded numbers to determine spacing
;; TODO - remove repetitive calls to (format "%04d-%02d-%02d" (elt seq a) (elt seq b) (elt seq c))
;; ## VARIABLES ##
(defvar timeclock-list-buffer-name "*Timeclock-List*")
(defvar timeclock-list-hide-cursor nil
@ -128,11 +131,14 @@ return a vector in the same form."
s1 s2))))
(defun timeclock-list-total-time-one-day (&optional date)
"Calculate the total time clocked today, or on DATE if non-nil."
(->>
timeclock-project-list
(--map (timeclock-ui-project-time-one-day it date))
(-reduce #'timeclock-list-time-add)))
"Return the total time clocked on DATE (if non-nil) or
today, as a vector in the form [HOURS MINUTES SECONDS].
DATE must be calendrical information calendrical
information (see (info \"(elisp)Time Conversion\"))."
(->> timeclock-project-list
(--map (timeclock-ui-project-time-one-day it date))
(-reduce #'timeclock-list-time-add)))
(defun timeclock-list-print-non-tabular ()
"Print the non-tabular part of the buffer in `timeclock-list'."

View File

@ -12,6 +12,10 @@
;; tabulated-list-format. Have it use timeclock-report-weekday-number-alist for day
;; names to aid i10n
;; TODO - add numeric arguments for next/previous week
;; TODO - use variables instead of hardcoded numbers to determine spacing
;; TODO - remove dead code
(defvar timeclock-report-week-start-day "Sunday"
"The day used for start of week by `timeclock-report'.")
(defvar timeclock-report--ui-date
@ -21,6 +25,11 @@
value of nil means the current week. Otherwise, it must be a list
in the form (YEAR WEEK), where WEEK is the numeric week of the
year (1-52).")
(defvar timeclock-report--ui-week-dates
nil
"List of dates currently displayed by
`timeclock-report' (specifically `timeclock-report-entries').
Each date is a list containing calendrical information (see (info \"(elisp)Time Conversion\"))")
(defvar timeclock-report-weekday-number-alist
'(("Sunday" . 0)
@ -84,9 +93,7 @@ Any time data provided is reset to midnight (00:00:00)."
"Return the date specified by `timeclock-report--ui-date'. If
it is nil, return the current date as calendrical
information (see (info \"(elisp)Time Conversion\"))."
(if timeclock-report--ui-date
timeclock-report--ui-date
(decode-time)))
(if timeclock-report--ui-date timeclock-report--ui-date (decode-time)))
;; ## VARIABLES ##
(defvar timeclock-report-buffer-name "*Timeclock-Report*")
@ -132,7 +139,7 @@ specified in `timeclock-report--year-week'."
;; maybe these two should take two arguments instead of a list?
(defun timeclock-report-dec-year-week (year-week)
"Decrements YEAR-WEEK by one week. YEAR-WEEK must be a list in
"Decrement YEAR-WEEK by one week. YEAR-WEEK must be a list in
the form (YEAR WEEK), where WEEK is the numeric week in
YEAR (1-52)."
(let ((y (car year-week))
@ -142,7 +149,7 @@ YEAR (1-52)."
(list y (1- w)))))
(defun timeclock-report-inc-year-week (year-week)
"Increments YEAR-WEEK by one week. YEAR-WEEK must be a list in
"Increment YEAR-WEEK by one week. YEAR-WEEK must be a list in
the form (YEAR WEEK), where WEEK is the numeric week in
YEAR (1-52)."
(let ((y (car year-week))
@ -164,21 +171,34 @@ FIRST-DATE-IN-WEEK must be a time value representing DAY-1."
(+ (cadr first-date-in-week) it))
it)))
(defun timeclock-report-dates-in-week->string (dates-in-week)
"Return a list in the form (DAY-1 DAY-2 ... DAY-7), where each
day is a string in the form \"YYYY/MM/DD\""
(--map (format "%04d/%02d/%02d"
(elt it 5)
(elt it 4)
(elt it 3))
dates-in-week))
(defun timeclock-report-date->week-dates ()
"Return dates in week as a list, where each element is
calendrical information (see (info \"(elisp)Time Conversion\")).
The first date is the first occurrence of
`timeclock-report-week-start-day' before the date specified in
`timeclock-report--ui-date' (if non-nil) or the current date."
(->> (timeclock-report-date)
(timeclock-report-previous-week-start)
(-take 6)
(apply #'encode-time)
(timeclock-report-date->dates-in-week)
(-map #'decode-time)))
(defun timeclock-report-entries ()
"Creates entries to be displayed in the buffer created by
`timeclock-report'."
(let* ((date (timeclock-report-date))
(first-date-of-week (->> date
(timeclock-report-previous-week-start)
(-take 6)
(apply #'encode-time)))
;; list of dates of each day in WEEK
(dates-in-week (->> (timeclock-report-date->dates-in-week first-date-of-week)
(-map #'decode-time)
(--map (format "%02d/%02d/%02d"
(elt it 5)
(elt it 4)
(elt it 3))))))
(let* ((week-dates (timeclock-report-date->week-dates))
(week-dates-string (timeclock-report-dates-in-week->string week-dates)))
(setq timeclock-report--ui-week-dates week-dates)
(mapcar (lambda (project)
(list project
(vconcat
@ -186,7 +206,7 @@ FIRST-DATE-IN-WEEK must be a time value representing DAY-1."
(apply #'vector
(--map (timeclock-ui-format-time
(timeclock-ui-project-time-one-day project it))
dates-in-week)))))
week-dates)))))
timeclock-project-list)))
(defun timeclock-report-idle-timer ()
@ -194,31 +214,34 @@ FIRST-DATE-IN-WEEK must be a time value representing DAY-1."
(timeclock-ui-buffer-visible? timeclock-report-buffer-name))
(timeclock-reread-log)
(with-current-buffer timeclock-report-buffer-name
(tabulated-list-print t t))))
(tabulated-list-print t nil)
(timeclock-report-print-non-tabular))))
(defun timeclock-report-format-date (date)
(->> date
(defun timeclock-report-format-date (format-string time-date)
"Extract date from TIME-DATE and format it according to
FORMAT-STRING."
(->> time-date
(-take 6)
(-drop 3)
(reverse)
(apply #'format "%02d-%02d-%02d")))
(apply #'format format-string)))
(defun timeclock-report-print-non-tabular ()
"Print the non-tabular part of the buffer in `timeclock-report'."
(let ((inhibit-read-only t))
(goto-char (point-min))
(insert " ")
(--map (insert (timeclock-report-format-date "%04d-%02d-%02d " it))
(timeclock-report-date->week-dates))
(insert "\n")
(goto-char (point-max))
(-->
;; (timeclock-list-total-time-one-day)
;; (timeclock-ui-format-time it)
;; (format "\n %- 26s%s" "Total" it)
""
(concat it
"\n\n l - open log file")
(insert it))
(insert "\n\n "
(if timeclock-report--ui-date
(timeclock-report-format-date timeclock-report--ui-date)
(timeclock-report-format-date (decode-time))))))
(insert (format "\n %- 21s" "Total"))
(->> timeclock-report--ui-week-dates
(mapcar #'timeclock-list-total-time-one-day)
(mapcar #'timeclock-ui-format-time)
(--map (format "% 9s " it))
(apply #'insert))
(insert "\n\n l - open log file")))
;; ## MAJOR MODE ##
@ -244,7 +267,7 @@ FIRST-DATE-IN-WEEK must be a time value representing DAY-1."
(tabulated-list-init-header)
(run-with-idle-timer 3 t #'timeclock-report-idle-timer)
(run-with-idle-timer 5 t #'timeclock-report-idle-timer)
(define-key timeclock-report-mode-map (kbd "l") 'timeclock-list-open-timeclock-file)
(define-key timeclock-report-mode-map (kbd "b") 'timeclock-report-previous-week)
(define-key timeclock-report-mode-map (kbd "f") 'timeclock-report-next-week))

View File

@ -153,9 +153,9 @@ TARGET-DATE."
(defun timeclock-ui-project-time-one-day (project &optional date)
"Read `timeclock-file' and return total time spent on a project
in one day. If DATE is a string in the form \"YYYY-MM-DD\", the
time for that date is shown, otherwise calculate time for that
day.
in one day. If DATE is a list containing calendrical
information (see (info \"(elisp)Time Conversion\")), the time for
that date is shown, otherwise calculate time for today.
The return value is a vector in the form [HOURS MINUTES SECONDS]"
(if (not (member project timeclock-project-list))
@ -163,7 +163,10 @@ The return value is a vector in the form [HOURS MINUTES SECONDS]"
(with-current-buffer (find-file-noselect timeclock-file)
(save-excursion
(let* ((target-date (if date
(replace-regexp-in-string "-" "/" date) ;; should probably validate it...
(format "%04d/%02d/%02d"
(elt date 5)
(elt date 4)
(elt date 3))
(format-time-string "%Y/%m/%d")))
(search-re (concat target-date " " timeclock-ui-time-re-file " " project))
(interval-list nil)
@ -206,21 +209,23 @@ The return value is a vector in the form [HOURS MINUTES SECONDS]"
;; '([0 0 0] [0 0 1] [0 0 10] [0 1 10] [0 10 10] [1 10 10] [10 10 10]))
;; => ("" "00:01" "00:10" "01:10" "10:10" "01:10:10" "10:10:10")
(defun timeclock-ui-format-time (time)
"Format and display TIME as a string, where time is a vector or
"Format and display TIME as a string, where TIME is a vector or
a list of the form [HOURS MINUTES SECONDS] or (HOURS MINUTES
SECONDS)."
(let ((h (elt time 0))
(m (elt time 1))
(s (elt time 2)))
(if (and (zerop h) (zerop m) (zerop s))
"-"
" -"
(let ((h (if (zerop h)
""
" "
;; Can't change this just yet or all commands break spectacularly.
;; Maybe it's best this way too? Looks uniform.
;; What if I pad with space?
(format "%02d:" h)))
(m (format "%02d:" m))
(format "% 2d:" h)))
(m (cond ((and (zerop h) (zerop m)) " ")
((and (zerop h) (< m 10)) (format "% 2d:" m))
(t (format "%02d:" m))))
(s (format "%02d" s)))
(concat h m s)))))