Iterate over logbook and send all clock times to an external command

What code do you want reviewed?

Hi all! This is my first post, so I hope this is the correct category. I am interested in some feedback for a piece of code that works but I find really clunky. :grin:

In a nutshell: I want to iterate through a logbook drawer and send all time stamps to an external command (which I need to track company times) that I called my-other-cli in this example.

Here is what I got so far. I replaced the (shell-command) with a (princ) so it can be evaluated in the source block:

* Worklog
** 2022-11
CLOCK: [2022-11-10 Thu 11:18]--[2022-11-10 Thu 19:55] =>  8:37
CLOCK: [2022-11-10 Thu 08:18]--[2022-11-10 Thu 10:06] =>  1:48
CLOCK: [2022-11-09 Wed 13:10]--[2022-11-09 Wed 17:55] =>  4:45
CLOCK: [2022-11-09 Wed 08:48]--[2022-11-09 Wed 12:25] =>  3:37

#+name: loop-through-clocks
#+begin_src elisp :results output replace
(defun bascht/call-command-with-timestamp (clock-line)
  (let* ((timestamp (org-element-property :value clock-line)))
    (let* ((year-start (org-element-property :year-start timestamp))
           (year-end (org-element-property :year-end timestamp))
           (month-start (org-element-property :month-start timestamp))
           (month-end (org-element-property :month-end timestamp))
           (day-start (org-element-property :day-start timestamp))
           (day-end (org-element-property :day-end timestamp))
           (hour-start (org-element-property :hour-start timestamp))
           (hour-end (org-element-property :hour-end timestamp))
           (minute-start (org-element-property :minute-start timestamp))
           (minute-end (org-element-property :minute-end timestamp)))

      (print (format "my-other-cli --work-start '%s-%s-%s %s:%s' --work-end '%s-%s-%s %s:%s'"
                       year-start month-start day-start hour-start minute-start
                       year-end month-end day-end hour-end minute-end)))))

  (goto-char (org-find-exact-headline-in-buffer "Worklog"))
  (goto-char (org-find-exact-headline-in-buffer (format-time-string "%Y-%m")))
  (search-forward ":LOGBOOK:")
  (forward-line 1)

   until (s-equals? (s-trim (thing-at-point 'line t)) ":END:")
   do (bascht/call-command-with-timestamp (org-element-at-point))
   (forward-line 1)))


#+RESULTS: loop-through-clocks
: "my-other-cli --work-start '2022-11-10 11:18' --work-end '2022-11-10 19:55'"
: "my-other-cli --work-start '2022-11-10 8:18' --work-end '2022-11-10 10:6'"
: "my-other-cli --work-start '2022-11-9 13:10' --work-end '2022-11-9 17:55'"
: "my-other-cli --work-start '2022-11-9 8:48' --work-end '2022-11-9 12:25'"

What are you trying to do?

As I already mentioned: The code works, but I am not very confident.

The timestamps

I found (org-element-property :value […]) which gives me the org timestamp including both start and end time. But only as individual properties (year-start, month-start, etc.) so I had to re-map all of them. :frowning:

Is there an easier way to access both time stamps for start and end? Feeding it directly into (format-time-string "%Y-%m" timestamp) just gives me “Invalid time specification”.

Finding the drawer.

At the moment I use (search-forward) to find the :LOGBOOK: drawer. Is there an easier way to jump straight into the drawer? (That way I could also get rid of the (forward-line)).

Any other feedback what you would do different?

Thanks already for reading up until the end! :slight_smile:

(defun bascht/call-command-with-timestamp (clock-line)
  (let* ((timestamp (org-element-property :value clock-line))
         (ts-start (org-timestamp-to-time timestamp))
         (ts-end (org-timestamp-to-time timestamp t)))
    (print (format "my-other-cli --work-start '%s' --work-end '%s'"
                   (format-time-string "%F %R" ts-start)
                   (format-time-string "%F %R" ts-end)))))

Awesome, I did not find (ts-start) as it’s not interactive. I guess I have to work on my search engine game. Thank you very much! :slight_smile: