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:
contrapunctus 2020-05-31 03:23:54 +05:30
parent e9a9209067
commit 6fb599104b
4 changed files with 89 additions and 78 deletions

View File

@ -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).

View File

@ -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)

View File

@ -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'."

View 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: