;;; org-taskjuggler.el --- TaskJuggler exporter for org-mode
;;
-;; Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+;; Copyright (C) 2007-2013 Free Software Foundation, Inc.
;;
;; Emacs Lisp Archive Entry
;; Filename: org-taskjuggler.el
-;; Version: 7.4
;; Author: Christian Egli
;; Maintainer: Christian Egli
;; Keywords: org, taskjuggler, project planning
;;
;; This library implements a TaskJuggler exporter for org-mode.
;; TaskJuggler uses a text format to define projects, tasks and
-;; resources, so it is a natural fit for org-mode. It can produce all
+;; resources, so it is a natural fit for org-mode. It can produce all
;; sorts of reports for tasks or resources in either HTML, CSV or PDF.
;; The current version of TaskJuggler requires KDE but the next
;; version is implemented in Ruby and should therefore run on any
;;
;; Instead the TaskJuggler exporter looks for a tree that defines the
;; tasks and a optionally tree that defines the resources for this
-;; project. It then creates a TaskJuggler file based on these trees
+;; project. It then creates a TaskJuggler file based on these trees
;; and the attributes defined in all the nodes.
;;
;; * Installation
;;
;; * Tasks
;;
-;; Let's illustrate the usage with a small example. Create your tasks
-;; as you usually do with org-mode. Assign efforts to each task using
+;; Let's illustrate the usage with a small example. Create your tasks
+;; as you usually do with org-mode. Assign efforts to each task using
;; properties (it's easiest to do this in the column view). You should
;; end up with something similar to the example by Peter Jones in
;; http://www.contextualdevelopment.com/static/artifacts/articles/2008/project-planning/project-planning.org.
;; "taskjuggler_project" (or whatever you customized
;; `org-export-taskjuggler-project-tag' to). You are now ready to
;; export the project plan with `org-export-as-taskjuggler-and-open'
-;; which will export the project plan and open a gant chart in
+;; which will export the project plan and open a Gantt chart in
;; TaskJugglerUI.
;;
;; * Resources
-;;
+;;
;; Next you can define resources and assign those to work on specific
-;; tasks. You can group your resources hierarchically. Tag the top
+;; tasks. You can group your resources hierarchically. Tag the top
;; node of the resources with "taskjuggler_resource" (or whatever you
;; customized `org-export-taskjuggler-resource-tag' to). You can
;; optionally assign an identifier (named "resource_id") to the
;; picks the first word of the headline as the identifier as long as
;; it is unique, see the documentation of
;; `org-taskjuggler-get-unique-id'). Using that identifier you can
-;; then allocate resources to tasks. This is again done with the
-;; "allocate" property on the tasks. Do this in column view or when on
+;; then allocate resources to tasks. This is again done with the
+;; "allocate" property on the tasks. Do this in column view or when on
;; the task type
;;
;; C-c C-x p allocate RET <resource_id> RET
;; etc for tasks.
;;
;; * Dependencies
-;;
+;;
;; The exporter will handle dependencies that are defined in the tasks
;; either with the ORDERED attribute (see TODO dependencies in the Org
;; mode manual) or with the BLOCKER attribute (see org-depend.el) or
-;; alternatively with a depends attribute. Both the BLOCKER and the
+;; alternatively with a depends attribute. Both the BLOCKER and the
;; depends attribute can be either "previous-sibling" or a reference
;; to an identifier (named "task_id") which is defined for another
-;; task in the project. BLOCKER and the depends attribute can define
-;; multiple dependencies separated by either space or comma. You can
+;; task in the project. BLOCKER and the depends attribute can define
+;; multiple dependencies separated by either space or comma. You can
;; also specify optional attributes on the dependency by simply
-;; appending it. The following examples should illustrate this:
+;; appending it. The following examples should illustrate this:
;;
;; * Training material
;; :PROPERTIES:
;; :END:
;; ** Markup Guidelines
;; :PROPERTIES:
-;; :Effort: 2.0
+;; :Effort: 2d
;; :END:
;; ** Workflow Guidelines
;; :PROPERTIES:
-;; :Effort: 2.0
+;; :Effort: 2d
;; :END:
;; * Presentation
;; :PROPERTIES:
-;; :Effort: 2.0
+;; :Effort: 2d
;; :BLOCKER: training_material { gapduration 1d } some_other_task
;; :END:
-;;
+;;
;;;; * TODO
;; - Use SCHEDULED and DEADLINE information (not just start and end
;; properties).
;; org-global-properties-fixed
;; - What about property inheritance and org-property-inherit-p?
;; - Use TYPE_TODO as an way to assign resources
-;; - Make sure multiple dependency definitions (i.e. BLOCKER on
+;; - Make sure multiple dependency definitions (i.e. BLOCKER on
;; previous-sibling and on a specific task_id) in multiple
;; attributes are properly exported.
;;
(defcustom org-export-taskjuggler-extension ".tjp"
"Extension of TaskJuggler files."
:group 'org-export-taskjuggler
+ :version "24.1"
:type 'string)
(defcustom org-export-taskjuggler-project-tag "taskjuggler_project"
"Tag, property or todo used to find the tree containing all
the tasks for the project."
:group 'org-export-taskjuggler
+ :version "24.1"
:type 'string)
(defcustom org-export-taskjuggler-resource-tag "taskjuggler_resource"
"Tag, property or todo used to find the tree containing all the
resources for the project."
:group 'org-export-taskjuggler
+ :version "24.1"
:type 'string)
+(defcustom org-export-taskjuggler-target-version 2.4
+ "Which version of TaskJuggler the exporter is targeting."
+ :group 'org-export-taskjuggler
+ :version "24.1"
+ :type 'number)
+
(defcustom org-export-taskjuggler-default-project-version "1.0"
"Default version string for the project."
:group 'org-export-taskjuggler
+ :version "24.1"
:type 'string)
(defcustom org-export-taskjuggler-default-project-duration 280
in the root node of the task tree, i.e. the tree that has been marked
with `org-export-taskjuggler-project-tag'"
:group 'org-export-taskjuggler
+ :version "24.1"
:type 'integer)
-(defcustom org-export-taskjuggler-default-reports
+(defcustom org-export-taskjuggler-default-reports
'("taskreport \"Gantt Chart\" {
headline \"Project Gantt Chart\"
columns hierarchindex, name, start, end, effort, duration, completed, chart
hideresource 1
loadunit shortauto
}"
-"resourcereport \"Resource Graph\" {
+ "resourcereport \"Resource Graph\" {
headline \"Resource Allocation Graph\"
columns no, name, utilization, freeload, chart
loadunit shortauto
}")
"Default reports for the project."
:group 'org-export-taskjuggler
+ :version "24.1"
:type '(repeat (string :tag "Report")))
-(defcustom org-export-taskjuggler-default-global-properties
+(defcustom org-export-taskjuggler-default-global-properties
"shift s40 \"Part time shift\" {
workinghours wed, thu, fri off
}
"
- "Default global properties for the project. Here you typically
+ "Default global properties for the project. Here you typically
define global properties such as shifts, accounts, rates,
-vacation, macros and flags. Any property that is allowed within
-the TaskJuggler file can be inserted. You could for example
-include another TaskJuggler file.
+vacation, macros and flags. Any property that is allowed within
+the TaskJuggler file can be inserted. You could for example
+include another TaskJuggler file.
The global properties are inserted after the project declaration
but before any resource and task declarations."
:group 'org-export-taskjuggler
+ :version "24.1"
:type '(string :tag "Preamble"))
;;; Hooks
"Export parts of the current buffer as a TaskJuggler file.
The exporter looks for a tree with tag, property or todo that
matches `org-export-taskjuggler-project-tag' and takes this as
-the tasks for this project. The first node of this tree defines
+the tasks for this project. The first node of this tree defines
the project properties such as project name and project period.
If there is a tree with tag, property or todo that matches
`org-export-taskjuggler-resource-tag' this three is taken as
-resources for the project. If no resources are specified, a
-default resource is created and allocated to the project. Also
+resources for the project. If no resources are specified, a
+default resource is created and allocated to the project. Also
the taskjuggler project will be created with default reports as
defined in `org-export-taskjuggler-default-reports'."
(interactive)
(setq-default org-done-keywords org-done-keywords)
(let* ((tasks
(org-taskjuggler-resolve-dependencies
- (org-taskjuggler-assign-task-ids
- (org-map-entries
- '(org-taskjuggler-components)
- org-export-taskjuggler-project-tag nil 'archive 'comment))))
+ (org-taskjuggler-assign-task-ids
+ (org-taskjuggler-compute-task-leafiness
+ (org-map-entries
+ 'org-taskjuggler-components
+ org-export-taskjuggler-project-tag nil 'archive 'comment)))))
(resources
(org-taskjuggler-assign-resource-ids
- (org-map-entries
- '(org-taskjuggler-components)
+ (org-map-entries
+ 'org-taskjuggler-components
org-export-taskjuggler-resource-tag nil 'archive 'comment)))
(filename (expand-file-name
(concat
(file-name-nondirectory buffer-file-name))
org-export-taskjuggler-extension)))
(buffer (find-file-noselect filename))
+ (old-buffer (current-buffer))
(org-export-taskjuggler-old-level 0)
task resource)
(unless tasks
(error "No tasks specified"))
;; add a default resource
(unless resources
- (setq resources
- `((("resource_id" . ,(user-login-name))
- ("headline" . ,user-full-name)
+ (setq resources
+ `((("resource_id" . ,(user-login-name))
+ ("headline" . ,user-full-name)
("level" . 1)))))
;; add a default allocation to the first task if none was given
(unless (assoc "allocate" (car tasks))
(setcar tasks (push (cons "version" version) task))))
(with-current-buffer buffer
(erase-buffer)
+ (org-clone-local-variables old-buffer "^org-")
(org-taskjuggler-open-project (car tasks))
(insert org-export-taskjuggler-default-global-properties)
(insert "\n")
(command (concat process-name " " file-name)))
(start-process-shell-command process-name nil command)))
+(defun org-taskjuggler-targeting-tj3-p ()
+ "Return true if we are targeting TaskJuggler III."
+ (>= org-export-taskjuggler-target-version 3.0))
+
(defun org-taskjuggler-parent-is-ordered-p ()
"Return true if the parent of the current node has a property
-\"ORDERED\". Return nil otherwise."
+\"ORDERED\". Return nil otherwise."
(save-excursion
(and (org-up-heading-safe) (org-entry-get (point) "ORDERED"))))
(let* ((props (org-entry-properties))
(components (org-heading-components))
(level (nth 1 components))
- (headline (nth 4 components))
+ (headline
+ (replace-regexp-in-string
+ "\"" "\\\"" (nth 4 components) t t)) ; quote double quotes in headlines
(parent-ordered (org-taskjuggler-parent-is-ordered-p)))
(push (cons "level" level) props)
(push (cons "headline" headline) props)
(defun org-taskjuggler-assign-task-ids (tasks)
"Given a list of tasks return the same list assigning a unique id
-and the full path to each task. Taskjuggler takes hierarchical ids.
+and the full path to each task. Taskjuggler takes hierarchical ids.
For that reason we have to make ids locally unique and we have to keep
a path to the current task."
(let ((previous-level 0)
(dolist (task tasks resolved-tasks)
(let ((level (cdr (assoc "level" task))))
(cond
- ((< previous-level level)
+ ((< previous-level level)
(setq unique-id (org-taskjuggler-get-unique-id task (car unique-ids)))
(dotimes (tmp (- level previous-level))
(push (list unique-id) unique-ids)
(push unique-id path)))
- ((= previous-level level)
+ ((= previous-level level)
(setq unique-id (org-taskjuggler-get-unique-id task (car unique-ids)))
(push unique-id (car unique-ids))
(setcar path unique-id))
- ((> previous-level level)
+ ((> previous-level level)
(dotimes (tmp (- previous-level level))
(pop unique-ids)
(pop path))
(setq previous-level level)
(setq resolved-tasks (append resolved-tasks (list task)))))))
-(defun org-taskjuggler-assign-resource-ids (resources &optional unique-ids)
+(defun org-taskjuggler-compute-task-leafiness (tasks)
+ "Figure out if each task is a leaf by looking at it's level,
+and the level of its successor. If the successor is higher (ie
+deeper), then it's not a leaf."
+ (let (new-list)
+ (while (car tasks)
+ (let ((task (car tasks))
+ (successor (car (cdr tasks))))
+ (cond
+ ;; if a task has no successors it is a leaf
+ ((null successor)
+ (push (cons (cons "leaf-node" t) task) new-list))
+ ;; if the successor has a lower level than task it is a leaf
+ ((<= (cdr (assoc "level" successor)) (cdr (assoc "level" task)))
+ (push (cons (cons "leaf-node" t) task) new-list))
+ ;; otherwise examine the rest of the tasks
+ (t (push task new-list))))
+ (setq tasks (cdr tasks)))
+ (nreverse new-list)))
+
+(defun org-taskjuggler-assign-resource-ids (resources)
"Given a list of resources return the same list, assigning a
unique id to each resource."
- (cond
- ((null resources) nil)
- (t
- (let* ((resource (car resources))
- (unique-id (org-taskjuggler-get-unique-id resource unique-ids)))
- (push (cons "unique-id" unique-id) resource)
- (cons resource
- (org-taskjuggler-assign-resource-ids (cdr resources)
- (cons unique-id unique-ids)))))))
+ (let (unique-ids new-list)
+ (dolist (resource resources new-list)
+ (let ((unique-id (org-taskjuggler-get-unique-id resource unique-ids)))
+ (push (cons "unique-id" unique-id) resource)
+ (push unique-id unique-ids)
+ (push resource new-list)))
+ (nreverse new-list)))
(defun org-taskjuggler-resolve-dependencies (tasks)
(let ((previous-level 0)
(depends (cdr (assoc "depends" task)))
(parent-ordered (cdr (assoc "parent-ordered" task)))
(blocker (cdr (assoc "BLOCKER" task)))
- (blocked-on-previous
+ (blocked-on-previous
(and blocker (string-match "previous-sibling" blocker)))
(dependencies
(org-taskjuggler-resolve-explicit-dependencies
- (append
+ (append
(and depends (org-taskjuggler-tokenize-dependencies depends))
- (and blocker (org-taskjuggler-tokenize-dependencies blocker)))
+ (and blocker (org-taskjuggler-tokenize-dependencies blocker)))
tasks))
- previous-sibling)
- ; update previous sibling info
+ previous-sibling)
+ ; update previous sibling info
(cond
- ((< previous-level level)
+ ((< previous-level level)
(dotimes (tmp (- level previous-level))
(push task siblings)))
((= previous-level level)
(setq previous-sibling (car siblings))
(setcar siblings task))
- ((> previous-level level)
+ ((> previous-level level)
(dotimes (tmp (- previous-level level))
(pop siblings))
(setq previous-sibling (car siblings))
(setcar siblings task)))
- ; insert a dependency on previous sibling if the parent is
- ; ordered or if the tasks has a BLOCKER attribute with value "previous-sibling"
+ ; insert a dependency on previous sibling if the parent is
+ ; ordered or if the tasks has a BLOCKER attribute with value "previous-sibling"
(when (or (and previous-sibling parent-ordered) blocked-on-previous)
(push (format "!%s" (cdr (assoc "unique-id" previous-sibling))) dependencies))
- ; store dependency information
- (when dependencies
+ ; store dependency information
+ (when dependencies
(push (cons "depends" (mapconcat 'identity dependencies ", ")) task))
(setq previous-level level)
(setq resolved-tasks (append resolved-tasks (list task)))))))
"Split a dependency property value DEPENDENCIES into the
individual dependencies and return them as a list while keeping
the optional arguments (such as gapduration) for the
-dependencies. A dependency will have to match `[-a-zA-Z0-9_]+'."
- (cond
+dependencies. A dependency will have to match `[-a-zA-Z0-9_]+'."
+ (cond
((string-match "^ *$" dependencies) nil)
((string-match "^[ \t]*\\([-a-zA-Z0-9_]+\\([ \t]*{[^}]+}\\)?\\)[ \t,]*" dependencies)
- (cons
+ (cons
(substring dependencies (match-beginning 1) (match-end 1))
(org-taskjuggler-tokenize-dependencies (substring dependencies (match-end 0)))))
(t (error (format "invalid dependency id %s" dependencies)))))
"For each dependency in DEPENDENCIES try to find a
corresponding task with a matching property \"task_id\" in TASKS.
Return a list containing the resolved links for all DEPENDENCIES
-where a matching tasks was found. If the dependency is
+where a matching tasks was found. If the dependency is
\"previous-sibling\" it is ignored (as this is dealt with in
`org-taskjuggler-resolve-dependencies'). If there is no matching
task the dependency is ignored and a warning is displayed ."
(unless (null dependencies)
- (let*
+ (let*
;; the dependency might have optional attributes such as "{
;; gapduration 5d }", so only use the first string as id for the
;; dependency
((dependency (car dependencies))
(id (car (split-string dependency)))
- (optional-attributes
+ (optional-attributes
(mapconcat 'identity (cdr (split-string dependency)) " "))
(path (org-taskjuggler-find-task-with-id id tasks)))
- (cond
+ (cond
;; ignore previous sibling dependencies
((equal (car dependencies) "previous-sibling")
(org-taskjuggler-resolve-explicit-dependencies (cdr dependencies) tasks))
;; if the id is found in another task use its path
- ((not (null path))
+ ((not (null path))
(cons (mapconcat 'identity (list path optional-attributes) " ")
- (org-taskjuggler-resolve-explicit-dependencies
+ (org-taskjuggler-resolve-explicit-dependencies
(cdr dependencies) tasks)))
;; warn about dangling dependency but otherwise ignore it
- (t (display-warning
- 'org-export-taskjuggler
+ (t (display-warning
+ 'org-export-taskjuggler
(format "No task with matching property \"task_id\" found for id %s" id))
(org-taskjuggler-resolve-explicit-dependencies (cdr dependencies) tasks))))))
(defun org-taskjuggler-find-task-with-id (id tasks)
- "Find ID in tasks. If found return the path of task. Otherwise
+ "Find ID in tasks. If found return the path of task. Otherwise
return nil."
(let ((task-id (cdr (assoc "task_id" (car tasks))))
(path (cdr (assoc "path" (car tasks)))))
- (cond
+ (cond
((null tasks) nil)
((equal task-id id) path)
(t (org-taskjuggler-find-task-with-id id (cdr tasks))))))
(let* ((headline (cdr (assoc "headline" item)))
(parts (split-string headline))
(id (org-taskjuggler-clean-id (downcase (pop parts)))))
- ; try to add more parts of the headline to make it unique
+ ; try to add more parts of the headline to make it unique
(while (and (member id unique-ids) (car parts))
(setq id (concat id "_" (org-taskjuggler-clean-id (downcase (pop parts))))))
- ; if its still not unique add "_"
+ ; if its still not unique add "_"
(while (member id unique-ids)
(setq id (concat id "_")))
id))
-
+
(defun org-taskjuggler-clean-id (id)
"Clean and return ID to make it acceptable for taskjuggler."
- (and id (replace-regexp-in-string "[^a-zA-Z0-9_]" "_" id)))
+ (and id
+ ;; replace non-ascii by _
+ (replace-regexp-in-string
+ "[^a-zA-Z0-9_]" "_"
+ ;; make sure id doesn't start with a number
+ (replace-regexp-in-string "^\\([0-9]\\)" "_\\1" id))))
(defun org-taskjuggler-open-project (project)
- "Insert the beginning of a project declaration. All valid
-attributes from the PROJECT alist are inserted. If no end date is
+ "Insert the beginning of a project declaration. All valid
+attributes from the PROJECT alist are inserted. If no end date is
specified it is calculated
`org-export-taskjuggler-default-project-duration' days from now."
(let* ((unique-id (cdr (assoc "unique-id" project)))
- (headline (cdr (assoc "headline" project)))
- (version (cdr (assoc "version" project)))
- (start (cdr (assoc "start" project)))
- (end (cdr (assoc "end" project))))
- (insert
+ (headline (cdr (assoc "headline" project)))
+ (version (cdr (assoc "version" project)))
+ (start (cdr (assoc "start" project)))
+ (end (cdr (assoc "end" project))))
+ (insert
(format "project %s \"%s\" \"%s\" %s +%sd {\n }\n"
unique-id headline version start
org-export-taskjuggler-default-project-duration))))
with separator \"\n\"."
(let ((filtered-items (remq nil items)))
(and filtered-items (mapconcat 'identity filtered-items "\n"))))
-
+
(defun org-taskjuggler-get-attributes (item attributes)
- "Return all attribute as a single formated string. ITEM is an
-alist representing either a resource or a task. ATTRIBUTES is a
-list of symbols. Only entries from ITEM are considered that are
+ "Return all attribute as a single formatted string. ITEM is an
+alist representing either a resource or a task. ATTRIBUTES is a
+list of symbols. Only entries from ITEM are considered that are
listed in ATTRIBUTES."
- (org-taskjuggler-filter-and-join
+ (org-taskjuggler-filter-and-join
(mapcar
- (lambda (attribute)
- (org-taskjuggler-filter-and-join
+ (lambda (attribute)
+ (org-taskjuggler-filter-and-join
(org-taskjuggler-get-attribute item attribute)))
attributes)))
"Return a list of strings containing the properly formatted
taskjuggler declaration for a given ATTRIBUTE in ITEM (an alist).
If the ATTRIBUTE is not in ITEM return nil."
- (cond
+ (cond
((null item) nil)
((equal (symbol-name attribute) (car (car item)))
(cons (format "%s %s" (symbol-name attribute) (cdr (car item)))
(t (org-taskjuggler-get-attribute (cdr item) attribute))))
(defun org-taskjuggler-open-resource (resource)
- "Insert the beginning of a resource declaration. All valid
-attributes from the RESOURCE alist are inserted. If the RESOURCE
+ "Insert the beginning of a resource declaration. All valid
+attributes from the RESOURCE alist are inserted. If the RESOURCE
defines a property \"resource_id\" it will be used as the id for
-this resource. Otherwise it will use the ID property. If neither
+this resource. Otherwise it will use the ID property. If neither
is defined it will calculate a unique id for the resource using
`org-taskjuggler-get-unique-id'."
- (let ((id (org-taskjuggler-clean-id
- (or (cdr (assoc "resource_id" resource))
- (cdr (assoc "ID" resource))
+ (let ((id (org-taskjuggler-clean-id
+ (or (cdr (assoc "resource_id" resource))
+ (cdr (assoc "ID" resource))
(cdr (assoc "unique-id" resource)))))
(headline (cdr (assoc "headline" resource)))
(attributes '(limits vacation shift booking efficiency journalentry rate)))
- (insert
- (concat
+ (insert
+ (concat
"resource " id " \"" headline "\" {\n "
(org-taskjuggler-get-attributes resource attributes) "\n"))))
(defun org-taskjuggler-clean-effort (effort)
"Translate effort strings into a format acceptable to taskjuggler,
-i.e. REAL UNIT. If the effort string is something like 5:30 it
-will be assumed to be hours and will be translated into 5.5h.
-Otherwise if it contains something like 3.0 it is assumed to be
-days and will be translated into 3.0d. Other formats that
-taskjuggler supports (like weeks, months and years) are currently
-not supported."
- (cond
+i.e. REAL UNIT. A valid effort string can be anything that is
+accepted by `org-duration-string-to-minutesĀ“."
+ (cond
((null effort) effort)
- ((string-match "\\([0-9]+\\):\\([0-9]+\\)" effort)
- (let ((hours (string-to-number (match-string 1 effort)))
- (minutes (string-to-number (match-string 2 effort))))
- (format "%dh" (+ hours (/ minutes 60.0)))))
- ((string-match "\\([0-9]+\\).\\([0-9]+\\)" effort) (concat effort "d"))
- (t (error "Not a valid effort (%s)" effort))))
+ (t (let* ((minutes (org-duration-string-to-minutes effort))
+ (hours (/ minutes 60.0)))
+ (format "%.1fh" hours)))))
(defun org-taskjuggler-get-priority (priority)
"Return a priority between 1 and 1000 based on PRIORITY, an
org-mode priority string."
- (max 1 (/ (* 1000 (- org-lowest-priority (string-to-char priority)))
+ (max 1 (/ (* 1000 (- org-lowest-priority (string-to-char priority)))
(- org-lowest-priority org-highest-priority))))
(defun org-taskjuggler-open-task (task)
(let* ((unique-id (cdr (assoc "unique-id" task)))
- (headline (cdr (assoc "headline" task)))
- (effort (org-taskjuggler-clean-effort (cdr (assoc org-effort-property task))))
- (depends (cdr (assoc "depends" task)))
- (allocate (cdr (assoc "allocate" task)))
- (priority-raw (cdr (assoc "PRIORITY" task)))
- (priority (and priority-raw (org-taskjuggler-get-priority priority-raw)))
- (state (cdr (assoc "TODO" task)))
- (complete (or (and (member state org-done-keywords) "100")
- (cdr (assoc "complete" task))))
- (parent-ordered (cdr (assoc "parent-ordered" task)))
- (previous-sibling (cdr (assoc "previous-sibling" task)))
- (attributes
- '(account start note duration endbuffer endcredit end
- flags journalentry length maxend maxstart milestone
- minend minstart period reference responsible
- scheduling startbuffer startcredit statusnote)))
+ (headline (cdr (assoc "headline" task)))
+ (effort (org-taskjuggler-clean-effort (cdr (assoc org-effort-property task))))
+ (depends (cdr (assoc "depends" task)))
+ (allocate (cdr (assoc "allocate" task)))
+ (priority-raw (cdr (assoc "PRIORITY" task)))
+ (priority (and priority-raw (org-taskjuggler-get-priority priority-raw)))
+ (state (cdr (assoc "TODO" task)))
+ (complete (or (and (member state org-done-keywords) "100")
+ (cdr (assoc "complete" task))))
+ (parent-ordered (cdr (assoc "parent-ordered" task)))
+ (previous-sibling (cdr (assoc "previous-sibling" task)))
+ (milestone (or (cdr (assoc "milestone" task))
+ (and (assoc "leaf-node" task)
+ (not (or effort
+ (cdr (assoc "duration" task))
+ (cdr (assoc "end" task))
+ (cdr (assoc "period" task)))))))
+ (attributes
+ '(account start note duration endbuffer endcredit end
+ flags journalentry length maxend maxstart minend
+ minstart period reference responsible scheduling
+ startbuffer startcredit statusnote)))
(insert
- (concat
- "task " unique-id " \"" headline "\" {\n"
+ (concat
+ "task " unique-id " \"" headline "\" {\n"
(if (and parent-ordered previous-sibling)
(format " depends %s\n" previous-sibling)
(and depends (format " depends %s\n" depends)))
- (and allocate (format " purge allocations\n allocate %s\n" allocate))
+ (and allocate (format " purge %s\n allocate %s\n"
+ (or (and (org-taskjuggler-targeting-tj3-p) "allocate")
+ "allocations")
+ allocate))
(and complete (format " complete %s\n" complete))
(and effort (format " effort %s\n" effort))
(and priority (format " priority %s\n" priority))
-
+ (and milestone (format " milestone\n"))
+
(org-taskjuggler-get-attributes task attributes)
"\n"))))
(defun org-taskjuggler-close-maybe (level)
- (while (> org-export-taskjuggler-old-level level)
+ (while (> org-export-taskjuggler-old-level level)
(insert "}\n")
(setq org-export-taskjuggler-old-level (1- org-export-taskjuggler-old-level)))
(when (= org-export-taskjuggler-old-level level)
(provide 'org-taskjuggler)
+;; Local variables:
+;; generated-autoload-file: "org-loaddefs.el"
+;; End:
+
;;; org-taskjuggler.el ends here