;;
;; 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.
;; * 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
;; 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:
;; 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.
;;
hideresource 1
loadunit shortauto
}"
-"resourcereport \"Resource Graph\" {
+ "resourcereport \"Resource Graph\" {
headline \"Resource Allocation Graph\"
columns no, name, utilization, freeload, chart
loadunit shortauto
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
+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
"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)
(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"))))
(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)
(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
+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)
(and depends (org-taskjuggler-tokenize-dependencies depends))
(and blocker (org-taskjuggler-tokenize-dependencies blocker)))
tasks))
- previous-sibling)
- ; update previous sibling info
+ previous-sibling)
+ ; update previous sibling info
(cond
((< previous-level level)
(dotimes (tmp (- level previous-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
+ ; store dependency information
(when dependencies
(push (cons "depends" (mapconcat 'identity dependencies ", ")) task))
(setq previous-level level)
"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_]+'."
+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)
"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 ."
(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)))))
(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))
(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)))
(and filtered-items (mapconcat 'identity filtered-items "\n"))))
(defun org-taskjuggler-get-attributes (item attributes)
- "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
+ "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
(mapcar
(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
(defun org-taskjuggler-clean-effort (effort)
"Translate effort strings into a format acceptable to taskjuggler,
-i.e. REAL UNIT. A valid effort string can be anything that is
+i.e. REAL UNIT. A valid effort string can be anything that is
accepted by `org-duration-string-to-minutesĀ“."
(cond
((null effort) effort)