Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle tag inheritance #29

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions org-clock-csv-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,34 @@
"Test file level category."
(org-clock-csv-should-match "tests/issue-5.org" "tests/issue-5.csv"))

(ert-deftest test-issue-24 ()
"Test nested tags."
(org-clock-csv-should-match "tests/issue-24.org" "tests/issue-24.csv"))

(ert-deftest test-issue-24-nil ()
"Test local tags only."
(setq org-use-tag-inheritance nil)
(org-clock-csv-should-match "tests/issue-24.org" "tests/issue-24-nil.csv")
(setq org-use-tag-inheritance t))

(ert-deftest test-issue-24-list ()
"Test tags in list."
(setq org-use-tag-inheritance (list "ex4" "ex1" "tag1"))
(org-clock-csv-should-match "tests/issue-24.org" "tests/issue-24-list.csv")
(setq org-use-tag-inheritance t))

(ert-deftest test-issue-24-regexp ()
"Test tags in regexp."
(setq org-use-tag-inheritance "e.[13]")
(org-clock-csv-should-match "tests/issue-24.org" "tests/issue-24-regexp.csv")
(setq org-use-tag-inheritance t))

(ert-deftest test-issue-24-exclude ()
"Test tags exclusion."
(setq org-tags-exclude-from-inheritance (list "tag2" "ex1"))
(org-clock-csv-should-match "tests/issue-24.org" "tests/issue-24-ex.csv")
(setq org-tags-exclude-from-inheritance nil))

(ert-deftest test-issue-26 ()
"Test file without title."
(let ((org-clock-csv-header org-clock-csv-header-all-props)
Expand Down
43 changes: 35 additions & 8 deletions org-clock-csv.el
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,33 @@ Returns the DEFAULT file level category if none is found."
(if ph
(cons ph (org-clock-csv--find-headlines ph)))))

(defun org-clock-csv--filter-inherited-tags (tags)
"Filter a list of TAGS according to
`org-tags-exclude-from-inheritance' and `org-use-tag-inheritance'."
(let ((included-tags
(cond ((not org-use-tag-inheritance) nil)
((eq org-use-tag-inheritance t) tags)
((listp org-use-tag-inheritance)
(mapcar (lambda (tag) (if (member tag org-use-tag-inheritance) tag)) tags))
((stringp org-use-tag-inheritance)
(mapcar (lambda (tag) (if (string-match org-use-tag-inheritance tag) tag)) tags)))))
(if (not org-tags-exclude-from-inheritance)
included-tags
(cl-set-difference included-tags org-tags-exclude-from-inheritance :test #'equal))))

(defun org-clock-csv--find-tags (headlines default)
"Search tags in HEADLINES respecting
`org-tags-exclude-from-inheritance' and `org-use-tag-inheritance'.
DEFAULT tags are also checked against those variables.

The tags of the first headline are always added."
(remove nil (append (org-clock-csv--filter-inherited-tags
(append (split-string default ":" t)
(apply #'append ; flatten
(mapcar (lambda (headline) (org-element-property :tags headline))
(reverse (cdr headlines))))))
(org-element-property :tags (car headlines)))))

(defun org-clock-csv--get-properties-plist (element)
"Returns a plist of the [inherited] properties drawer of an org element"
;; org-entry-properties returns an ALIST, but we don't want to have to handle
Expand All @@ -172,7 +199,7 @@ Returns the DEFAULT file level category if none is found."
(lambda (acc key) (plist-put acc key (org-entry-get el key t)))
(org-buffer-property-keys) nil))))

(defun org-clock-csv--parse-element (element title default-category)
(defun org-clock-csv--parse-element (element title default-category default-tags)
"Ingest clock ELEMENT and produces a plist of its relevant
properties."
(when (and (equal (org-element-type element) 'clock)
Expand All @@ -188,10 +215,8 @@ properties."
(task (car headlines-values))
(parents (reverse (cdr headlines-values)))
(effort (org-element-property :EFFORT task-headline))
;; TODO: Handle tag inheritance, respecting the value of
;; `org-tags-exclude-from-inheritance'.
(tags (mapconcat #'identity
(org-element-property :tags task-headline) ":"))
(org-clock-csv--find-tags headlines default-tags) ":"))
(ishabit (when (equal "habit" (org-element-property
:STYLE task-headline))
"t"))
Expand Down Expand Up @@ -236,8 +261,9 @@ properties."
or the DEFAULT value if it does not exist."
(let ((value (org-element-map ast 'keyword
(lambda (elem) (if (string-equal (org-element-property :key elem) property)
(org-element-property :value elem))))))
(if (equal nil value) default (car value))))
(org-element-property :value elem)))
nil t)))
(if (equal nil value) default value)))

(defun org-clock-csv--get-entries (filelist &optional no-check)
"Retrieves clock entries from files in FILELIST.
Expand All @@ -251,9 +277,10 @@ When NO-CHECK is non-nil, skip checking if all files exist."
(with-current-buffer (find-file-noselect file)
(let* ((ast (org-element-parse-buffer))
(title (org-clock-csv--get-org-data 'TITLE ast file))
(category (org-clock-csv--get-org-data 'CATEGORY ast "")))
(category (org-clock-csv--get-org-data 'CATEGORY ast ""))
(tags (org-clock-csv--get-org-data 'FILETAGS ast "")))
(org-element-map ast 'clock
(lambda (c) (org-clock-csv--parse-element c title category))
(lambda (c) (org-clock-csv--parse-element c title category tags))
nil nil)))))

;;;; Public API:
Expand Down
8 changes: 8 additions & 0 deletions tests/issue-24-ex.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
task,parents,category,start,end,effort,ishabit,tags
Task A,,,2017-01-01 06:00,2017-01-01 07:00,,,tag1:ex1:ex2
Task AB,Task A,,2017-01-01 07:00,2017-01-01 08:00,,,tag1:ex2:ex3
Task ABC,Task A/Task AB,,2017-01-01 08:00,2017-01-01 09:00,,,tag1:ex2:ex3:ex4
Task ABCD,Task A/Task AB/Task ABC,,2017-01-01 09:00,2017-01-01 10:00,,,tag1:ex2:ex3:ex4:ex5
Task BA,Task B,,2017-01-01 10:00,2017-01-01 11:00,,,tag1:ex2:ex1
Task BB,Task B,,2017-01-01 11:00,2017-01-01 12:00,,,tag1:ex2
Task BBC,Task B/Task BB,,2017-01-01 12:00,2017-01-01 13:00,,,tag1:ex2:ex4
8 changes: 8 additions & 0 deletions tests/issue-24-list.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
task,parents,category,start,end,effort,ishabit,tags
Task A,,,2017-01-01 06:00,2017-01-01 07:00,,,tag1:ex1:ex2
Task AB,Task A,,2017-01-01 07:00,2017-01-01 08:00,,,tag1:ex1:ex3
Task ABC,Task A/Task AB,,2017-01-01 08:00,2017-01-01 09:00,,,tag1:ex1:ex4
Task ABCD,Task A/Task AB/Task ABC,,2017-01-01 09:00,2017-01-01 10:00,,,tag1:ex1:ex4:ex5
Task BA,Task B,,2017-01-01 10:00,2017-01-01 11:00,,,tag1:ex1
Task BB,Task B,,2017-01-01 11:00,2017-01-01 12:00,,,tag1
Task BBC,Task B/Task BB,,2017-01-01 12:00,2017-01-01 13:00,,,tag1:ex4
8 changes: 8 additions & 0 deletions tests/issue-24-nil.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
task,parents,category,start,end,effort,ishabit,tags
Task A,,,2017-01-01 06:00,2017-01-01 07:00,,,ex1:ex2
Task AB,Task A,,2017-01-01 07:00,2017-01-01 08:00,,,ex3
Task ABC,Task A/Task AB,,2017-01-01 08:00,2017-01-01 09:00,,,ex4
Task ABCD,Task A/Task AB/Task ABC,,2017-01-01 09:00,2017-01-01 10:00,,,ex5
Task BA,Task B,,2017-01-01 10:00,2017-01-01 11:00,,,ex1
Task BB,Task B,,2017-01-01 11:00,2017-01-01 12:00,,,
Task BBC,Task B/Task BB,,2017-01-01 12:00,2017-01-01 13:00,,,ex4
8 changes: 8 additions & 0 deletions tests/issue-24-regexp.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
task,parents,category,start,end,effort,ishabit,tags
Task A,,,2017-01-01 06:00,2017-01-01 07:00,,,ex1:ex2
Task AB,Task A,,2017-01-01 07:00,2017-01-01 08:00,,,ex1:ex3
Task ABC,Task A/Task AB,,2017-01-01 08:00,2017-01-01 09:00,,,ex1:ex3:ex4
Task ABCD,Task A/Task AB/Task ABC,,2017-01-01 09:00,2017-01-01 10:00,,,ex1:ex3:ex5
Task BA,Task B,,2017-01-01 10:00,2017-01-01 11:00,,,ex1
Task BB,Task B,,2017-01-01 11:00,2017-01-01 12:00,,,
Task BBC,Task B/Task BB,,2017-01-01 12:00,2017-01-01 13:00,,,ex4
8 changes: 8 additions & 0 deletions tests/issue-24.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
task,parents,category,start,end,effort,ishabit,tags
Task A,,,2017-01-01 06:00,2017-01-01 07:00,,,tag1:tag2:ex1:ex2
Task AB,Task A,,2017-01-01 07:00,2017-01-01 08:00,,,tag1:tag2:ex1:ex2:ex3
Task ABC,Task A/Task AB,,2017-01-01 08:00,2017-01-01 09:00,,,tag1:tag2:ex1:ex2:ex3:ex4
Task ABCD,Task A/Task AB/Task ABC,,2017-01-01 09:00,2017-01-01 10:00,,,tag1:tag2:ex1:ex2:ex3:ex4:ex5
Task BA,Task B,,2017-01-01 10:00,2017-01-01 11:00,,,tag1:tag2:ex2:ex1
Task BB,Task B,,2017-01-01 11:00,2017-01-01 12:00,,,tag1:tag2:ex2
Task BBC,Task B/Task BB,,2017-01-01 12:00,2017-01-01 13:00,,,tag1:tag2:ex2:ex4
32 changes: 32 additions & 0 deletions tests/issue-24.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#+STARTUP: content
#+FILETAGS: :tag1:tag2:

* Task A :ex1:ex2:
:LOGBOOK:
CLOCK: [2017-01-01 Sun 06:00]--[2017-01-01 Sun 07:00] => 1:00
:END:
** Task AB :ex3:
:LOGBOOK:
CLOCK: [2017-01-01 Sun 07:00]--[2017-01-01 Sun 08:00] => 1:00
:END:
*** Task ABC :ex4:
:LOGBOOK:
CLOCK: [2017-01-01 Sun 08:00]--[2017-01-01 Sun 09:00] => 1:00
:END:
**** Task ABCD :ex5:
:LOGBOOK:
CLOCK: [2017-01-01 Sun 09:00]--[2017-01-01 Sun 10:00] => 1:00
:END:
* Task B :ex2:
** Task BA :ex1:
:LOGBOOK:
CLOCK: [2017-01-01 Sun 10:00]--[2017-01-01 Sun 11:00] => 1:00
:END:
** Task BB
:LOGBOOK:
CLOCK: [2017-01-01 Sun 11:00]--[2017-01-01 Sun 12:00] => 1:00
:END:
*** Task BBC :ex4:
:LOGBOOK:
CLOCK: [2017-01-01 Sun 12:00]--[2017-01-01 Sun 13:00] => 1:00
:END: