feat(details): implement custom ranges
This commit is contained in:
parent
9f8e455e01
commit
c4b94be941
|
@ -869,7 +869,8 @@ unchanged."
|
|||
|
||||
(defun chronometrist-iso-timestamp-to-ts (timestamp)
|
||||
"Convert TIMESTAMP to a TS struct. (see `ts.el')
|
||||
TIMESTAMP must be the ISO-8601 format, as handled by `parse-iso8601-time-string'."
|
||||
TIMESTAMP must be an ISO-8601 timestamp, as handled by
|
||||
`parse-iso8601-time-string'."
|
||||
(-let [(second minute hour day month year dow _dst utcoff)
|
||||
(decode-time
|
||||
(parse-iso8601-time-string timestamp))]
|
||||
|
@ -2051,7 +2052,8 @@ using `chronometrist-details-schema-transformers'.")
|
|||
"Return rows to be displayed in the `chronometrist-details' buffer.
|
||||
Return value is a list as specified by `tabulated-list-entries'."
|
||||
(cl-loop with index = 1
|
||||
for plist in (gethash (chronometrist-events-last-date) chronometrist-events) collect
|
||||
for plist in (chronometrist-details-intervals-for-range chronometrist-details-range chronometrist-events)
|
||||
collect
|
||||
(-let* (((&plist :name name :tags tags :start start :stop stop) plist)
|
||||
;; whether tags or key-values are actually displayed is handled later
|
||||
(tags (chronometrist-details-rows-helper tags))
|
||||
|
@ -2110,6 +2112,35 @@ ISO date or date-time strings - display intervals in this range.
|
|||
Dates are inclusive.")
|
||||
(make-variable-buffer-local 'chronometrist-details-range)
|
||||
|
||||
(defun chronometrist-iso-date-p (string)
|
||||
(string-match-p
|
||||
(rx (and string-start
|
||||
(>= 1 num) "-" (= 2 num) "-" (= 2 num)
|
||||
string-end))
|
||||
string))
|
||||
|
||||
(defun chronometrist-details-intervals-for-range (range table)
|
||||
"Return intervals for RANGE from TABLE.
|
||||
RANGE must be a time range as specified by `chronometrist-details-range'.
|
||||
|
||||
TABLE must be a hash table similar to `chronometrist-events'."
|
||||
(pcase range
|
||||
('nil
|
||||
(gethash (format-time-string "%F") table))
|
||||
((pred stringp)
|
||||
(gethash range table))
|
||||
(`(,begin . ,end)
|
||||
(if (and (chronometrist-iso-date-p begin) (chronometrist-iso-date-p end))
|
||||
(let ((begin-ts (chronometrist-iso-timestamp-to-ts begin))
|
||||
(end-ts (chronometrist-iso-timestamp-to-ts end)))
|
||||
(cl-loop while (not (ts> begin-ts end-ts))
|
||||
append (gethash (ts-format "%F" begin-ts) table)
|
||||
do (ts-adjustf begin-ts 'day 1)))))))
|
||||
|
||||
;; (chronometrist-details-intervals-for-range nil chronometrist-events)
|
||||
;; (chronometrist-details-intervals-for-range "2021-06-01" chronometrist-events)
|
||||
;; (chronometrist-details-intervals-for-range '("2021-06-01" . "2021-06-03") chronometrist-events)
|
||||
|
||||
(defvar chronometrist-details-set-range ()
|
||||
"Prompt user for range for current `chronometrist-details' buffer.")
|
||||
|
||||
|
|
|
@ -976,7 +976,7 @@ Boilerplate for updating state between file operations in tests.
|
|||
(list :last (chronometrist-file-hash :before-last nil)
|
||||
:rest (chronometrist-file-hash nil :before-last t)))))
|
||||
#+END_SRC
|
||||
*** chronometrist-file :custom:variable:
|
||||
*** chronometrist-file :custom:variable:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defcustom chronometrist-file
|
||||
(locate-user-emacs-file "chronometrist.sexp")
|
||||
|
@ -1510,7 +1510,7 @@ The data from =chronometrist-events= is used by most (all?) interval-consuming f
|
|||
(setq chronometrist--file-state nil)
|
||||
(chronometrist-refresh))
|
||||
#+END_SRC
|
||||
*** chronometrist-events :variable:
|
||||
*** chronometrist-events :variable:
|
||||
:PROPERTIES:
|
||||
:VALUE: hash table
|
||||
:END:
|
||||
|
@ -1635,6 +1635,7 @@ If REPLACE is non-nil, replace the last event with PLIST."
|
|||
*** events-subset :reader:
|
||||
:PROPERTIES:
|
||||
:VALUE: hash table
|
||||
:CUSTOM_ID: program-data-structures-events-subset
|
||||
:END:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-events-subset (start end)
|
||||
|
@ -1764,7 +1765,8 @@ unchanged."
|
|||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-iso-timestamp-to-ts (timestamp)
|
||||
"Convert TIMESTAMP to a TS struct. (see `ts.el')
|
||||
TIMESTAMP must be the ISO-8601 format, as handled by `parse-iso8601-time-string'."
|
||||
TIMESTAMP must be an ISO-8601 timestamp, as handled by
|
||||
`parse-iso8601-time-string'."
|
||||
(-let [(second minute hour day month year dow _dst utcoff)
|
||||
(decode-time
|
||||
(parse-iso8601-time-string timestamp))]
|
||||
|
@ -1772,6 +1774,18 @@ TIMESTAMP must be the ISO-8601 format, as handled by `parse-iso8601-time-string'
|
|||
(make-ts :hour hour :minute minute :second second
|
||||
:day day :month month :year year
|
||||
:dow dow :tz-offset utcoff))))
|
||||
|
||||
#+END_SRC
|
||||
**** tests
|
||||
#+BEGIN_SRC emacs-lisp :tangle chronometrist-tests.el :load test
|
||||
(ert-deftest chronometrist-iso-timestamp-to-ts ()
|
||||
(should (ts= (chronometrist-iso-timestamp-to-ts "2021-01-01")
|
||||
(make-ts :year 2021 :month 1 :day 1
|
||||
:hour 0 :minute 0 :second 0)))
|
||||
(should (ts= (chronometrist-iso-timestamp-to-ts "2021-01-01T01:01:01")
|
||||
(make-ts :year 2021 :month 1 :day 1
|
||||
:hour 1 :minute 1 :second 1))))
|
||||
|
||||
#+END_SRC
|
||||
*** iso-date-to-ts :function:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
@ -2099,7 +2113,7 @@ Return the value returned by Fₙ."
|
|||
See `tabulated-list-format'."
|
||||
:type '(vector))
|
||||
#+END_SRC
|
||||
**** chronometrist-mode-hook :hook:normal:
|
||||
**** chronometrist-mode-hook :hook:normal:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defvar chronometrist-mode-hook nil
|
||||
"Normal hook run at the very end of `chronometrist-mode'.")
|
||||
|
@ -3268,7 +3282,8 @@ using `chronometrist-details-schema-transformers'.")
|
|||
"Return rows to be displayed in the `chronometrist-details' buffer.
|
||||
Return value is a list as specified by `tabulated-list-entries'."
|
||||
(cl-loop with index = 1
|
||||
for plist in (gethash (chronometrist-events-last-date) chronometrist-events) collect
|
||||
for plist in (chronometrist-details-intervals-for-range chronometrist-details-range chronometrist-events)
|
||||
collect
|
||||
(-let* (((&plist :name name :tags tags :start start :stop stop) plist)
|
||||
;; whether tags or key-values are actually displayed is handled later
|
||||
(tags (chronometrist-details-rows-helper tags))
|
||||
|
@ -3296,7 +3311,7 @@ Return value is a list as specified by `tabulated-list-entries'."
|
|||
do (cl-incf index)))
|
||||
#+END_SRC
|
||||
|
||||
**** chronometrist-details-mode :major:mode:
|
||||
**** chronometrist-details-mode :major:mode:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(define-derived-mode chronometrist-details-mode tabulated-list-mode "Details"
|
||||
"Major mode for `chronometrist-details'."
|
||||
|
@ -3310,7 +3325,7 @@ Return value is a list as specified by `tabulated-list-entries'."
|
|||
(run-hooks 'chronometrist-mode-hook))
|
||||
#+END_SRC
|
||||
|
||||
**** chronometrist-details :command:
|
||||
**** chronometrist-details :command:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-details ()
|
||||
(interactive)
|
||||
|
@ -3336,6 +3351,43 @@ ISO date or date-time strings - display intervals in this range.
|
|||
Dates are inclusive.")
|
||||
(make-variable-buffer-local 'chronometrist-details-range)
|
||||
|
||||
#+END_SRC
|
||||
**** iso-date-p
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-iso-date-p (string)
|
||||
(string-match-p
|
||||
(rx (and string-start
|
||||
(>= 1 num) "-" (= 2 num) "-" (= 2 num)
|
||||
string-end))
|
||||
string))
|
||||
|
||||
#+END_SRC
|
||||
**** intervals-for-range :reader:
|
||||
This is basically like [[#program-data-structures-events-subset][chronometrist-events-subset]], but returns a list instead of a hash table. Might replace one with the other in the future.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-details-intervals-for-range (range table)
|
||||
"Return intervals for RANGE from TABLE.
|
||||
RANGE must be a time range as specified by `chronometrist-details-range'.
|
||||
|
||||
TABLE must be a hash table similar to `chronometrist-events'."
|
||||
(pcase range
|
||||
('nil
|
||||
(gethash (format-time-string "%F") table))
|
||||
((pred stringp)
|
||||
(gethash range table))
|
||||
(`(,begin . ,end)
|
||||
(if (and (chronometrist-iso-date-p begin) (chronometrist-iso-date-p end))
|
||||
(let ((begin-ts (chronometrist-iso-timestamp-to-ts begin))
|
||||
(end-ts (chronometrist-iso-timestamp-to-ts end)))
|
||||
(cl-loop while (not (ts> begin-ts end-ts))
|
||||
append (gethash (ts-format "%F" begin-ts) table)
|
||||
do (ts-adjustf begin-ts 'day 1)))))))
|
||||
|
||||
;; (chronometrist-details-intervals-for-range nil chronometrist-events)
|
||||
;; (chronometrist-details-intervals-for-range "2021-06-01" chronometrist-events)
|
||||
;; (chronometrist-details-intervals-for-range '("2021-06-01" . "2021-06-03") chronometrist-events)
|
||||
|
||||
#+END_SRC
|
||||
**** set-range :command:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
|
Reference in New Issue