added orgmode conflict detection algorithm
This commit is contained in:
parent
d7fcf4e24d
commit
12ff47da89
102
conf.org
102
conf.org
|
@ -1122,6 +1122,108 @@ Clocking is still new and experimental (I'm not a ninja like Bernt yet). I mostl
|
||||||
org-clock-persist t
|
org-clock-persist t
|
||||||
org-clock-report-include-clocking-task t)
|
org-clock-report-include-clocking-task t)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
*** conflict detection
|
||||||
|
Somehow org-mode has no way to detect conflicts between tasks with timestamps (!!??). Luckily I can make my own.
|
||||||
|
|
||||||
|
Steps for this algorithm:
|
||||||
|
1. make a list of all entries with timestamps
|
||||||
|
2. sort timestamp list
|
||||||
|
3. Walk through list and compare entries immediately after (sorting ensures that entries can be skipped once one non-conflict is found). If conflicts are found make a new list of each conflict pair.
|
||||||
|
4. Display conflicts in buffer
|
||||||
|
|
||||||
|
This should be O(n) (best case/no conflicts) to O(n^2) (worst case/everything conflicts)
|
||||||
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
(defun nd/are-conflicting-p (ts-a ts-b)
|
||||||
|
"Return t if timestamps TS-A and TS-B conflict."
|
||||||
|
(let* ((earlier-a (car ts-a))
|
||||||
|
(earlier-b (car ts-b))
|
||||||
|
(later-b (+ earlier-b (nth 1 ts-b))))
|
||||||
|
(and (>= earlier-a earlier-b) (<= earlier-a later-b))))
|
||||||
|
|
||||||
|
(defun nd/detect-conflict (ts ts-list conlist)
|
||||||
|
"Recursively determine if timestamp TS conflicts with anything in TS-LIST.
|
||||||
|
If detected, conflict pair is added to CONLIST."
|
||||||
|
(let ((next-ts (car ts-list))
|
||||||
|
(rem-ts (cdr ts-list)))
|
||||||
|
(if (nd/are-conflicting-p ts next-ts)
|
||||||
|
(progn
|
||||||
|
(setq conlist (cons (list ts next-ts) conlist))
|
||||||
|
(if rem-ts (nd/detect-conflict ts rem-ts conlist) conlist))
|
||||||
|
conlist)))
|
||||||
|
|
||||||
|
(defun nd/build-conlist (ts-list conlist)
|
||||||
|
"Recursively build a list of timestamp conflicts from TS-LIST.
|
||||||
|
|
||||||
|
TS-LIST is comprised of entries in the form (staring-ts timerange marker)
|
||||||
|
where timerange is 0 for singular timestamps and a positive number for
|
||||||
|
anything with to times or a timestamp range.
|
||||||
|
Detected conflicts are stored in CONLIST as pairs of conflicting ts
|
||||||
|
entries from the TS-LIST."
|
||||||
|
(let ((cur-ts (car ts-list))
|
||||||
|
(rem-ts (cdr ts-list)))
|
||||||
|
(if rem-ts
|
||||||
|
(nd/build-conlist rem-ts (nd/detect-conflict cur-ts rem-ts conlist))
|
||||||
|
conlist)))
|
||||||
|
|
||||||
|
(defconst nd/org-tsm-regexp
|
||||||
|
"\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [^]+0-9>\r\n -]+? \\)\\([0-9]\\{1,2\\}:[0-9]\\{2\\}?\\)-\\([0-9]\\{1,2\\}:[0-9]\\{2\\}\\)"
|
||||||
|
"Regular expression for timestamps with two times.")
|
||||||
|
|
||||||
|
(defun nd/get-timestamps ()
|
||||||
|
"Get the org-marker and timestamp(s) (multiple if range) or current heading."
|
||||||
|
;; TODO, what if I care about more than just TIMESTAMPs
|
||||||
|
(let* ((ts (org-entry-get nil "TIMESTAMP"))
|
||||||
|
(marker (point-marker))
|
||||||
|
(ts-range 0)
|
||||||
|
(ts-entry))
|
||||||
|
(when ts
|
||||||
|
(cond
|
||||||
|
;; match timestamps that have two times
|
||||||
|
((string-match nd/org-tsm-regexp ts)
|
||||||
|
(let* ((ts1 (concat (match-string 1 ts) (match-string 2 ts)))
|
||||||
|
(ts2 (concat (match-string 1 ts) (match-string 3 ts)))
|
||||||
|
(ft1 (org-2ft ts1))
|
||||||
|
(ft2 (org-2ft ts2)))
|
||||||
|
(setq ts-entry ft1)
|
||||||
|
(setq ts-range (- ft2 ft1))))
|
||||||
|
|
||||||
|
;; match timestamps that have a range (eq two timestamps)
|
||||||
|
((string-match org-tr-regexp ts)
|
||||||
|
(let* ((ts1 (match-string 1 ts))
|
||||||
|
(ts2 (match-string 2 ts))
|
||||||
|
(ft1 (org-2ft ts1))
|
||||||
|
(ft2 (org-2ft ts2)))
|
||||||
|
(setq ts-entry ft1)
|
||||||
|
(setq ts-range (- ft2 ft1))))
|
||||||
|
|
||||||
|
;; match timestamps with only one time
|
||||||
|
(t (setq ts-entry (org-2ft ts))))
|
||||||
|
(list ts-entry ts-range marker))))
|
||||||
|
|
||||||
|
(defun nd/build-conflict-list ()
|
||||||
|
"Scan all org files and make a list of all timestamps that conflict."
|
||||||
|
(let ((files '("~/Org/reference/testconflict.org"))
|
||||||
|
prev-point ts-list cur-index conflicts)
|
||||||
|
;; get all timestamps from org buffers
|
||||||
|
(dolist (f files ts-list)
|
||||||
|
(with-current-buffer
|
||||||
|
(find-file-noselect f)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(if (not (outline-on-heading-p)) (outline-next-heading))
|
||||||
|
(setq prev-point -1)
|
||||||
|
(while (> (point) prev-point)
|
||||||
|
(let ((new-ts (nd/get-timestamps)))
|
||||||
|
(if new-ts (setq ts-list (cons new-ts ts-list))))
|
||||||
|
(setq prev-point (point))
|
||||||
|
(outline-next-heading))))
|
||||||
|
|
||||||
|
;; sort the timestamp list
|
||||||
|
;; TODO, need to make range-aware
|
||||||
|
(setq ts-list (sort ts-list (lambda (a b) (< (car a) (car b)))))
|
||||||
|
|
||||||
|
;; build a list of conflicts
|
||||||
|
(nd/build-conlist ts-list conflicts)))
|
||||||
|
#+END_SRC
|
||||||
*** agenda
|
*** agenda
|
||||||
**** targets
|
**** targets
|
||||||
The agenda files are limited to as few as possible to keep scanning and startup reasonably fast.
|
The agenda files are limited to as few as possible to keep scanning and startup reasonably fast.
|
||||||
|
|
Loading…
Reference in New Issue