Add: Timestamp selectors :on a number

This ought to be pretty thorough now.
This commit is contained in:
Adam Porter 2019-08-18 22:57:38 -05:00
parent c2ecbc67a5
commit 49d0fa8306
3 changed files with 90 additions and 7 deletions

View file

@ -207,15 +207,22 @@ Arguments are listed next to predicate names, where applicable.
:TOC: ignore :TOC: ignore
:END: :END:
All of these selectors take optional keyword arguments ~:from~, ~:to:~, and ~:on~. If ~:from~, return non-nil if entry has a timestamp on or after ~:from~. If ~:to~, return non-nil if entry has a timestamp on or before ~:to~. If ~:on~, return non-nil if entry has a timestamp on date ~:on~. Argument values should be either ~ts~ structs, or strings parseable by ~parse-time-string~ which may omit the time value. All of these selectors take optional keyword arguments ~:from~, ~:to:~, and ~:on~:
+ If ~:from~, return non-nil if entry has a timestamp on or after ~:from~.
+ If ~:to~, return non-nil if entry has a timestamp on or before ~:to~.
+ If ~:on~, return non-nil if entry has a timestamp on date ~:on~.
Argument values should be either a number of days (positive to look forward, or negative to look backward), a ~ts~ struct, or a string parseable by ~parse-time-string~ which may omit the time value.
*Selectors:*
+ ~ts~ :: Return non-nil if current entry has a timestamp in given period. If no arguments are specified, return non-nil if entry has any timestamp. + ~ts~ :: Return non-nil if current entry has a timestamp in given period. If no arguments are specified, return non-nil if entry has any timestamp.
+ ~ts-active~ :: Like ~ts~, but only matches active timestamps. + ~ts-active~ :: Like ~ts~, but only matches active timestamps.
+ ~ts-a~ :: Like ~ts~, but only matches active timestamps. + ~ts-a~ :: Like ~ts~, but only matches active timestamps.
+ ~ts-inactive~ :: Like ~ts~, but only matches inactive timestamps. + ~ts-inactive~ :: Like ~ts~, but only matches inactive timestamps.
+ ~ts-i~ :: Like ~ts~, but only matches inactive timestamps. + ~ts-i~ :: Like ~ts~, but only matches inactive timestamps.
The following selectors can also take a single argument, a number, which looks backward or forward a number of days. The number can also be negative to invert the direction. The following selectors, in addition to the keyword arguments, can also take a single argument, a number, which looks backward or forward a number of days. The number can be negative to invert the direction.
*Backward-looking:* *Backward-looking:*
+ ~clocked~ :: Return non-nil if current entry was clocked in given period. If no arguments are specified, return non-nil if entry was clocked at any time. Note: Clock entries are expected to be clocked out. Currently clocked entries (i.e. with unclosed timestamp ranges) are ignored. + ~clocked~ :: Return non-nil if current entry was clocked in given period. If no arguments are specified, return non-nil if entry was clocked at any time. Note: Clock entries are expected to be clocked out. Currently clocked entries (i.e. with unclosed timestamp ranges) are ignored.

View file

@ -313,14 +313,45 @@ Replaces bare strings with (regexp) selectors, and appropriate
(ts-apply :hour 23 :minute 59 :second 59)))) (ts-apply :hour 23 :minute 59 :second 59))))
`(,pred :to ,to))) `(,pred :to ,to)))
(`(,(and pred (or 'deadline 'scheduled 'planning)) (`(,(and pred (or 'deadline 'scheduled 'planning))
,(and type (or :from :to :on)) ,(and direction (or :from :to :on))
,(and num-days (pred numberp))) ,(and num-days (pred numberp)))
(let ((target (->> (ts-now) (let ((target (->> (ts-now)
(ts-adjust 'day num-days) (ts-adjust 'day num-days)
(ts-apply :hour 23 :minute 59 :second 59)))) (ts-apply :hour 23 :minute 59 :second 59))))
`(,pred ,type ,target))) `(,pred ,direction ,target)))
(`(,(and pred (or 'ts 'ts-a 'ts-i 'ts-active 'ts-inactive))
,(and direction (or :from :to))
,(and num-days (pred numberp)))
(-let* ((type (pcase-exhaustive pred
((or 'ts-i 'ts-inactive) 'inactive)
((or 'ts-a 'ts-active) 'active)
('ts 'both)))
((hour minute second) (pcase-exhaustive direction
(:from '(0 0 0))
(:to '(23 59 59))))
(ts (->> (ts-now)
(ts-adjust 'day num-days)
(ts-apply :hour hour :minute minute :second second))))
`(ts :type ,type ,direction ,ts)))
(`(,(and pred (or 'ts 'ts-a 'ts-i 'ts-active 'ts-inactive))
:on
,(and num-days (pred numberp)))
;; This rule is only for :on, because we must provide both :from and :to
;; timestamps to the (ts) selector for the --query-predicate function.
(-let* ((type (pcase-exhaustive pred
((or 'ts-i 'ts-inactive) 'inactive)
((or 'ts-a 'ts-active) 'active)
('ts 'both)))
(from (->> (ts-now)
(ts-adjust 'day num-days)
(ts-apply :hour 0 :minute 0 :second 0)))
(to (->> (ts-now)
(ts-adjust 'day num-days)
(ts-apply :hour 23 :minute 59 :second 59))))
`(ts :type ,type :from ,from :to ,to)))
(`(,(or 'ts-active 'ts-a) . ,rest) `(ts :type active ,@rest)) (`(,(or 'ts-active 'ts-a) . ,rest) `(ts :type active ,@rest))
(`(,(or 'ts-inactive 'ts-i) . ,rest) `(ts :type inactive ,@rest)) (`(,(or 'ts-inactive 'ts-i) . ,rest) `(ts :type inactive ,@rest))
;; Any other form: passed through unchanged.
(_ element)))) (_ element))))
(rec query))) (rec query)))

