update packages
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
;;; ox-taskjuggler.el --- TaskJuggler Back-End for Org Export Engine
|
||||
;;; ox-taskjuggler.el --- TaskJuggler Back-End for Org Export Engine -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (C) 2007-2021 Free Software Foundation, Inc.
|
||||
;;
|
||||
@@ -8,6 +8,7 @@
|
||||
;; Nicolas Goaziou <n dot goaziou at gmail dot com>
|
||||
;; Maintainer: Christian Egli
|
||||
;; Keywords: org, taskjuggler, project planning
|
||||
;; Homepage: https://github.com/h-oll/ox-taskjuggler
|
||||
;; Description: Converts an Org mode buffer into a TaskJuggler project plan
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
@@ -145,7 +146,7 @@
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
(eval-when-compile (require 'cl-lib))
|
||||
|
||||
(require 'ox)
|
||||
|
||||
@@ -177,6 +178,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
|
||||
@@ -311,36 +319,47 @@ exported with the corresponding task.
|
||||
|
||||
Note that multiline properties are not supported, so attributes
|
||||
like note or journalentry have to be on a single line."
|
||||
:group 'org-export-taskjuggler)
|
||||
:group 'org-export-taskjuggler
|
||||
:type '(repeat symbol))
|
||||
|
||||
(defcustom org-taskjuggler-valid-project-attributes
|
||||
'(timingresolution timezone alertlevels currency currencyformat
|
||||
dailyworkinghours extend includejournalentry now numberformat
|
||||
outputdir scenario shorttimeformat timeformat trackingscenario
|
||||
weekstartsmonday weekstartssunday workinghours
|
||||
yearlyworkingdays)
|
||||
'( timingresolution timezone alertlevels currency currencyformat
|
||||
dailyworkinghours extend includejournalentry now numberformat
|
||||
outputdir scenario shorttimeformat timeformat trackingscenario
|
||||
weekstartsmonday weekstartssunday workinghours
|
||||
yearlyworkingdays)
|
||||
"Valid attributes for Taskjuggler project.
|
||||
If one of these appears as a property for a headline that is a
|
||||
project definition, it will be exported with the corresponding
|
||||
task. Attribute 'timingresolution' should be the first in the
|
||||
task. Attribute `timingresolution' should be the first in the
|
||||
list."
|
||||
:group 'org-export-taskjuggler)
|
||||
:group 'org-export-taskjuggler
|
||||
:type '(repeat symbol))
|
||||
|
||||
(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."
|
||||
:group 'org-export-taskjuggler)
|
||||
:group 'org-export-taskjuggler
|
||||
:type '(repeat symbol))
|
||||
|
||||
(defcustom org-taskjuggler-process-command
|
||||
"tj3 --silent --no-color --output-dir %o %f"
|
||||
@@ -352,7 +371,8 @@ full file name, \"%o\" by the reports directory (see
|
||||
|
||||
If you are targeting Taskjuggler 2.4 (see
|
||||
`org-taskjuggler-target-version') this setting is ignored."
|
||||
:group 'org-export-taskjuggler)
|
||||
:group 'org-export-taskjuggler
|
||||
:type 'string)
|
||||
|
||||
(defcustom org-taskjuggler-reports-directory "reports"
|
||||
"Default directory to generate the Taskjuggler reports in.
|
||||
@@ -365,7 +385,8 @@ doesn't exist.
|
||||
|
||||
If you are targeting Taskjuggler 2.4 (see
|
||||
`org-taskjuggler-target-version') this setting is ignored."
|
||||
:group 'org-export-taskjuggler)
|
||||
:group 'org-export-taskjuggler
|
||||
:type 'string)
|
||||
|
||||
(defcustom org-taskjuggler-keep-project-as-task t
|
||||
"Non-nil keeps the project headline as an umbrella task for all tasks.
|
||||
@@ -440,6 +461,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
|
||||
@@ -478,7 +512,11 @@ ITEM is a headline. Return value is a string or nil if ITEM
|
||||
doesn't have any start date defined."
|
||||
(let ((scheduled (org-element-property :scheduled item)))
|
||||
(or
|
||||
(and scheduled (org-timestamp-format scheduled "%Y-%02m-%02d"))
|
||||
(and scheduled (funcall (eval-and-compile
|
||||
(if (fboundp 'org-format-timestamp)
|
||||
#'org-format-timestamp
|
||||
(with-no-warnings #'org-timestamp-format)))
|
||||
scheduled "%Y-%02m-%02d"))
|
||||
(and (memq 'start org-taskjuggler-valid-task-attributes)
|
||||
(org-element-property :START item)))))
|
||||
|
||||
@@ -487,7 +525,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 (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)))))
|
||||
|
||||
|
||||
|
||||
@@ -626,9 +667,9 @@ doesn't include leading \"depends\"."
|
||||
|
||||
;;; Translator Functions
|
||||
|
||||
(defun org-taskjuggler-project-plan (contents info)
|
||||
(defun org-taskjuggler-project-plan (_ info)
|
||||
"Build TaskJuggler project plan.
|
||||
CONTENTS is ignored. INFO is a plist holding export options.
|
||||
INFO is a plist holding export options.
|
||||
Return complete project plan as a string in TaskJuggler syntax."
|
||||
(let* ((tree (plist-get info :parse-tree))
|
||||
(project (or (org-taskjuggler-get-project info)
|
||||
@@ -640,89 +681,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.
|
||||
@@ -785,20 +852,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
|
||||
|
||||
Reference in New Issue
Block a user