summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarold Ollivier <holl@mailbox.org>2024-05-15 18:49:25 +0200
committerBastien Guerry <bzg@gnu.org>2024-05-15 18:49:25 +0200
commit8e32471fc8d7b4edc07e416488e55f0f33c86f23 (patch)
treec2108d5657fe69abadde1be1f5f866ae72156f72
parent3062acd0e350598727f8dac459ae3a5ee05db7c6 (diff)
ox-taskjuggler.el: Update
* lisp/ox-taskjuggler.el (org-taskjuggler-report-tag): New option. (org-taskjuggler-valid-resource-attributes): New argument. (org-taskjuggler-valid-account-attributes): New option. (org-taskjuggler-valid-report-attributes): New arguments. (org-taskjuggler-assign-account-ids): New function. (org-taskjuggler-get-end): Use `org-taskjuggler-valid-task-attributes'. (org-taskjuggler-project-plan): Use `org-taskjuggler-account-tag'. (org-taskjuggler-project-plan): Rewrite. (org-taskjuggler--build-account): New function. (org-taskjuggler--build-report): Update.
-rw-r--r--lisp/ox-taskjuggler.el238
1 files changed, 165 insertions, 73 deletions
diff --git a/lisp/ox-taskjuggler.el b/lisp/ox-taskjuggler.el
index 4144685..19e4da9 100644
--- a/lisp/ox-taskjuggler.el
+++ b/lisp/ox-taskjuggler.el
@@ -177,6 +177,13 @@ for the project."
:group 'org-export-taskjuggler
:type 'string)
+(defcustom org-taskjuggler-account-tag "taskjuggler_account"
+ "Tag marking project's accounts.
+This tag is used to find the tree containing all the accounts
+for the project."
+ :group 'org-export-taskjuggler
+ :type 'string)
+
(defcustom org-taskjuggler-report-tag "taskjuggler_report"
"Tag marking project's reports.
This tag is used to find the tree containing all the reports for
@@ -330,16 +337,23 @@ list."
(defcustom org-taskjuggler-valid-resource-attributes
'(limits vacation shift booking efficiency journalentry rate
- workinghours flags)
+ workinghours flags chargeset)
"Valid attributes for Taskjuggler resources.
If one of these appears as a property for a headline, it will be
exported with the corresponding resource."
:group 'org-export-taskjuggler
:type '(repeat symbol))
+(defcustom org-taskjuggler-valid-account-attributes
+ '(aggregate credits flags)
+ "Valid attributes for Taskjuggler accounts.
+If one of these appears as a property for a headline, it will be
+exported with the corresponding account."
+ :group 'org-export-taskjuggler)
+
(defcustom org-taskjuggler-valid-report-attributes
- '(headline columns definitions timeformat hideresource hidetask
- loadunit sorttasks formats period)
+ '(headline columns definitions timeformat hideaccount hideresource hidetask
+ loadunit sorttasks formats period start end)
"Valid attributes for Taskjuggler reports.
If one of these appears as a property for a headline, it will be
exported with the corresponding report."
@@ -446,6 +460,19 @@ headlines and their associated ID."
(cons resource id)))
info)))
+(defun org-taskjuggler-assign-account-ids (accounts info)
+ "Assign a unique ID to each account within ACCOUNTS.
+ACCOUNTS is a list of headlines. INFO is a plist used as a
+communication channel. Return value is an alist between
+headlines and their associated ID."
+ (let (ids)
+ (org-element-map accounts 'headline
+ (lambda (account)
+ (let ((id (org-taskjuggler--build-unique-id account ids)))
+ (push id ids)
+ (cons account id)))
+ info)))
+
;;; Accessors
@@ -497,11 +524,10 @@ doesn't have any start date defined."
ITEM is a headline. Return value is a string or nil if ITEM
doesn't have any end date defined."
(let ((deadline (org-element-property :deadline item)))
- (and deadline (funcall (eval-and-compile
- (if (fboundp 'org-format-timestamp)
- #'org-format-timestamp
- (with-no-warnings #'org-timestamp-format)))
- deadline "%Y-%02m-%02d"))))
+ (or
+ (and deadline (org-timestamp-format deadline "%Y-%02m-%02d"))
+ (and (memq 'end org-taskjuggler-valid-task-attributes)
+ (org-element-property :END item)))))
@@ -654,89 +680,115 @@ Return complete project plan as a string in TaskJuggler syntax."
(org-taskjuggler--build-project project info)
;; 3. Insert global properties.
(org-element-normalize-string org-taskjuggler-default-global-properties)
- ;; 4. Insert resources. Provide a default one if none is
+ ;; 3.5. Insert accounts. Provide a default one if none is
;; specified.
- (let ((main-resources
+ (let ((main-accounts
;; Collect contents from various trees marked with
- ;; `org-taskjuggler-resource-tag'. Only gather top level
- ;; resources.
+ ;; `org-taskjuggler-account-tag'. Only gather top level
+ ;; accounts.
(apply 'append
(org-element-map tree 'headline
(lambda (hl)
- (and (member org-taskjuggler-resource-tag
+ (and (member org-taskjuggler-account-tag
(org-export-get-tags hl info))
(org-element-map (org-element-contents hl) 'headline
'identity info nil 'headline)))
info nil 'headline))))
- ;; Assign a unique ID to each resource. Store it under
+ ;; Assign a unique ID to each account. Store it under
;; `:taskjuggler-unique-ids' property in INFO.
(setq info
(plist-put info :taskjuggler-unique-ids
- (org-taskjuggler-assign-resource-ids
- main-resources info)))
+ (org-taskjuggler-assign-account-ids
+ main-accounts info)))
(concat
- (if main-resources
+ (if main-accounts
(mapconcat
- (lambda (resource) (org-taskjuggler--build-resource resource info))
- main-resources "")
- (format "resource %s \"%s\" {\n}\n" (user-login-name) user-full-name))
- ;; 5. Insert tasks.
- (let ((main-tasks
- ;; If `org-taskjuggler-keep-project-as-task' is
- ;; non-nil, there is only one task. Otherwise, every
- ;; direct children of PROJECT is a top level task.
- (if org-taskjuggler-keep-project-as-task (list project)
- (or (org-element-map (org-element-contents project) 'headline
- 'identity info nil 'headline)
- (error "No task specified")))))
- ;; Assign a unique ID to each task. Add it to
- ;; `:taskjuggler-unique-ids' property in INFO.
- (setq info
- (plist-put info :taskjuggler-unique-ids
- (append
- (org-taskjuggler-assign-task-ids main-tasks info)
- (plist-get info :taskjuggler-unique-ids))))
- ;; If no resource is allocated among tasks, allocate one to
- ;; the first task.
- (unless (org-element-map main-tasks 'headline
- (lambda (task) (org-element-property :ALLOCATE task))
- info t)
- (org-element-put-property
- (car main-tasks) :ALLOCATE
- (or (org-taskjuggler-get-id (car main-resources) info)
- (user-login-name))))
- (mapconcat
- (lambda (task) (org-taskjuggler--build-task task info))
- main-tasks ""))
- ;; 6. Insert reports. If no report is defined, insert default
- ;; reports.
- (let ((main-reports
+ (lambda (account) (org-taskjuggler--build-account account info))
+ main-accounts "")
+ (format "account %s \"%s\" {\n}\n" (user-login-name) user-full-name))
+ ;; 4. Insert resources. Provide a default one if none is
+ ;; specified.
+ (let ((main-resources
;; Collect contents from various trees marked with
- ;; `org-taskjuggler-report-tag'. Only gather top level
- ;; reports.
+ ;; `org-taskjuggler-resource-tag'. Only gather top level
+ ;; resources.
(apply 'append
(org-element-map tree 'headline
(lambda (hl)
- (and (member org-taskjuggler-report-tag
+ (and (member org-taskjuggler-resource-tag
(org-export-get-tags hl info))
- (org-element-map (org-element-contents hl)
- 'headline 'identity info nil 'headline)))
+ (org-element-map (org-element-contents hl) 'headline
+ 'identity info nil 'headline)))
info nil 'headline))))
- (if main-reports
- (mapconcat
- (lambda (report) (org-taskjuggler--build-report report info))
- main-reports "")
- ;; insert title in default reports
- (let* ((title (org-export-data (plist-get info :title) info))
- (report-title (if (string= title "")
- (org-taskjuggler-get-name project)
- title)))
- (mapconcat
- 'org-element-normalize-string
- (mapcar
- (lambda (report)
- (replace-regexp-in-string "%title" report-title report t t))
- org-taskjuggler-default-reports) "")))))))))
+ ;; Assign a unique ID to each resource. Store it under
+ ;; `:taskjuggler-unique-ids' property in INFO.
+ (setq info
+ (plist-put info :taskjuggler-unique-ids
+ (org-taskjuggler-assign-resource-ids
+ main-resources info)))
+ (concat
+ (if main-resources
+ (mapconcat
+ (lambda (resource) (org-taskjuggler--build-resource resource info))
+ main-resources "")
+ (format "resource %s \"%s\" {\n}\n" (user-login-name) user-full-name))
+ ;; 5. Insert tasks.
+ (let ((main-tasks
+ ;; If `org-taskjuggler-keep-project-as-task' is
+ ;; non-nil, there is only one task. Otherwise, every
+ ;; direct children of PROJECT is a top level task.
+ (if org-taskjuggler-keep-project-as-task (list project)
+ (or (org-element-map (org-element-contents project) 'headline
+ 'identity info nil 'headline)
+ (error "No task specified")))))
+ ;; Assign a unique ID to each task. Add it to
+ ;; `:taskjuggler-unique-ids' property in INFO.
+ (setq info
+ (plist-put info :taskjuggler-unique-ids
+ (append
+ (org-taskjuggler-assign-task-ids main-tasks info)
+ (plist-get info :taskjuggler-unique-ids))))
+ ;; If no resource is allocated among tasks, allocate one to
+ ;; the first task.
+ (unless (org-element-map main-tasks 'headline
+ (lambda (task) (org-element-property :ALLOCATE task))
+ info t)
+ (org-element-put-property
+ (car main-tasks) :ALLOCATE
+ (or (org-taskjuggler-get-id (car main-resources) info)
+ (user-login-name))))
+ (mapconcat
+ (lambda (task) (org-taskjuggler--build-task task info))
+ main-tasks ""))
+ ;; 6. Insert reports. If no report is defined, insert default
+ ;; reports.
+ (let ((main-reports
+ ;; Collect contents from various trees marked with
+ ;; `org-taskjuggler-report-tag'. Only gather top level
+ ;; reports.
+ (apply 'append
+ (org-element-map tree 'headline
+ (lambda (hl)
+ (and (member org-taskjuggler-report-tag
+ (org-export-get-tags hl info))
+ (org-element-map (org-element-contents hl)
+ 'headline 'identity info nil 'headline)))
+ info nil 'headline))))
+ (if main-reports
+ (mapconcat
+ (lambda (report) (org-taskjuggler--build-report report info))
+ main-reports "")
+ ;; insert title in default reports
+ (let* ((title (org-export-data (plist-get info :title) info))
+ (report-title (if (string= title "")
+ (org-taskjuggler-get-name project)
+ title)))
+ (mapconcat
+ 'org-element-normalize-string
+ (mapcar
+ (lambda (report)
+ (replace-regexp-in-string "%title" report-title report t t))
+ org-taskjuggler-default-reports) "")))))))))))
(defun org-taskjuggler--build-project (project info)
"Return a project declaration.
@@ -799,20 +851,60 @@ neither is defined a unique id will be associated to it."
;; Closing resource.
"}\n"))
+(defun org-taskjuggler--build-account (account info)
+ "Return a account declaration.
+
+ACCOUNT is a headline. INFO is a plist used as a communication
+channel.
+
+All valid attributes from ACCOUNT are inserted. If ACCOUNT
+defines a property \"account_id\" it will be used as the id for
+this account. Otherwise it will use the ID property. If
+neither is defined a unique id will be associated to it."
+ (concat
+ ;; Opening account.
+ (format "account %s \"%s\" {\n"
+ (org-taskjuggler--clean-id
+ (or (org-element-property :ACCOUNT_ID account)
+ (org-element-property :ID account)
+ (org-taskjuggler-get-id account info)))
+ (org-taskjuggler-get-name account))
+ ;; Add attributes.
+ (org-taskjuggler--indent-string
+ (org-taskjuggler--build-attributes
+ account org-taskjuggler-valid-account-attributes))
+ ;; Add inner accounts.
+ (org-taskjuggler--indent-string
+ (mapconcat
+ 'identity
+ (org-element-map (org-element-contents account) 'headline
+ (lambda (hl) (org-taskjuggler--build-account hl info))
+ info nil 'headline)
+ ""))
+ ;; Closing account.
+ "}\n"))
+
(defun org-taskjuggler--build-report (report info)
"Return a report declaration.
REPORT is a headline. INFO is a plist used as a communication
channel."
(concat
;; Opening report.
- (format "%s \"%s\" {\n"
+ (format "%s %s \"%s\" {\n"
(or (org-element-property :REPORT_KIND report) "taskreport")
+ (or (org-element-property :REPORT_ID report)
+ (org-element-property :ID report)
+ (org-taskjuggler-get-id report info))
(org-taskjuggler-get-name report))
;; Add attributes.
(org-taskjuggler--indent-string
(org-taskjuggler--build-attributes
report org-taskjuggler-valid-report-attributes))
- ;; Add inner reports.
+ ;; Add core of report, ie the first paragraph after the headline
+ ;; and before the next sub-headline
+ (format "%s" (if (eq (org-element-type (car (org-element-contents report))) 'section)
+ (org-element-interpret-data (car (org-element-contents report)))
+ ""))
(org-taskjuggler--indent-string
(mapconcat
'identity