View file

@ -529,17 +529,32 @@ RESULTS should be a list of strings as returned by
(org-ql-expect ((ts :from "2019-06-08" :type active)) (org-ql-expect ((ts :from "2019-06-08" :type active))
nil)) nil))
(org-ql-it ":from a number of days"
(org-ql-then
(org-ql-expect ((ts :from 5))
'("Take over the universe" "Visit Mars" "Visit the moon" "Renew membership in supervillain club" "Internet" "Spaceship lease" "Rewrite Emacs in Common Lisp"))))
(org-ql-it ":to a timestamp" (org-ql-it ":to a timestamp"
(org-ql-expect ((ts :to "2019-06-10" :type active)) (org-ql-expect ((ts :to "2019-06-10" :type active))
'("Take over the universe" "Take over the world" "Skype with president of Antarctica" "Visit Mars" "Visit the moon" "Practice leaping tall buildings in a single bound" "Renew membership in supervillain club" "Order a pizza" "Get haircut" "Internet" "Spaceship lease" "Fix flux capacitor" "/r/emacs" "Shop for groceries" "Rewrite Emacs in Common Lisp")) '("Take over the universe" "Take over the world" "Skype with president of Antarctica" "Visit Mars" "Visit the moon" "Practice leaping tall buildings in a single bound" "Renew membership in supervillain club" "Order a pizza" "Get haircut" "Internet" "Spaceship lease" "Fix flux capacitor" "/r/emacs" "Shop for groceries" "Rewrite Emacs in Common Lisp"))
(org-ql-expect ((ts :to "2017-07-04" :type active)) (org-ql-expect ((ts :to "2017-07-04" :type active))
'("Skype with president of Antarctica"))) '("Skype with president of Antarctica")))
(org-ql-it ":to a number of days"
(org-ql-then
(org-ql-expect ((ts :to -1))
'("Skype with president of Antarctica"))))
(org-ql-it ":on a timestamp" (org-ql-it ":on a timestamp"
(org-ql-expect ((ts :on "2017-07-05" :type active)) (org-ql-expect ((ts :on "2017-07-05" :type active))
'("Practice leaping tall buildings in a single bound" "Order a pizza" "Get haircut" "Fix flux capacitor" "/r/emacs" "Shop for groceries" "Rewrite Emacs in Common Lisp")) '("Practice leaping tall buildings in a single bound" "Order a pizza" "Get haircut" "Fix flux capacitor" "/r/emacs" "Shop for groceries" "Rewrite Emacs in Common Lisp"))
(org-ql-expect ((ts :on "2019-06-09" :type active)) (org-ql-expect ((ts :on "2019-06-09" :type active))
nil))) nil))
(org-ql-it ":on a number of days"
(org-ql-then
(org-ql-expect ((ts-active :on 2))
'("Take over the world")))))
(describe "inactive" (describe "inactive"
@ -553,17 +568,32 @@ RESULTS should be a list of strings as returned by
(org-ql-expect ((ts :from "2019-06-08" :type inactive)) (org-ql-expect ((ts :from "2019-06-08" :type inactive))
nil)) nil))
(org-ql-it ":from a number of days"
(org-ql-then
(org-ql-expect ((ts-i :from 5))
'("Visit the moon" "Rewrite Emacs in Common Lisp"))))
(org-ql-it ":to a timestamp" (org-ql-it ":to a timestamp"
(org-ql-expect ((ts :to "2019-06-10" :type inactive)) (org-ql-expect ((ts :to "2019-06-10" :type inactive))
'("Test data" "Visit the moon" "Learn universal sign language" "Rewrite Emacs in Common Lisp")) '("Test data" "Visit the moon" "Learn universal sign language" "Rewrite Emacs in Common Lisp"))
(org-ql-expect ((ts :to "2017-07-04" :type inactive)) (org-ql-expect ((ts :to "2017-07-04" :type inactive))
'nil)) 'nil))
(org-ql-it ":to a number of days"
(org-ql-then
(org-ql-expect ((ts-i :to 5))
'("Test data" "Learn universal sign language"))))
(org-ql-it ":on a timestamp" (org-ql-it ":on a timestamp"
(org-ql-expect ((ts :on "2017-07-05" :type inactive)) (org-ql-expect ((ts :on "2017-07-05" :type inactive))
'("Test data" "Learn universal sign language")) '("Test data" "Learn universal sign language"))
(org-ql-expect ((ts :on "2019-06-09" :type inactive)) (org-ql-expect ((ts :on "2019-06-09" :type inactive))
nil))) nil))
(org-ql-it ":on a number of days"
(org-ql-then
(org-ql-expect ((ts-inactive :on 19))
'("Visit the moon" "Rewrite Emacs in Common Lisp")))))
(describe "both" (describe "both"
@ -584,6 +614,11 @@ RESULTS should be a list of strings as returned by
(org-ql-expect ((ts :from "2019-06-08" :type both)) (org-ql-expect ((ts :from "2019-06-08" :type both))
nil)) nil))
(org-ql-it ":from a number of days"
(org-ql-then
(org-ql-expect ((ts :from -5))
'("Test data" "Take over the universe" "Take over the world" "Skype with president of Antarctica" "Visit Mars" "Visit the moon" "Practice leaping tall buildings in a single bound" "Renew membership in supervillain club" "Learn universal sign language" "Order a pizza" "Get haircut" "Internet" "Spaceship lease" "Fix flux capacitor" "/r/emacs" "Shop for groceries" "Rewrite Emacs in Common Lisp"))))
(org-ql-it ":to a timestamp" (org-ql-it ":to a timestamp"
(org-ql-expect ((ts :to "2017-07-06")) (org-ql-expect ((ts :to "2017-07-06"))
'("Test data" "Skype with president of Antarctica" "Practice leaping tall buildings in a single bound" "Learn universal sign language" "Order a pizza" "Get haircut" "Fix flux capacitor" "/r/emacs" "Shop for groceries" "Rewrite Emacs in Common Lisp")) '("Test data" "Skype with president of Antarctica" "Practice leaping tall buildings in a single bound" "Learn universal sign language" "Order a pizza" "Get haircut" "Fix flux capacitor" "/r/emacs" "Shop for groceries" "Rewrite Emacs in Common Lisp"))
@ -594,6 +629,11 @@ RESULTS should be a list of strings as returned by
(org-ql-expect ((ts :to "2017-07-04" :type both)) (org-ql-expect ((ts :to "2017-07-04" :type both))
'("Skype with president of Antarctica"))) '("Skype with president of Antarctica")))
(org-ql-it ":to a number of days"
(org-ql-then
(org-ql-expect ((ts :to 5))
'("Test data" "Take over the world" "Skype with president of Antarctica" "Practice leaping tall buildings in a single bound" "Renew membership in supervillain club" "Learn universal sign language" "Order a pizza" "Get haircut" "Fix flux capacitor" "/r/emacs" "Shop for groceries" "Rewrite Emacs in Common Lisp"))))
(org-ql-it ":on a timestamp" (org-ql-it ":on a timestamp"
(org-ql-expect ((ts :on "2017-07-05")) (org-ql-expect ((ts :on "2017-07-05"))
'("Test data" "Practice leaping tall buildings in a single bound" "Learn universal sign language" "Order a pizza" "Get haircut" "Fix flux capacitor" "/r/emacs" "Shop for groceries" "Rewrite Emacs in Common Lisp")) '("Test data" "Practice leaping tall buildings in a single bound" "Learn universal sign language" "Order a pizza" "Get haircut" "Fix flux capacitor" "/r/emacs" "Shop for groceries" "Rewrite Emacs in Common Lisp"))
@ -602,7 +642,12 @@ RESULTS should be a list of strings as returned by
(org-ql-expect ((ts :on "2019-06-09")) (org-ql-expect ((ts :on "2019-06-09"))
nil) nil)
(org-ql-expect ((ts :on "2019-06-09" :type both)) (org-ql-expect ((ts :on "2019-06-09" :type both))
nil)))) nil))
(org-ql-it ":on a number of days"
(org-ql-then
(org-ql-expect ((ts :on 5))
'("Renew membership in supervillain club"))))))
(describe "Compound queries" (describe "Compound queries"