cmc/dotfiles

Using GNU Stow to manage my dotfiles. config dotfiles macos stow

Commit 76e87ab090

76e87ab090cecb92579645ce3a5bb0247ef548b1

parent: 00eb327a84

Verified · cmc

cmc <hello@cleberg.net> · 2026-02-21 19:15 UTC

move time reporting to its own internal function

Layout: unified · split

common/doom/.config/doom/config.el +85
@@ -47,3 +47,88 @@
4747 ("xnow" "" (lambda () (insert (format-time-string "%H:%M:%S"))))
4848 ))
4949
50;; Time Report
51(defun tr/parse-org-clock-entries (file)
52 "Return list of (date heading minutes) from FILE's CLOCK entries."
53 (let (entries)
54 (with-current-buffer (find-file-noselect file)
55 (org-with-wide-buffer
56 (goto-char (point-min))
57 (while (re-search-forward
58 "CLOCK: \\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\) [A-Za-z]\\{3\\} [0-9:]\\{5\\}\\]--\\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\) [A-Za-z]\\{3\\} \\([0-9:]\\{5\\}\\)\\] => *\\([0-9]+\\):\\([0-9]+\\)"
59 nil t)
60 (let* ((date (match-string 1))
61 (h (string-to-number (match-string 4)))
62 (m (string-to-number (match-string 5)))
63 (mins (+ (* h 60) m))
64 (heading (save-excursion
65 (org-back-to-heading t)
66 (org-get-heading t t t t))))
67 (push (list date heading mins) entries)))))
68 (nreverse entries)))
69
70(defun tr/format-minutes (mins)
71 (format "%d:%02d" (/ mins 60) (mod mins 60)))
72
73(defun tr/generate-report (input-file output-file &optional tstart tend)
74 "Parse INPUT-FILE and write time report to OUTPUT-FILE."
75 (let* ((entries (tr/parse-org-clock-entries input-file))
76 (entries (if (or tstart tend)
77 (cl-remove-if-not
78 (lambda (e)
79 (let ((d (car e)))
80 (and (or (null tstart) (not (string< d tstart)))
81 (or (null tend) (not (string> d tend))))))
82 entries)
83 entries))
84 (totals (make-hash-table :test #'equal)))
85 (dolist (e entries)
86 (let ((key (cons (nth 0 e) (nth 1 e))))
87 (puthash key (+ (gethash key totals 0) (nth 2 e)) totals)))
88 (let* ((keys (hash-table-keys totals))
89 (keys (sort keys (lambda (a b)
90 (or (string< (car a) (car b))
91 (and (string= (car a) (car b))
92 (string< (cdr a) (cdr b)))))))
93 (current-week nil))
94 (with-temp-file output-file
95 (insert "#+title: Time Report\n\n")
96 (insert (format "| %-12s | %-45s | %5s |\n" "Date" "Code" "Hours"))
97 (insert (concat "|" (make-string 14 ?-) "+" (make-string 47 ?-) "+" (make-string 7 ?-) "|\n"))
98 (let (week-mins week-start)
99 (dolist (k keys)
100 (let* ((date (car k))
101 (heading (cdr k))
102 (mins (gethash k totals))
103 (time (date-to-time (concat date " 00:00:00")))
104 (week (format-time-string "%Y-W%V" time)))
105 (when (and week-start (not (string= week current-week)))
106 (insert (concat "|" (make-string 14 ?-) "+" (make-string 47 ?-) "+" (make-string 7 ?-) "|\n"))
107 (insert (format "| %-12s | %-45s | %5s |\n" current-week "WEEK TOTAL" (tr/format-minutes week-mins)))
108 (insert (concat "|" (make-string 14 ?-) "+" (make-string 47 ?-) "+" (make-string 7 ?-) "|\n"))
109 (setq week-mins 0))
110 (unless (string= week current-week)
111 (setq current-week week week-start date week-mins 0))
112 (setq week-mins (+ week-mins mins))
113 (insert (format "| %-12s | %-45s | %5s |\n" date heading (tr/format-minutes mins)))))
114 (when week-mins
115 (insert (concat "|" (make-string 14 ?-) "+" (make-string 47 ?-) "+" (make-string 7 ?-) "|\n"))
116 (insert (format "| %-12s | %-45s | %5s |\n" current-week "WEEK TOTAL" (tr/format-minutes week-mins)))))
117 (insert "\n")))))
118
119(defun tr/time-report-interactive ()
120 "Interactively generate a time report from an org file."
121 (interactive)
122 (let* ((input (read-file-name "Time tracking file: " nil nil t))
123 (output (read-file-name "Output report file: "))
124 (tstart (read-string "Start date (YYYY-MM-DD, blank for none): "))
125 (tend (read-string "End date (YYYY-MM-DD, blank for none): "))
126 (tstart (if (string-empty-p tstart) nil tstart))
127 (tend (if (string-empty-p tend) nil tend)))
128 (tr/generate-report input output tstart tend)
129 (find-file output)
130 (message "Report written to %s" output)))
131
132;; Map time report to keybindings
133(map! :leader
134 :desc "Generate time report" "z t" #'tr/time-report-interactive)