diff --git a/ci.sh b/ci.sh index cefc17d..959eef5 100755 --- a/ci.sh +++ b/ci.sh @@ -2,8 +2,10 @@ set -xe -emacs -batch -f package-initialize \ +emacs -Q -batch -f package-initialize \ --eval '(add-to-list (quote package-archives) (quote ("melpa" . "http://melpa.org/packages/")))' \ - --eval '(use-package alert :ensure t)' + --eval '(use-package alert :ensure t)' \ + --eval '(use-package org-ql :ensure t)' \ + --eval '(use-package ts :ensure t)' cd test && make test diff --git a/org-alert.el b/org-alert.el index 30ac443..91d3fbc 100644 --- a/org-alert.el +++ b/org-alert.el @@ -4,7 +4,7 @@ ;; Author: Stephen Pegoraro ;; Version: 0.2.0 -;; Package-Requires: ((org "9.0") (alert "1.2")) +;; Package-Requires: ((org "9.0") (alert "1.2") (org-ql "0.9-pre") (ts "0.2-pre")) ;; Keywords: org, org-mode, notify, notifications, calendar ;; URL: https://github.com/spegoraro/org-alert @@ -37,6 +37,8 @@ (require 'cl-lib) (require 'alert) (require 'org-agenda) +(require 'org-ql) +(require 'ts) (defgroup org-alert nil "Notify org deadlines via notify-send." @@ -69,10 +71,11 @@ If nil, never stop sending notifications." "SCHEDULED>=\"\"+SCHEDULED<\"\"|DEADLINE>=\"\"+DEADLINE<\"\"" "property/todo/tags match string to be passed to `org-map-entries'." :group 'org-alert - :type 'regexp) + :type '(choice (regexp :tag "Match Regexp") + (const :tag "Match All" nil))) (defcustom org-alert-time-match-string - "\\(?:SCHEDULED\\|DEADLINE\\):.*<.*\\([0-9]\\{2\\}:[0-9]\\{2\\}\\).*>" + "<.*\\([0-9]\\{2\\}:[0-9]\\{2\\}\\).*>" "regex to find times in an org subtree. The first capture group is used to extract the time" :group 'org-alert @@ -181,22 +184,40 @@ heading, the scheduled/deadline time, and the cutoff to apply" (alert head :title org-alert-notification-title :category org-alert-notification-category)))))) +(defun org-alert--get-after-event-cutoff-time () + "Get the time to how early we want to get the events." + (when org-alert-notify-after-event-cutoff + (ts-format "%F %T" + (ts-adjust 'minute (- org-alert-notify-after-event-cutoff) + (ts-now))))) + (defun org-alert--map-entries (func) (org-map-entries func org-alert-match-string 'agenda '(org-agenda-skip-entry-if 'todo org-done-keywords-for-agenda))) (defun org-alert-check () - "Check for active, due deadlines and initiate notifications." + "Check for active, due deadlines and initiate notifications using `org-ql'. +This will match org heading with active timestamp, from now, until the +next `org-alert-notify-cutoff' minutes." (interactive) - (org-alert--map-entries 'org-alert--dispatch) + (let ((org-ql-cache (make-hash-table))) + (org-ql-select (org-agenda-files) + `(or (ts-active :with-time t + :from ,(org-alert--get-after-event-cutoff-time) + :to ,(ts-format "%F %T" (ts-adjust 'minute org-alert-notify-cutoff (ts-now)))) + (and (property ,org-alert-cutoff-prop) + (ts-active :with-time t + :from ,(org-alert--get-after-event-cutoff-time)))) + :action #'org-alert--dispatch)) t) +;;;###autoload (defun org-alert-enable () "Enable the notification timer. Cancels existing timer if running." (interactive) (org-alert-disable) - (run-at-time 0 org-alert-interval 'org-alert-check)) + (run-at-time t org-alert-interval 'org-alert-check)) (defun org-alert-disable () "Cancel the running notification timer." diff --git a/test/Makefile b/test/Makefile index afd3519..df768d6 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,5 +1,5 @@ test: - emacs -batch -f package-initialize \ + emacs -Q -batch -f package-initialize \ -l ert \ -l alert \ -l ../org-alert.el \ diff --git a/test/test.el b/test/test.el index 29554aa..11f4269 100644 --- a/test/test.el +++ b/test/test.el @@ -66,7 +66,9 @@ a post-event cutoff set." a post-event cutoff set but the current time set appropriately." (with-test-org nil (with-current-time (25704 52667 0 0) ; 9:40:11 - (let ((org-alert-notify-after-event-cutoff 60)) + (let ( + ;; (org-alert-notify-after-event-cutoff 60) + ) (should (= (length test-alert-notifications) 0)) (org-alert-check) (should (= (length test-alert-notifications) 1)))))) @@ -78,7 +80,9 @@ a post-event cutoff set but the current time set appropriately." ;; (current-time-string '(25704 52655 0 0)) => "Sat May 20 09:39:59 2023" or ;; just before the notification should trigger (with-current-time (25704 52655 0 0) - (let ((org-alert-notify-after-event-cutoff 60)) + (let ( + ;; (org-alert-notify-after-event-cutoff 60) + ) (org-alert-check) (should (= (length test-alert-notifications) 0)))))) @@ -88,6 +92,14 @@ a post-event cutoff set but the current time set appropriately." (org-alert-check) (should (= (length test-alert-notifications) 1))))) +(ert-deftest check-alert-multiple () + (with-test-org "plain.org" + (with-current-time (25704 52957 0 0) ; 9:45:01 + (org-alert-check) + (org-alert-check) + (org-alert-check) + (should (= (length test-alert-notifications) 3))))) + (ert-deftest check-alert-none () (with-test-org "plain.org" (with-current-time (25704 52945 0 0) ; 9:44:49