Redesign API; revert sexp-read to earlier behaviour
sexp-read goes back to not splitting events, not even when they cross the provided timestamps. Splitting events is left to data consuming functions via events-maybe-split, since not all of them need it. (e.g. history generation for tags/keys/values) Created filter-events-time
This commit is contained in:
parent
e9a9209067
commit
6fb599104b
|
@ -97,23 +97,22 @@ Where START and STOP are ISO-8601 timestamps."
|
|||
(defun chronometrist-events-maybe-split (event)
|
||||
"Split EVENT if it spans midnight.
|
||||
Return a list of two events if EVENT was split, else nil"
|
||||
(when (plist-get event :stop)
|
||||
(let ((split-time (chronometrist-midnight-spanning-p (chronometrist-iso-timestamp->ts
|
||||
(plist-get event :start))
|
||||
(chronometrist-iso-timestamp->ts
|
||||
(plist-get event :stop)))))
|
||||
(when split-time
|
||||
(-let ((((start-1 . stop-1) (start-2 . stop-2)) split-time)
|
||||
;; plist-put modifies lists in-place. The resulting bugs
|
||||
;; left me puzzled for a while.
|
||||
(event-1 (cl-copy-list event))
|
||||
(event-2 (cl-copy-list event)))
|
||||
(list (-> event-1
|
||||
(plist-put :start start-1)
|
||||
(plist-put :stop stop-1))
|
||||
(-> event-2
|
||||
(plist-put :start start-2)
|
||||
(plist-put :stop stop-2))))))))
|
||||
(let ((split-time (chronometrist-midnight-spanning-p (chronometrist-iso-timestamp->ts
|
||||
(plist-get event :start))
|
||||
(chronometrist-iso-timestamp->ts
|
||||
(plist-get event :stop)))))
|
||||
(when split-time
|
||||
(-let ((((start-1 . stop-1) (start-2 . stop-2)) split-time)
|
||||
;; plist-put modifies lists in-place. The resulting bugs
|
||||
;; left me puzzled for a while.
|
||||
(event-1 (cl-copy-list event))
|
||||
(event-2 (cl-copy-list event)))
|
||||
(list (-> event-1
|
||||
(plist-put :start start-1)
|
||||
(plist-put :stop stop-1))
|
||||
(-> event-2
|
||||
(plist-put :start start-2)
|
||||
(plist-put :stop stop-2)))))))
|
||||
|
||||
;; TODO - Maybe strip dates from values, since they're part of the key
|
||||
;; anyway. Consider using a state machine.
|
||||
|
@ -143,7 +142,6 @@ were none."
|
|||
;; to be replaced by plist-query
|
||||
(defun chronometrist-events-subset (start end)
|
||||
"Return a subset of `chronometrist-events'.
|
||||
|
||||
The subset will contain values between dates START and END (both
|
||||
inclusive).
|
||||
|
||||
|
|
|
@ -31,14 +31,23 @@ The data is obtained from `chronometrist-file', via `chronometrist-events'.
|
|||
TS should be a ts struct (see `ts.el').
|
||||
|
||||
The return value is seconds, as an integer."
|
||||
(let* ((events-today (chronometrist-sexp-read ts (ts-adjust 'day 1 ts)))
|
||||
(let* ((today-start ts)
|
||||
(today-end (ts-adjust 'day 1 ts))
|
||||
(events-today (chronometrist-sexp-read today-start today-end))
|
||||
(task-events (when events-today
|
||||
(chronometrist-filter-events-task events-today task))))
|
||||
(if task-events
|
||||
(->> (chronometrist-events->ts-pairs task-events)
|
||||
(chronometrist-ts-pairs->durations)
|
||||
(-reduce #'+)
|
||||
(truncate))
|
||||
(--> (cl-loop with new with list
|
||||
for event in task-events
|
||||
do (setq new (chronometrist-events-maybe-split event))
|
||||
if new append new into list
|
||||
else collect event into list
|
||||
finally return list)
|
||||
(chronometrist-filter-events-time it today-start today-end)
|
||||
(chronometrist-events->ts-pairs it)
|
||||
(chronometrist-ts-pairs->durations it)
|
||||
(-reduce #'+ it)
|
||||
(truncate it))
|
||||
;; no events for this task on TS, i.e. no time spent
|
||||
0)))
|
||||
|
||||
|
@ -70,10 +79,18 @@ EVENTS should be a list of property lists in the form (:name
|
|||
\"NAME\" :start START :stop STOP ...), where START and STOP are
|
||||
ISO-8601 time strings."
|
||||
(cl-loop for event in events
|
||||
when (equal (plist-get event :name)
|
||||
task)
|
||||
when (equal task (plist-get event :name))
|
||||
collect event))
|
||||
|
||||
(defun chronometrist-filter-events-time (events begin end)
|
||||
"From EVENTS, return the ones between BEGIN and END."
|
||||
(cl-loop with start with stop
|
||||
for event in events
|
||||
do (setq start (chronometrist-iso-timestamp->ts (plist-get event :start))
|
||||
stop (chronometrist-iso-timestamp->ts (plist-get event :stop)))
|
||||
when (or (ts<= begin start)
|
||||
(ts<= end stop))
|
||||
collect event))
|
||||
|
||||
(provide 'chronometrist-queries)
|
||||
|
||||
|
|
|
@ -32,51 +32,47 @@ If not supplied, TS-BEG is the beginning of today and TS-END is
|
|||
the beginning of tomorrow, i.e. the events for today are returned.
|
||||
|
||||
If TS-BEG or TS-END is between the :start and :stop time of an
|
||||
event, the event will be split into two, and only the matching
|
||||
event will be included. Thus, events returned will not span
|
||||
midnights."
|
||||
(chronometrist-sexp-in-file chronometrist-file
|
||||
(goto-char (point-max))
|
||||
;; there is no range - collect everything, maybe split
|
||||
(if (and (not ts-beg) (not ts-end))
|
||||
(cl-loop with expr with split-events with list do
|
||||
(if (bobp)
|
||||
(setq expr nil)
|
||||
(backward-list 1)
|
||||
(setq expr (read (current-buffer))
|
||||
split-events (chronometrist-events-maybe-split expr))
|
||||
(backward-list 1))
|
||||
while expr
|
||||
when split-events append split-events into list
|
||||
unless split-events collect expr into list
|
||||
finally return list)
|
||||
;; there is a range
|
||||
(cl-loop with expr with start with stop with split-events with list
|
||||
do (if (bobp)
|
||||
(setq expr nil)
|
||||
(backward-list 1)
|
||||
(setq expr (read (current-buffer)))
|
||||
(backward-list 1))
|
||||
;; loop till we reach the beginning of the range
|
||||
while
|
||||
(and expr
|
||||
(setq start (chronometrist-iso-timestamp->ts
|
||||
(plist-get expr :start))
|
||||
stop (plist-get expr :stop)
|
||||
stop (if stop (chronometrist-iso-timestamp->ts stop) (ts-now)))
|
||||
;; don't go past TS-BEG
|
||||
(or (ts> start ts-beg)
|
||||
(ts> stop ts-beg)))
|
||||
when (or (ts-in ts-beg ts-end start)
|
||||
(ts-in ts-beg ts-end stop))
|
||||
;; expr is within range
|
||||
collect (let ((split-events (chronometrist-events-maybe-split expr)))
|
||||
(cond ((ts< start ts-beg)
|
||||
(cl-second split-events))
|
||||
((ts< ts-end stop)
|
||||
(cl-first split-events))
|
||||
(t expr))) into list
|
||||
finally return list))))
|
||||
event, the event will be included. Thus, the first and the last
|
||||
events may extend outside the given range.
|
||||
|
||||
The events returned may also cross the
|
||||
`chronometrist-day-start-time', which can affect duration
|
||||
calculations. See `chronometrist-events-maybe-split'.
|
||||
|
||||
If the latest (first) event does not have a :stop property, it
|
||||
will be added with the current time as the value."
|
||||
(let ((no-range-p (and (not ts-beg) (not ts-end))))
|
||||
(chronometrist-sexp-in-file chronometrist-file
|
||||
(goto-char (point-max))
|
||||
(cl-loop
|
||||
with expr with start with stop
|
||||
do
|
||||
(if (bobp)
|
||||
(setq expr nil)
|
||||
(backward-list 1)
|
||||
(setq expr (read (current-buffer)))
|
||||
(backward-list 1))
|
||||
;; loop till we reach the beginning of the range
|
||||
while
|
||||
(and expr
|
||||
(setq start (chronometrist-iso-timestamp->ts
|
||||
(plist-get expr :start))
|
||||
stop (plist-get expr :stop)
|
||||
stop (if stop
|
||||
(chronometrist-iso-timestamp->ts stop)
|
||||
(let ((now (ts-now)))
|
||||
(plist-put expr :stop (chronometrist-ts->iso now))
|
||||
now)))
|
||||
;; don't go past TS-BEG
|
||||
(or no-range-p
|
||||
(ts> start ts-beg)
|
||||
(ts> stop ts-beg)))
|
||||
when
|
||||
(or no-range-p
|
||||
(ts-in ts-beg ts-end start)
|
||||
(ts-in ts-beg ts-end stop))
|
||||
;; expr is within range
|
||||
collect expr))))
|
||||
|
||||
(defun chronometrist-sexp-last ()
|
||||
"Return last s-expression from `chronometrist-file'."
|
||||
|
|
|
@ -12,26 +12,26 @@
|
|||
(before-all (setq chronometrist-file-old chronometrist-file
|
||||
chronometrist-file "tests/test.sexp"))
|
||||
(after-all (setq chronometrist-file chronometrist-file-old))
|
||||
(it "returns all events if no arguments are given, splitting ones which span midnights"
|
||||
(expect (length (chronometrist-sexp-read)) :to-equal 13))
|
||||
(it "returns all events if no arguments are given"
|
||||
(expect (length (chronometrist-sexp-read)) :to-equal 11))
|
||||
(it "returns events between a certain time"
|
||||
(expect (length
|
||||
(chronometrist-sexp-read (chronometrist-iso-date->ts "2020-05-10")
|
||||
(chronometrist-iso-date->ts "2020-05-11")))
|
||||
:to-equal 3)
|
||||
(expect (length
|
||||
(chronometrist-sexp-read (chronometrist-iso-date->ts "2020-05-02")
|
||||
(chronometrist-iso-date->ts "2020-05-05")))
|
||||
:to-equal 4))
|
||||
(it "splits events if they cross the given times"
|
||||
(chronometrist-sexp-read (chronometrist-iso-date->ts "2018-01-02")
|
||||
(chronometrist-iso-date->ts "2018-01-05")))
|
||||
:to-equal 2))
|
||||
(it "includes events whose start or end crosses the given ranges"
|
||||
(expect (chronometrist-sexp-read (chronometrist-iso-date->ts "2018-01-03")
|
||||
(chronometrist-iso-date->ts "2018-01-04"))
|
||||
:to-equal
|
||||
'((:name "Cooking"
|
||||
:start "2018-01-03T23:00:00+0530"
|
||||
:stop "2018-01-04T00:00:00+0530")
|
||||
:stop "2018-01-04T01:00:00+0530")
|
||||
(:name "Programming"
|
||||
:start "2018-01-03T00:00:00+0530"
|
||||
:start "2018-01-02T23:00:00+0530"
|
||||
:stop "2018-01-03T01:00:00+0530")))))
|
||||
|
||||
;; Local Variables:
|
||||
|
|
Reference in New Issue