Revert "added iterator blocks"

This reverts commit 4432784c2a.
This commit is contained in:
petrucci4prez 2018-06-24 17:47:07 -04:00
parent 4432784c2a
commit 0b3d02efad
2 changed files with 541 additions and 112 deletions

324
conf.el
View File

@ -603,14 +603,111 @@ todoitem which in turn has a parent which is a todoitem"
'("WAIT" "NEXT") '("WAIT" "NEXT")
"projects cannot have these todostates") "projects cannot have these todostates")
(defmacro nd/compare-statuscodes (op sc1 sc2 sc-list) (defconst nd/project-statuscodes
'(:archivable
:complete
:stuck
:held
:waiting
:active
:done-incomplete
:undone-complete
:invalid-todostate
:scheduled-project)
"list of statuscodes to be used in assessing projects
Note they are listed in order of priority (eg items further
down the list override higher items")
(defconst nd/complete-statuscodes
'(:archivable
:complete))
(defconst nd/todo-statuscodes
'(:stuck
:held
:waiting
:active))
(defconst nd/error-statuscodes
'(:done-incomplete
:undone-complete
:invalid-todostate
:scheduled-project))
(defmacro nd/compare-statuscodes0 (op sc1 sc2 sc-list)
`(,op (position ,sc1 ,sc-list) (position ,sc2 ,sc-list))) `(,op (position ,sc1 ,sc-list) (position ,sc2 ,sc-list)))
(defun nd/decend-into-project (allowed-statuscodes trans-tbl (defmacro nd/compare-statuscodes (operator statuscode-1 statuscode-2)
get-task-status) "syntactic suger to compare statuscodes by position"
`(,operator (position ,statuscode-1 nd/project-statuscodes)
(position ,statuscode-2 nd/project-statuscodes)))
(defun nd/status< (statuscode-1 statuscode-2)
"returns t is statuscode-1 is lesser priority than statuscode-2"
(nd/compare-statuscodes < statuscode-1 statuscode-2))
(defun nd/status> (statuscode-1 statuscode-2)
"returns t is statuscode-1 is greater priority than statuscode-2"
(nd/compare-statuscodes > statuscode-1 statuscode-2))
(defun nd/status= (statuscode-1 statuscode-2)
"returns t is statuscode-1 is equal priority than statuscode-2"
(nd/compare-statuscodes = statuscode-1 statuscode-2))
(defun nd/descend-into-project ()
"returns statuscode of project and recursively descends into subprojects"
(let ((project-state :archivable)
(previous-point))
(save-excursion
(setq previous-point (point))
(outline-next-heading)
;; loop breaks if active or higher priority
;; note that all invalid statuscodes are higher
;; thus this function will only return the first
;; encountered error
(while (and (nd/status< project-state :active)
(> (point) previous-point))
(let ((keyword (nd/is-todoitem-p)))
(if keyword
(let ((cur-state
(if (nd/heading-has-children 'nd/is-todoitem-p)
(cond ((member keyword nd/project-invalid-todostates) :invalid-todostate)
((nd/is-scheduled-heading-p) :scheduled-project)
((equal keyword "CANC") (if (nd/is-archivable-heading-p)
:archivable
:complete))
((equal keyword "HOLD") :held)
(t (let ((child-statuscode (nd/descend-into-project)))
(cond ((equal keyword "TODO")
(if (nd/status> child-statuscode :complete)
child-statuscode
:undone-complete))
(t (case child-statuscode
(:complete :complete)
(:archivable (if (nd/is-archivable-heading-p)
:archivable
:complete))
(t (if (nd/status= child-statuscode :complete)
:complete
:done-incomplete))))))))
(cond ((equal keyword "HOLD") :held)
((equal keyword "WAIT") :waiting)
((equal keyword "NEXT") :active)
((and (equal keyword "TODO") (nd/is-scheduled-heading-p)) :active)
((equal keyword "TODO") :stuck)
((nd/is-archivable-heading-p) :archivable)
(t :complete)))))
(if (nd/status> cur-state project-state)
(setq project-state cur-state)))))
(setq previous-point (point))
(org-forward-heading-same-level 1 t)))
project-state))
(defun nd/decend-into-project0 (allowed-statuscodes trans-tbl get-task-status)
(let ((project-status (first allowed-statuscodes)) (let ((project-status (first allowed-statuscodes))
(breaker-status (car (last allowed-statuscodes))) (breaker-status (car (last allowed-statuscodes)))
(previous-point)) (previous-point))
;; (message "hi")
(save-excursion (save-excursion
(setq previous-point (point)) (setq previous-point (point))
(outline-next-heading) (outline-next-heading)
@ -636,7 +733,10 @@ todoitem which in turn has a parent which is a todoitem"
;; to obtain status of task ;; to obtain status of task
(nth (funcall get-task-status keyword) (nth (funcall get-task-status keyword)
allowed-statuscodes)))) allowed-statuscodes))))
(if (nd/compare-statuscodes > new-status project-status allowed-statuscodes) ;; (message (format "%s" (concat "new status: " (symbol-name new-status))))
;; (message (format "%s" (concat "project status: " (symbol-name project-status))))
;; (message (format "%s" keyword))
(if (nd/compare-statuscodes0 > new-status project-status allowed-statuscodes)
(setq project-status new-status))))) (setq project-status new-status)))))
(setq previous-point (point)) (setq previous-point (point))
(org-forward-heading-same-level 1 t))) (org-forward-heading-same-level 1 t)))
@ -654,7 +754,7 @@ todoitem which in turn has a parent which is a todoitem"
;; these require descending into the project subtasks ;; these require descending into the project subtasks
((equal keyword "CANC") ((equal keyword "CANC")
(nd/decend-into-project (nd/decend-into-project0
'(:archivable :complete) '(:archivable :complete)
'((:stuck . 1) '((:stuck . 1)
(:held . 1) (:held . 1)
@ -669,7 +769,7 @@ todoitem which in turn has a parent which is a todoitem"
(nd/is-archivable-heading-p)) 0 1)))) (nd/is-archivable-heading-p)) 0 1))))
((equal keyword "DONE") ((equal keyword "DONE")
(nd/decend-into-project (nd/decend-into-project0
'(:archivable :complete :done-incomplete) '(:archivable :complete :done-incomplete)
'((:stuck . 2) '((:stuck . 2)
(:held . 2) (:held . 2)
@ -684,7 +784,7 @@ todoitem which in turn has a parent which is a todoitem"
2)))) 2))))
((equal keyword "TODO") ((equal keyword "TODO")
(nd/decend-into-project (nd/decend-into-project0
'(:undone-complete :stuck :held :waiting :active) '(:undone-complete :stuck :held :waiting :active)
'((:complete . 0) '((:complete . 0)
(:archivable . 0) (:archivable . 0)
@ -701,6 +801,70 @@ todoitem which in turn has a parent which is a todoitem"
(t (error (concat "invalid keyword detected: " keyword)))))) (t (error (concat "invalid keyword detected: " keyword))))))
(defmacro nd/is-project-keyword-status-p (test-keyword operator statuscode)
"tests if a project has toplevel heading of top-keyword and
child status equal to status code and returns keyword if
both are true"
`(and
(equal ,keyword ,test-keyword)
(nd/compare-statuscodes ,operator (nd/descend-into-project) ,statuscode)))
(defun nd/is-project-status-p (statuscode)
"Returns t if project matches statuscode given.
Note that this assumes the headline being tested is a valid project"
(case statuscode
;; projects closed more than 30 days ago
;; note CANC overrides all subtasks/projects
(:archivable
(if (nd/is-archivable-heading-p)
(or (equal keyword "CANC")
(nd/is-project-keyword-status-p "DONE" = :archivable))))
;; projects closed less than 30 days ago
;; note CANC overrides all subtasks/projects
(:complete
(if (not (nd/is-archivable-heading-p))
(or (equal keyword "CANC")
(nd/is-project-keyword-status-p "DONE" = :complete))))
;; projects with no waiting, held, or active components
(:stuck
(nd/is-project-keyword-status-p "TODO" = :stuck))
;; held projects
;; note toplevel HOLD overrides all subtasks/projects
(:held
(or (equal keyword "HOLD")
(nd/is-project-keyword-status-p "TODO" = :held)))
;; projects with at least one waiting component
(:waiting
(nd/is-project-keyword-status-p "TODO" = :waiting))
;; projects with at least one active component
(:active
(nd/is-project-keyword-status-p "TODO" = :active))
;; projects marked DONE but still have undone subtasks
(:done-incomplete
(nd/is-project-keyword-status-p "DONE" > :complete))
;; projects marked TODO but all subtasks are done
(:undone-complete
(nd/is-project-keyword-status-p "TODO" < :stuck))
;; projects with invalid todo keywords
(:invalid-todostate
(member keyword nd/project-invalid-todostates))
;; projects with scheduled heading (only subtasks should be scheduled)
(:scheduled-project
(nd/is-scheduled-heading-p))
;; error if not known
(t (if (not (member statuscode nd/project-statuscodes))
(error "unknown statuscode")))))
(defconst nd/iter-future-time (* 7 24 60 60)) (defconst nd/iter-future-time (* 7 24 60 60))
(defconst nd/iter-statuscodes '(:uninit :empty :active)) (defconst nd/iter-statuscodes '(:uninit :empty :active))
@ -890,6 +1054,18 @@ tags that do not have tags in neg-tags-list"
nd/is-task-p nd/is-task-p
(not (nd/heading-has-effort-p)))) (not (nd/heading-has-effort-p))))
(defun nd/skip-projects-without-statuscode (statuscode)
(save-restriction
(widen)
(let ((keyword (nd/is-project-p)))
(if keyword
(if (and nd/agenda-limit-project-toplevel
(nd/heading-has-parent 'nd/is-todoitem-p))
(nd/skip-subtree)
(if (not (nd/is-project-status-p statuscode))
(nd/skip-heading)))
(nd/skip-heading)))))
(defun nd/skip-non-projects () (defun nd/skip-non-projects ()
(save-restriction (save-restriction
(widen) (widen)
@ -957,12 +1133,14 @@ tasks with context tags"
((< pa pb) -1))))) ((< pa pb) -1)))))
(defun nd/org-agenda-filter-status (filter status-fun a-line) (defun nd/org-agenda-filter-status (filter status-fun a-line)
"Filter for org-agenda-before-sorting-filter-function. "Filter for org-agenda-before-sorting-filter-function intended for
agenda project views (eg makes the assumption that all entries are
from projects in the original org buffer)
Will go to the original org buffer and apply status-fun to determine Will go to the original org buffer and determine the project status
the status after which it will check if status is in filter. If true, after which it will check if status is in filter. If true, the flag
the flag string in the prefix is replaced with the status and the string in the prefix is replaced with the status and the status is
status is set as a text property for further sorting" set as a text property for further sorting"
(let* ((m (get-text-property 1 'org-marker a-line)) (let* ((m (get-text-property 1 'org-marker a-line))
(s (with-current-buffer (marker-buffer m) (s (with-current-buffer (marker-buffer m)
(goto-char m) (goto-char m)
@ -973,9 +1151,6 @@ status is set as a text property for further sorting"
nil 'project-status s)))) nil 'project-status s))))
(defun nd/org-agenda-sort-prop (prop order a b) (defun nd/org-agenda-sort-prop (prop order a b)
"Custom agenda sorting function that looks at property prop and
determines order based on the position of the prop return value in
order (a list of properties as either symbols or strings)"
(let* ((ta (get-text-property 1 prop a)) (let* ((ta (get-text-property 1 prop a))
(tb (get-text-property 1 prop b)) (tb (get-text-property 1 prop b))
(pa (position ta order :test (if (stringp ta) #'equal))) (pa (position ta order :test (if (stringp ta) #'equal)))
@ -1000,17 +1175,16 @@ order (a list of properties as either symbols or strings)"
(org-agenda-todo-ignore-with-date t) (org-agenda-todo-ignore-with-date t)
(org-agenda-sorting-strategy ,sort)))) (org-agenda-sorting-strategy ,sort))))
(defun nd/agenda-base-status-cmd (match header skip-fun sc-list sc-fun) (defun nd/agenda-base-proj-cmd (match header statuscode)
,(tags-todo `(tags-todo
,match ,match
((org-agenda-overriding-header ,header) ((org-agenda-overriding-header
(org-agenda-skip-function ,skip-fun) (concat (and nd/agenda-limit-project-toplevel "Toplevel ") ,header))
(org-agenda-before-sorting-filter-function (org-agenda-skip-function '(nd/skip-projects-without-statuscode ,statuscode))
(lambda (l) (nd/org-agenda-filter-status ,sc-list ,sc-fun l))) ;;(org-agenda-before-sorting-filter-function 'nd/sorting-filter-demo)
(org-agenda-cmp-user-defined ;; (nd/apply-statuscodes t)
(lambda (a b) (nd/org-agenda-sort-prop 'project-status ,sc-list a b))) ;; (org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")"))) (org-agenda-sorting-strategy '(category-keep)))))
(org-agenda-sorting-strategy '(user-defined-down category-keep)))))
(let* ((actionable "-NA-REFILE-%inc") (let* ((actionable "-NA-REFILE-%inc")
(periodical "PARENT_TYPE=\"periodical\"") (periodical "PARENT_TYPE=\"periodical\"")
@ -1035,14 +1209,34 @@ order (a list of properties as either symbols or strings)"
("p" ("p"
"Project View" "Project View"
,(nd/agenda-base-status-cmd ((tags-todo
(concat (and nd/agenda-limit-project-toplevel "Toplevel ") "Projects")
,act-no-rep-match ,act-no-rep-match
''nd/skip-non-projects ((org-agenda-overriding-header
'(:scheduled-project :invalid-todostate :undone-complete (concat (and
:done-incomplete :stuck :waiting nd/agenda-limit-project-toplevel "Toplevel ")
:held :active) "Projects"))
'nd/get-project-status)) (org-agenda-skip-function '(nd/skip-non-projects))
(org-agenda-before-sorting-filter-function
(lambda (l) (nd/org-agenda-filter-status '(:stuck :waiting :held :active) 'nd/get-project-status l)))
(org-agenda-cmp-user-defined
(lambda (a b) (nd/org-agenda-sort-prop 'project-status '(:stuck :waiting :active :held) a b)))
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
(org-agenda-sorting-strategy '(user-defined-down category-keep))))))
;; ("p"
;; "Project View"
;; (,(nd/agenda-base-proj-cmd act-no-rep-match
;; "Stuck Projects"
;; :stuck)
;; ,(nd/agenda-base-proj-cmd act-no-rep-match
;; "Waiting Projects"
;; :waiting)
;; ,(nd/agenda-base-proj-cmd act-no-rep-match
;; "Active Projects"
;; :active)
;; ,(nd/agenda-base-proj-cmd act-no-rep-match
;; "Held Projects"
;; :held)))
("P" ("P"
"Periodical View" "Periodical View"
@ -1080,47 +1274,75 @@ order (a list of properties as either symbols or strings)"
,(nd/agenda-base-task-cmd "-NA-REFILE+%inc/!" ,(nd/agenda-base-task-cmd "-NA-REFILE+%inc/!"
"Incubated Tasks" "Incubated Tasks"
''nd/skip-non-atomic-tasks) ''nd/skip-non-atomic-tasks)
,(nd/agenda-base-status-cmd (tags-todo
(concat (and nd/agenda-limit-project-toplevel "Toplevel ") "Incubated Projects")
"-NA-REFILE+%inc/!" "-NA-REFILE+%inc/!"
''nd/skip-non-projects ((org-agenda-overriding-header
'(:scheduled-project :invalid-todostate :undone-complete (concat (and
:done-incomplete :stuck :waiting nd/agenda-limit-project-toplevel "Toplevel ")
:held :active) "Incubated Projects"))
'nd/get-project-status))) (org-agenda-skip-function '(nd/skip-non-projects))
(org-agenda-before-sorting-filter-function
(lambda (l) (nd/org-agenda-filter-status '(:stuck :waiting :held :active) 'nd/get-project-status l)))
(org-agenda-cmp-user-defined
(lambda (a b) (nd/org-agenda-sort-prop 'project-status '(:stuck :waiting :active :held) a b)))
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
(org-agenda-sorting-strategy '(user-defined-down category-keep))))))
("r" ("r"
"Refile and Errors" "Refile and Critical Errors"
((tags "REFILE" ((tags "REFILE"
((org-agenda-overriding-header "Tasks to Refile")) ((org-agenda-overriding-header "Tasks to Refile"))
(org-tags-match-list-sublevels nil)) (org-tags-match-list-sublevels nil))
,(nd/agenda-base-task-cmd task-match ,(nd/agenda-base-task-cmd task-match
"Discontinous Project" "Discontinous Project"
''nd/skip-non-discontinuous-project-tasks) ''nd/skip-non-discontinuous-project-tasks)))
,(nd/agenda-base-header-cmd task-match
("e"
"Non-critical Errors"
(,(nd/agenda-base-header-cmd task-match
"Undone Closed" "Undone Closed"
''nd/skip-non-undone-closed-todoitems) ''nd/skip-non-undone-closed-todoitems)
,(nd/agenda-base-header-cmd (concat actionable "-" periodical) ,(nd/agenda-base-header-cmd (concat actionable "-" periodical)
"Done Unclosed" "Done Unclosed"
''nd/skip-non-done-unclosed-todoitems))) ''nd/skip-non-done-unclosed-todoitems)
(tags-todo
,act-no-rep-match
((org-agenda-overriding-header
(concat (and
nd/agenda-limit-project-toplevel "Toplevel ")
"Project Errors"))
(org-agenda-skip-function '(nd/skip-non-projects))
(org-agenda-before-sorting-filter-function
(lambda (l) (nd/org-agenda-filter-status '(:scheduled-project :invalid-todostate :undone-complete :done-incomplete) 'nd/get-project-status l)))
(org-agenda-cmp-user-defined
(lambda (a b) (nd/org-agenda-sort-prop 'project-status '(:scheduled-project :invalid-todostate :undone-complete :done-incomplete) a b)))
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
(org-agenda-sorting-strategy '(user-defined-down category-keep))))))
("A" ("A"
"Archivable Tasks and Projects" "Archivable Tasks and Projects"
(,(nd/agenda-base-header-cmd (concat actionable "-" periodical "-" iterator) (,(nd/agenda-base-header-cmd (concat actionable "-" periodical)
"Archivable Atomic Tasks" "Archivable Atomic Tasks"
''nd/skip-non-archivable-atomic-tasks) ''nd/skip-non-archivable-atomic-tasks)
,(nd/agenda-base-header-cmd (concat actionable "-" periodical) ,(nd/agenda-base-header-cmd (concat actionable "-" periodical)
"Stale Tasks" "Stale Tasks"
''nd/skip-non-stale-headings) ''nd/skip-non-stale-headings)
,(nd/agenda-base-header-cmd (concat actionable "-" periodical "+" iterator) ,(nd/agenda-base-proj-cmd (concat actionable "-" periodical "+" iterator)
"Archivable Iterators" "Archivable Iterators"
''nd/skip-non-archivable-atomic-tasks) :archivable)
,(nd/agenda-base-status-cmd (tags-todo
(concat (and nd/agenda-limit-project-toplevel "Toplevel ") "Archivable Projects")
,(concat actionable "-" periodical "-" iterator) ,(concat actionable "-" periodical "-" iterator)
''nd/skip-non-projects ((org-agenda-overriding-header
'(:archivable) (concat (and
'nd/get-project-status)))))) nd/agenda-limit-project-toplevel "Toplevel ")
"Archivable Projects"))
(org-agenda-skip-function '(nd/skip-non-projects))
(org-agenda-before-sorting-filter-function
(lambda (l) (nd/org-agenda-filter-status '(:archivable) 'nd/get-project-status l)))
(org-agenda-cmp-user-defined
(lambda (a b) (nd/org-agenda-sort-prop 'project-status '(:archivable) a b)))
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
(org-agenda-sorting-strategy '(user-defined-down category-keep)))))))))
(setq org-agenda-start-on-weekday 0) (setq org-agenda-start-on-weekday 0)
(setq org-agenda-span 'day) (setq org-agenda-span 'day)

309
conf.org
View File

@ -832,14 +832,111 @@ Returns t if heading has certain relationship to other headings
'("WAIT" "NEXT") '("WAIT" "NEXT")
"projects cannot have these todostates") "projects cannot have these todostates")
(defmacro nd/compare-statuscodes (op sc1 sc2 sc-list) (defconst nd/project-statuscodes
'(:archivable
:complete
:stuck
:held
:waiting
:active
:done-incomplete
:undone-complete
:invalid-todostate
:scheduled-project)
"list of statuscodes to be used in assessing projects
Note they are listed in order of priority (eg items further
down the list override higher items")
(defconst nd/complete-statuscodes
'(:archivable
:complete))
(defconst nd/todo-statuscodes
'(:stuck
:held
:waiting
:active))
(defconst nd/error-statuscodes
'(:done-incomplete
:undone-complete
:invalid-todostate
:scheduled-project))
(defmacro nd/compare-statuscodes0 (op sc1 sc2 sc-list)
`(,op (position ,sc1 ,sc-list) (position ,sc2 ,sc-list))) `(,op (position ,sc1 ,sc-list) (position ,sc2 ,sc-list)))
(defun nd/decend-into-project (allowed-statuscodes trans-tbl (defmacro nd/compare-statuscodes (operator statuscode-1 statuscode-2)
get-task-status) "syntactic suger to compare statuscodes by position"
`(,operator (position ,statuscode-1 nd/project-statuscodes)
(position ,statuscode-2 nd/project-statuscodes)))
(defun nd/status< (statuscode-1 statuscode-2)
"returns t is statuscode-1 is lesser priority than statuscode-2"
(nd/compare-statuscodes < statuscode-1 statuscode-2))
(defun nd/status> (statuscode-1 statuscode-2)
"returns t is statuscode-1 is greater priority than statuscode-2"
(nd/compare-statuscodes > statuscode-1 statuscode-2))
(defun nd/status= (statuscode-1 statuscode-2)
"returns t is statuscode-1 is equal priority than statuscode-2"
(nd/compare-statuscodes = statuscode-1 statuscode-2))
(defun nd/descend-into-project ()
"returns statuscode of project and recursively descends into subprojects"
(let ((project-state :archivable)
(previous-point))
(save-excursion
(setq previous-point (point))
(outline-next-heading)
;; loop breaks if active or higher priority
;; note that all invalid statuscodes are higher
;; thus this function will only return the first
;; encountered error
(while (and (nd/status< project-state :active)
(> (point) previous-point))
(let ((keyword (nd/is-todoitem-p)))
(if keyword
(let ((cur-state
(if (nd/heading-has-children 'nd/is-todoitem-p)
(cond ((member keyword nd/project-invalid-todostates) :invalid-todostate)
((nd/is-scheduled-heading-p) :scheduled-project)
((equal keyword "CANC") (if (nd/is-archivable-heading-p)
:archivable
:complete))
((equal keyword "HOLD") :held)
(t (let ((child-statuscode (nd/descend-into-project)))
(cond ((equal keyword "TODO")
(if (nd/status> child-statuscode :complete)
child-statuscode
:undone-complete))
(t (case child-statuscode
(:complete :complete)
(:archivable (if (nd/is-archivable-heading-p)
:archivable
:complete))
(t (if (nd/status= child-statuscode :complete)
:complete
:done-incomplete))))))))
(cond ((equal keyword "HOLD") :held)
((equal keyword "WAIT") :waiting)
((equal keyword "NEXT") :active)
((and (equal keyword "TODO") (nd/is-scheduled-heading-p)) :active)
((equal keyword "TODO") :stuck)
((nd/is-archivable-heading-p) :archivable)
(t :complete)))))
(if (nd/status> cur-state project-state)
(setq project-state cur-state)))))
(setq previous-point (point))
(org-forward-heading-same-level 1 t)))
project-state))
(defun nd/decend-into-project0 (allowed-statuscodes trans-tbl get-task-status)
(let ((project-status (first allowed-statuscodes)) (let ((project-status (first allowed-statuscodes))
(breaker-status (car (last allowed-statuscodes))) (breaker-status (car (last allowed-statuscodes)))
(previous-point)) (previous-point))
;; (message "hi")
(save-excursion (save-excursion
(setq previous-point (point)) (setq previous-point (point))
(outline-next-heading) (outline-next-heading)
@ -865,7 +962,10 @@ Returns t if heading has certain relationship to other headings
;; to obtain status of task ;; to obtain status of task
(nth (funcall get-task-status keyword) (nth (funcall get-task-status keyword)
allowed-statuscodes)))) allowed-statuscodes))))
(if (nd/compare-statuscodes > new-status project-status allowed-statuscodes) ;; (message (format "%s" (concat "new status: " (symbol-name new-status))))
;; (message (format "%s" (concat "project status: " (symbol-name project-status))))
;; (message (format "%s" keyword))
(if (nd/compare-statuscodes0 > new-status project-status allowed-statuscodes)
(setq project-status new-status))))) (setq project-status new-status)))))
(setq previous-point (point)) (setq previous-point (point))
(org-forward-heading-same-level 1 t))) (org-forward-heading-same-level 1 t)))
@ -883,7 +983,7 @@ Returns t if heading has certain relationship to other headings
;; these require descending into the project subtasks ;; these require descending into the project subtasks
((equal keyword "CANC") ((equal keyword "CANC")
(nd/decend-into-project (nd/decend-into-project0
'(:archivable :complete) '(:archivable :complete)
'((:stuck . 1) '((:stuck . 1)
(:held . 1) (:held . 1)
@ -898,7 +998,7 @@ Returns t if heading has certain relationship to other headings
(nd/is-archivable-heading-p)) 0 1)))) (nd/is-archivable-heading-p)) 0 1))))
((equal keyword "DONE") ((equal keyword "DONE")
(nd/decend-into-project (nd/decend-into-project0
'(:archivable :complete :done-incomplete) '(:archivable :complete :done-incomplete)
'((:stuck . 2) '((:stuck . 2)
(:held . 2) (:held . 2)
@ -913,7 +1013,7 @@ Returns t if heading has certain relationship to other headings
2)))) 2))))
((equal keyword "TODO") ((equal keyword "TODO")
(nd/decend-into-project (nd/decend-into-project0
'(:undone-complete :stuck :held :waiting :active) '(:undone-complete :stuck :held :waiting :active)
'((:complete . 0) '((:complete . 0)
(:archivable . 0) (:archivable . 0)
@ -929,6 +1029,70 @@ Returns t if heading has certain relationship to other headings
(t 0))))) (t 0)))))
(t (error (concat "invalid keyword detected: " keyword)))))) (t (error (concat "invalid keyword detected: " keyword))))))
(defmacro nd/is-project-keyword-status-p (test-keyword operator statuscode)
"tests if a project has toplevel heading of top-keyword and
child status equal to status code and returns keyword if
both are true"
`(and
(equal ,keyword ,test-keyword)
(nd/compare-statuscodes ,operator (nd/descend-into-project) ,statuscode)))
(defun nd/is-project-status-p (statuscode)
"Returns t if project matches statuscode given.
Note that this assumes the headline being tested is a valid project"
(case statuscode
;; projects closed more than 30 days ago
;; note CANC overrides all subtasks/projects
(:archivable
(if (nd/is-archivable-heading-p)
(or (equal keyword "CANC")
(nd/is-project-keyword-status-p "DONE" = :archivable))))
;; projects closed less than 30 days ago
;; note CANC overrides all subtasks/projects
(:complete
(if (not (nd/is-archivable-heading-p))
(or (equal keyword "CANC")
(nd/is-project-keyword-status-p "DONE" = :complete))))
;; projects with no waiting, held, or active components
(:stuck
(nd/is-project-keyword-status-p "TODO" = :stuck))
;; held projects
;; note toplevel HOLD overrides all subtasks/projects
(:held
(or (equal keyword "HOLD")
(nd/is-project-keyword-status-p "TODO" = :held)))
;; projects with at least one waiting component
(:waiting
(nd/is-project-keyword-status-p "TODO" = :waiting))
;; projects with at least one active component
(:active
(nd/is-project-keyword-status-p "TODO" = :active))
;; projects marked DONE but still have undone subtasks
(:done-incomplete
(nd/is-project-keyword-status-p "DONE" > :complete))
;; projects marked TODO but all subtasks are done
(:undone-complete
(nd/is-project-keyword-status-p "TODO" < :stuck))
;; projects with invalid todo keywords
(:invalid-todostate
(member keyword nd/project-invalid-todostates))
;; projects with scheduled heading (only subtasks should be scheduled)
(:scheduled-project
(nd/is-scheduled-heading-p))
;; error if not known
(t (if (not (member statuscode nd/project-statuscodes))
(error "unknown statuscode")))))
#+END_SRC #+END_SRC
**** iterator testing **** iterator testing
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
@ -1183,6 +1347,18 @@ Projects are handled quite simply. They have statuscodes
for which I test, and this can all be handled by one function. for which I test, and this can all be handled by one function.
Note that this is used for "normal" projects as well as iterators Note that this is used for "normal" projects as well as iterators
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(defun nd/skip-projects-without-statuscode (statuscode)
(save-restriction
(widen)
(let ((keyword (nd/is-project-p)))
(if keyword
(if (and nd/agenda-limit-project-toplevel
(nd/heading-has-parent 'nd/is-todoitem-p))
(nd/skip-subtree)
(if (not (nd/is-project-status-p statuscode))
(nd/skip-heading)))
(nd/skip-heading)))))
(defun nd/skip-non-projects () (defun nd/skip-non-projects ()
(save-restriction (save-restriction
(widen) (widen)
@ -1256,12 +1432,14 @@ tasks with context tags"
*** custom commands *** custom commands
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(defun nd/org-agenda-filter-status (filter status-fun a-line) (defun nd/org-agenda-filter-status (filter status-fun a-line)
"Filter for org-agenda-before-sorting-filter-function. "Filter for org-agenda-before-sorting-filter-function intended for
agenda project views (eg makes the assumption that all entries are
from projects in the original org buffer)
Will go to the original org buffer and apply status-fun to determine Will go to the original org buffer and determine the project status
the status after which it will check if status is in filter. If true, after which it will check if status is in filter. If true, the flag
the flag string in the prefix is replaced with the status and the string in the prefix is replaced with the status and the status is
status is set as a text property for further sorting" set as a text property for further sorting"
(let* ((m (get-text-property 1 'org-marker a-line)) (let* ((m (get-text-property 1 'org-marker a-line))
(s (with-current-buffer (marker-buffer m) (s (with-current-buffer (marker-buffer m)
(goto-char m) (goto-char m)
@ -1272,9 +1450,6 @@ status is set as a text property for further sorting"
nil 'project-status s)))) nil 'project-status s))))
(defun nd/org-agenda-sort-prop (prop order a b) (defun nd/org-agenda-sort-prop (prop order a b)
"Custom agenda sorting function that looks at property prop and
determines order based on the position of the prop return value in
order (a list of properties as either symbols or strings)"
(let* ((ta (get-text-property 1 prop a)) (let* ((ta (get-text-property 1 prop a))
(tb (get-text-property 1 prop b)) (tb (get-text-property 1 prop b))
(pa (position ta order :test (if (stringp ta) #'equal))) (pa (position ta order :test (if (stringp ta) #'equal)))
@ -1299,17 +1474,16 @@ order (a list of properties as either symbols or strings)"
(org-agenda-todo-ignore-with-date t) (org-agenda-todo-ignore-with-date t)
(org-agenda-sorting-strategy ,sort)))) (org-agenda-sorting-strategy ,sort))))
(defun nd/agenda-base-status-cmd (match header skip-fun sc-list sc-fun) (defun nd/agenda-base-proj-cmd (match header statuscode)
,(tags-todo `(tags-todo
,match ,match
((org-agenda-overriding-header ,header) ((org-agenda-overriding-header
(org-agenda-skip-function ,skip-fun) (concat (and nd/agenda-limit-project-toplevel "Toplevel ") ,header))
(org-agenda-before-sorting-filter-function (org-agenda-skip-function '(nd/skip-projects-without-statuscode ,statuscode))
(lambda (l) (nd/org-agenda-filter-status ,sc-list ,sc-fun l))) ;;(org-agenda-before-sorting-filter-function 'nd/sorting-filter-demo)
(org-agenda-cmp-user-defined ;; (nd/apply-statuscodes t)
(lambda (a b) (nd/org-agenda-sort-prop 'project-status ,sc-list a b))) ;; (org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")"))) (org-agenda-sorting-strategy '(category-keep)))))
(org-agenda-sorting-strategy '(user-defined-down category-keep)))))
(let* ((actionable "-NA-REFILE-%inc") (let* ((actionable "-NA-REFILE-%inc")
(periodical "PARENT_TYPE=\"periodical\"") (periodical "PARENT_TYPE=\"periodical\"")
@ -1334,14 +1508,19 @@ order (a list of properties as either symbols or strings)"
("p" ("p"
"Project View" "Project View"
,(nd/agenda-base-status-cmd ((tags-todo
(concat (and nd/agenda-limit-project-toplevel "Toplevel ") "Projects")
,act-no-rep-match ,act-no-rep-match
''nd/skip-non-projects ((org-agenda-overriding-header
'(:scheduled-project :invalid-todostate :undone-complete (concat (and
:done-incomplete :stuck :waiting nd/agenda-limit-project-toplevel "Toplevel ")
:held :active) "Projects"))
'nd/get-project-status)) (org-agenda-skip-function '(nd/skip-non-projects))
(org-agenda-before-sorting-filter-function
(lambda (l) (nd/org-agenda-filter-status '(:stuck :waiting :held :active) 'nd/get-project-status l)))
(org-agenda-cmp-user-defined
(lambda (a b) (nd/org-agenda-sort-prop 'project-status '(:stuck :waiting :active :held) a b)))
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
(org-agenda-sorting-strategy '(user-defined-down category-keep))))))
("P" ("P"
"Periodical View" "Periodical View"
@ -1379,47 +1558,75 @@ order (a list of properties as either symbols or strings)"
,(nd/agenda-base-task-cmd "-NA-REFILE+%inc/!" ,(nd/agenda-base-task-cmd "-NA-REFILE+%inc/!"
"Incubated Tasks" "Incubated Tasks"
''nd/skip-non-atomic-tasks) ''nd/skip-non-atomic-tasks)
,(nd/agenda-base-status-cmd (tags-todo
(concat (and nd/agenda-limit-project-toplevel "Toplevel ") "Incubated Projects")
"-NA-REFILE+%inc/!" "-NA-REFILE+%inc/!"
''nd/skip-non-projects ((org-agenda-overriding-header
'(:scheduled-project :invalid-todostate :undone-complete (concat (and
:done-incomplete :stuck :waiting nd/agenda-limit-project-toplevel "Toplevel ")
:held :active) "Incubated Projects"))
'nd/get-project-status))) (org-agenda-skip-function '(nd/skip-non-projects))
(org-agenda-before-sorting-filter-function
(lambda (l) (nd/org-agenda-filter-status '(:stuck :waiting :held :active) 'nd/get-project-status l)))
(org-agenda-cmp-user-defined
(lambda (a b) (nd/org-agenda-sort-prop 'project-status '(:stuck :waiting :active :held) a b)))
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
(org-agenda-sorting-strategy '(user-defined-down category-keep))))))
("r" ("r"
"Refile and Errors" "Refile and Critical Errors"
((tags "REFILE" ((tags "REFILE"
((org-agenda-overriding-header "Tasks to Refile")) ((org-agenda-overriding-header "Tasks to Refile"))
(org-tags-match-list-sublevels nil)) (org-tags-match-list-sublevels nil))
,(nd/agenda-base-task-cmd task-match ,(nd/agenda-base-task-cmd task-match
"Discontinous Project" "Discontinous Project"
''nd/skip-non-discontinuous-project-tasks) ''nd/skip-non-discontinuous-project-tasks)))
,(nd/agenda-base-header-cmd task-match
("e"
"Non-critical Errors"
(,(nd/agenda-base-header-cmd task-match
"Undone Closed" "Undone Closed"
''nd/skip-non-undone-closed-todoitems) ''nd/skip-non-undone-closed-todoitems)
,(nd/agenda-base-header-cmd (concat actionable "-" periodical) ,(nd/agenda-base-header-cmd (concat actionable "-" periodical)
"Done Unclosed" "Done Unclosed"
''nd/skip-non-done-unclosed-todoitems))) ''nd/skip-non-done-unclosed-todoitems)
(tags-todo
,act-no-rep-match
((org-agenda-overriding-header
(concat (and
nd/agenda-limit-project-toplevel "Toplevel ")
"Project Errors"))
(org-agenda-skip-function '(nd/skip-non-projects))
(org-agenda-before-sorting-filter-function
(lambda (l) (nd/org-agenda-filter-status '(:scheduled-project :invalid-todostate :undone-complete :done-incomplete) 'nd/get-project-status l)))
(org-agenda-cmp-user-defined
(lambda (a b) (nd/org-agenda-sort-prop 'project-status '(:scheduled-project :invalid-todostate :undone-complete :done-incomplete) a b)))
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
(org-agenda-sorting-strategy '(user-defined-down category-keep))))))
("A" ("A"
"Archivable Tasks and Projects" "Archivable Tasks and Projects"
(,(nd/agenda-base-header-cmd (concat actionable "-" periodical "-" iterator) (,(nd/agenda-base-header-cmd (concat actionable "-" periodical)
"Archivable Atomic Tasks" "Archivable Atomic Tasks"
''nd/skip-non-archivable-atomic-tasks) ''nd/skip-non-archivable-atomic-tasks)
,(nd/agenda-base-header-cmd (concat actionable "-" periodical) ,(nd/agenda-base-header-cmd (concat actionable "-" periodical)
"Stale Tasks" "Stale Tasks"
''nd/skip-non-stale-headings) ''nd/skip-non-stale-headings)
,(nd/agenda-base-header-cmd (concat actionable "-" periodical "+" iterator) ,(nd/agenda-base-proj-cmd (concat actionable "-" periodical "+" iterator)
"Archivable Iterators" "Archivable Iterators"
''nd/skip-non-archivable-atomic-tasks) :archivable)
,(nd/agenda-base-status-cmd (tags-todo
(concat (and nd/agenda-limit-project-toplevel "Toplevel ") "Archivable Projects")
,(concat actionable "-" periodical "-" iterator) ,(concat actionable "-" periodical "-" iterator)
''nd/skip-non-projects ((org-agenda-overriding-header
'(:archivable) (concat (and
'nd/get-project-status)))))) nd/agenda-limit-project-toplevel "Toplevel ")
"Archivable Projects"))
(org-agenda-skip-function '(nd/skip-non-projects))
(org-agenda-before-sorting-filter-function
(lambda (l) (nd/org-agenda-filter-status '(:archivable) 'nd/get-project-status l)))
(org-agenda-cmp-user-defined
(lambda (a b) (nd/org-agenda-sort-prop 'project-status '(:archivable) a b)))
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
(org-agenda-sorting-strategy '(user-defined-down category-keep)))))))))
#+END_SRC #+END_SRC
*** views *** views
**** calendar display **** calendar display