mirror of
https://github.com/SqrtMinusOne/org-clock-agg.git
synced 2025-12-10 14:03:02 +03:00
Compare commits
2 commits
27c54bfb78
...
6a40c31c78
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a40c31c78 | |||
| e8cfd456bc |
1 changed files with 75 additions and 25 deletions
|
|
@ -216,12 +216,13 @@ Return a list of alists with the following keys:
|
|||
- `:start' - start time in seconds since the epoch
|
||||
- `:end' - end time in seconds since the epoch
|
||||
- `:duration' - duration in seconds."
|
||||
(let ((contents (buffer-substring-no-properties
|
||||
;; contents-begin starts after the headline
|
||||
(save-restriction
|
||||
;; I used to insert a substring into a separate buffer to run
|
||||
;; `org-element-parse-buffer', but somehow this broke on the most
|
||||
;; recent `org-mode'.
|
||||
(narrow-to-region
|
||||
(org-element-property :contents-begin headline)
|
||||
(org-element-property :contents-end headline))))
|
||||
(with-temp-buffer
|
||||
(insert contents)
|
||||
(org-element-property :contents-end headline))
|
||||
(let (res)
|
||||
(org-element-map (org-element-parse-buffer) 'clock
|
||||
(lambda (clock)
|
||||
|
|
@ -239,7 +240,7 @@ Return a list of alists with the following keys:
|
|||
;; The last argument stops parsing after the first headline.
|
||||
;; So only clocks in the first headline are parsed.
|
||||
nil nil 'headline)
|
||||
res))))
|
||||
res)))
|
||||
|
||||
(defun org-clock-agg--properties-at-point ()
|
||||
"Return a list of selected properties at point.
|
||||
|
|
@ -304,6 +305,7 @@ list of properties to select
|
|||
(:properties . ,properties)
|
||||
(:category . ,category)))))
|
||||
|
||||
;; TODO use `org-read-date'
|
||||
(defun org-clock-agg--normalize-time-predicate (val kind)
|
||||
"Normalize VAL to a time predicate.
|
||||
|
||||
|
|
@ -553,7 +555,9 @@ BODY can also contain the following keyword arguments:
|
|||
:readable-name "TODO keyword"
|
||||
:default-sort total
|
||||
(list (substring-no-properties
|
||||
(org-element-property :todo-keyword (alist-get :headline elem)))))
|
||||
(or
|
||||
(org-element-property :todo-keyword (alist-get :headline elem))
|
||||
"No TODO"))))
|
||||
|
||||
(org-clock-agg-defgroupby is-done
|
||||
:readable-name "Is done"
|
||||
|
|
@ -1356,6 +1360,52 @@ attributes."
|
|||
(with-temp-file file-name
|
||||
(insert csv-string))))
|
||||
|
||||
(defun org-clock-agg--flatten-tree (tree &optional attrs)
|
||||
"Flatten an `org-clock-agg' TREE.
|
||||
|
||||
TREE is as defined by `org-clock-agg--groupby'. ATTRS is a recursive
|
||||
parameter."
|
||||
|
||||
(let (res)
|
||||
(cl-loop for (name . node) in tree
|
||||
for groupby-name = (intern
|
||||
(string-replace
|
||||
" " "-"
|
||||
(downcase
|
||||
(alist-get :readable-name
|
||||
(alist-get :groupby node)))))
|
||||
if (seq-empty-p (alist-get :children node))
|
||||
do (push
|
||||
`((name . ,name)
|
||||
(total . ,(alist-get :total node))
|
||||
(parent-share . ,(alist-get :parent-share node))
|
||||
(total-share . ,(alist-get :total-share node))
|
||||
(elems-count . ,(seq-length (alist-get :elems node)))
|
||||
(,groupby-name . ,name)
|
||||
,@attrs)
|
||||
res)
|
||||
else
|
||||
do (setq res
|
||||
(append
|
||||
(org-clock-agg--flatten-tree
|
||||
(alist-get :children node)
|
||||
`(,@attrs
|
||||
(,groupby-name . ,name)))
|
||||
res nil)))
|
||||
res))
|
||||
|
||||
(defun org-clock-agg-tree-csv ()
|
||||
"Export the current `org-clock-agg' tree into CSV."
|
||||
(interactive)
|
||||
(unless org-clock-agg--tree
|
||||
(user-error "Tree not found"))
|
||||
(let* ((data (org-clock-agg--flatten-tree
|
||||
org-clock-agg--tree))
|
||||
(csv-string (org-clock-agg--csv-alist-to-string data))
|
||||
(file-name (read-file-name "Save CSV: " nil "report.csv")))
|
||||
(with-temp-file file-name
|
||||
(insert csv-string))))
|
||||
|
||||
(defun org-clock-agg (from to files groupby sort sort-order extra-params)
|
||||
"Aggregate org-clock data.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue