From 016fbf3c100eca69a48eb9e9d1e9bbd0afa0d67d Mon Sep 17 00:00:00 2001 From: Daniel Borchmann Date: Sat, 16 Feb 2019 11:21:55 +0100 Subject: [PATCH] [Org] Extend functionality to insert past interruptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For this a new function ‘timeline-tools-clockline-no-org-agenda-conflicts’ as been added that reads in a clock line (start and end times), updates all org agenda files to fix conflicting clock lines, and returns the string of the new clock line. This works for all complete clock lines (i.e., those with end times), but not for open clock lines (i.e., those missing an end timestamp). --- init.el | 6 +- site-lisp/db-org.el | 13 ---- site-lisp/timeline-tools.el | 115 +++++++++++++++++++++++------------- 3 files changed, 77 insertions(+), 57 deletions(-) diff --git a/init.el b/init.el index ad0d086..b409734 100644 --- a/init.el +++ b/init.el @@ -608,7 +608,6 @@ hydra-org-agenda-view/body org-babel-execute:hy db/org-timestamp-difference - db/read-clockline db/org-capture-code-snippet hydra-org-clock/body db/make-org-capture-frame)) @@ -1020,7 +1019,7 @@ (file db/org-default-refile-file) ,(concat "* DONE %^{What}\n" ":LOGBOOK:\n" - "%(db/read-clockline)\n" ; evaluated before above prompt? + "%(timeline-tools-clockline-no-org-agenda-conflicts)\n" ; evaluated before above prompt? ":END:\n" "%?")) ("j" "journal entry" @@ -2579,7 +2578,8 @@ :load-path "site-lisp" :commands (timeline-tools-format-timeline timeline-tools-format-timeline-of-day - timeline-tools-copy-clocklines)) + timeline-tools-copy-clocklines + timeline-tools-clockline-no-org-agenda-conflicts)) (use-package typing :commands (typing-of-emacs) diff --git a/site-lisp/db-org.el b/site-lisp/db-org.el index b426d1f..c5a8847 100644 --- a/site-lisp/db-org.el +++ b/site-lisp/db-org.el @@ -194,19 +194,6 @@ _y_: ?y? year _q_: quit _L__l__c_: ?l? (m (floor (/ (- s (* 3600 h)) 60)))) (format (if neg "-%d:%02d" "%2d:%02d") h m))) -(defun db/read-clockline () - "Read starting and ending time from user and return org mode - clock line." - (let* ((now (format-time-string "%H:%M")) - (starting (format "[%s]" (org-read-date t nil nil "Started: " - (current-time) - now))) - (ending (format "[%s]" (org-read-date t nil nil "Ended: " - (current-time) - now))) - (difference (db/org-timestamp-difference starting ending))) - (format "CLOCK: %s--%s => %s" starting ending difference))) - ;; Capture Code Snippets ;; from http://ul.io/nb/2018/04/30/better-code-snippets-with-org-capture/ (defun db/org-capture-code-snippet (filename) diff --git a/site-lisp/timeline-tools.el b/site-lisp/timeline-tools.el index 5320424..a533e37 100644 --- a/site-lisp/timeline-tools.el +++ b/site-lisp/timeline-tools.el @@ -595,56 +595,89 @@ Interactively query for the exact value of \"short\"." ;; XXX: All this needs some autoloadable frontend -(defun timeline-tools-add-clockline-to-marker (target-marker start end) - "Add clock line to task under TARGET-MARKER from START to END. +(defun timeline-tools-clockline-no-conflict (start end &rest buffers) + "Return clock line string from START to END. + +START and END must be suitable arguments for `float-time’. +Update conflicting clock lines in BUFFERS before returning the +clock line." + (let ((new-start (float-time start)) + (new-end (float-time end))) + (dolist (buffer buffers) + (with-current-buffer buffer + (timeline-tools-map-clocklines + (lambda (timestamp-1 timestamp-2) + (let ((current-start (org-time-string-to-seconds timestamp-1)) + (current-end (org-time-string-to-seconds timestamp-2)) + (kill-whole-line nil) ; don’t delete newlines if not asked to + ) + (cond + ;; if the current clock line is completely contained within the + ;; given period, delete it + ((and (<= new-start current-start current-end new-end)) + (kill-whole-line)) + ;; if the current clock line completely contains the given one, + ;; split it + ((and (<= current-start new-start new-end current-end)) + (beginning-of-line) + (kill-line) + (timeline-tools-insert-clockline current-start new-start) + (open-line 1) + (timeline-tools-insert-clockline new-end current-end)) + ;; New interval overlaps beginning of current line + ((<= new-start current-start new-end current-end) + (beginning-of-line) + (kill-line) + (timeline-tools-insert-clockline new-end current-end)) + ;; New interval overlaps at end of current line + ((<= current-start new-start current-end new-end) + (beginning-of-line) + (kill-line) + (timeline-tools-insert-clockline current-start new-start))))) + + ;; Keep headline as they are, i.e., do nothing + #'ignore))) + + ;; Return valid clockline + (with-temp-buffer + (timeline-tools-insert-clockline new-start new-end) + (buffer-string)))) + +(defun timeline-tools-add-clockline-to-marker + (target-marker start end &rest buffers) + "Add clock line from START to END to task under TARGET-MARKER. START and END must be given as time objects as returned by `encode-time’, or as an integer or float denoting seconds since 1970-01-01. TARGET-MARKER must be positioned on the task where -the clock line is to be added to." +the clock line is to be added to. BUFFERS must be a list of +buffers where to look for conflicting clock lines. Those +conflicting clock lines are updated accordingly. If BUFFERS is +not given, update clock lines in the buffer of TARGET-MARKER." (when (not (markerp target-marker)) (user-error "Marker not valid")) - (let ((new-start (float-time start)) - (new-end (float-time end))) - (with-current-buffer (marker-buffer target-marker) - (timeline-tools-map-clocklines - (lambda (timestamp-1 timestamp-2) - (let ((current-start (org-time-string-to-seconds timestamp-1)) - (current-end (org-time-string-to-seconds timestamp-2)) - (kill-whole-line nil) ; don’t delete newlines if not asked to - ) - (cond - ;; if the current clock line is completely contained within the - ;; given period, delete it - ((and (<= new-start current-start current-end new-end)) - (kill-whole-line)) - ;; if the current clock line completely contains the given one, - ;; split it - ((and (<= current-start new-start new-end current-end)) - (beginning-of-line) - (kill-line) - (timeline-tools-insert-clockline current-start new-start) - (open-line 1) - (timeline-tools-insert-clockline new-end current-end)) - ;; New interval overlaps beginning of current line - ((<= new-start current-start new-end current-end) - (beginning-of-line) - (kill-line) - (timeline-tools-insert-clockline new-end current-end)) - ;; New interval overlaps at end of current line - ((<= current-start new-start current-end new-end) - (beginning-of-line) - (kill-line) - (timeline-tools-insert-clockline current-start new-start))))) - - ;; Keep headline as they are, i.e., do nothing - #'ignore)) - - ;; Finally add the new clock line + (let ((new-start (float-time start)) + (new-end (float-time end)) + (org-buffers (if buffers buffers (list (marker-buffer target-marker))))) (org-with-point-at target-marker (org-clock-find-position nil) (open-line 1) - (timeline-tools-insert-clockline new-start new-end)))) + (insert (apply #'timeline-tools-clockline-no-conflicts + new-start new-end org-buffers))))) + +;;;###autoload +(defun timeline-tools-clockline-no-org-agenda-conflicts () + "Read clock line from user and return it. + +Update all files in `org-agenda-files’ to update conflicting clock lines." + (let* ((now (format-time-string "%H:%M")) + (start (org-read-date t nil nil "Started: " (current-time) now)) + (end (org-read-date t nil nil "Ended: " (current-time) now))) + (apply #'timeline-tools-clockline-no-conflict + (org-time-string-to-seconds start) + (org-time-string-to-seconds end) + (mapcar #'find-file-noselect + (cl-remove-if-not #'file-exists-p org-agenda-files))))) (defun timeline-tools-copy-clocklines (source-id target-id) "Copy clock lines from SOURCE-ID to TARGET-ID.