From 21b1713c6431210db24a045ae8d3a4ee01f36492 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 16 Mar 2010 10:11:52 +0100 Subject: [PATCH 01/54] Added an exporter to produce taskjuggler files --- lisp/org-taskjuggler.el | 236 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 lisp/org-taskjuggler.el diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el new file mode 100644 index 000000000..b7b9c5bcf --- /dev/null +++ b/lisp/org-taskjuggler.el @@ -0,0 +1,236 @@ +;;; org-taskjuggler.el --- TaskJuggler exporter for org-mode +;; +;; Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +;; +;; Emacs Lisp Archive Entry +;; Filename: org-taskjuggler.el +;; Version: 6.34trans +;; Author: Christian Egli +;; Maintainer: Christian Egli +;; Keywords: org, taskjuggler, project planning +;; Description: Converts an org-mode buffer into a taskjuggler project plan +;; URL: + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;; Commentary: +;; +;; This library implements a TaskJuggler exporter for org-mode. +;; +;; The interactive functions are similar to those of the HTML and LaTeX +;; exporters: +;; +;; M-x `org-export-as-taskjuggler' +;; M-x `org-export-as-taskjuggler-and-open' +;; M-x `org-export-as-taskjuggler-batch' +;; M-x `org-export-as-taskjuggler-to-buffer' +;; M-x `org-export-region-as-taskjuggler' +;; M-x `org-replace-region-by-taskjuggler' +;; +;;; Code: + +(eval-when-compile + (require 'cl)) + +(require 'org) +(require 'org-exp) + +;;; Variables: + +(declare-function org-id-find-id-file "org-id" (id)) + +;;; User variables: + +(defgroup org-export-taskjuggler nil + "Options for exporting Org-mode files to TaskJuggler." + :tag "Org Export TaskJuggler" + :group 'org-export) + +(defcustom org-export-taskjuggler-extension ".tjp" + "Extension of TaskJuggler files." + :group 'org-export-taskjuggler + :type 'string) + +(defcustom org-export-taskjuggler-project-tag "project" + "." + :group 'org-export-taskjuggler + :type 'string) + +(defcustom org-export-taskjuggler-resource-tag "resource" + "." + :group 'org-export-taskjuggler + :type 'string) + +(defcustom org-export-taskjuggler-default-project-version "1.0" + "." + :group 'org-export-taskjuggler + :type 'string) + +(defcustom org-export-taskjuggler-default-project-duration 365 + "." + :group 'org-export-taskjuggler + :type 'integer) + +;;; Hooks + +(defvar org-export-taskjuggler-final-hook nil + "Hook run at the end of TaskJuggler export, in the new buffer.") + +;;; Autoload functions: + +;;;###autoload +(defun org-export-as-taskjuggler () + "Export the current buffer as a TaskJuggler file." + (interactive) + + (message "Exporting...") + (let* ((tasks + (org-map-entries '(org-taskjuggler-components) + org-export-taskjuggler-project-tag nil 'archive 'comment)) + (resources + (org-map-entries '(org-taskjuggler-components) + org-export-taskjuggler-resource-tag nil 'archive 'comment)) + (filename (expand-file-name + (concat + (file-name-sans-extension + (file-name-nondirectory buffer-file-name)) + org-export-taskjuggler-extension))) + (buffer (find-file-noselect filename)) + (old-level 0) + (current-id 0) + task resource) + ;; add a default resource + (unless resources + (setq resources + `((("ID" . ,(user-login-name)) + ("headline" . ,user-full-name) + ("level" . 1))))) + ;; add a default allocation if none was given + (unless (assoc "allocate" (car tasks)) + (let ((task (car tasks))) + (setcar tasks (push (cons "allocate" (user-login-name)) task)))) + ;; add a default start date to the first task if none was given + (unless (assoc "start" (car tasks)) + (let ((task (car tasks)) + (time-string (format-time-string "%Y-%m-%d"))) + (setcar tasks (push (cons "start" time-string) task)))) + ;; add a default end date to the first task if none was given + (unless (assoc "end" (car tasks)) + (let* ((task (car tasks)) + (now (current-time)) + (duration + (days-to-time org-export-taskjuggler-default-project-duration)) + (time-string + (format-time-string "%Y-%m-%d" (time-add now duration)))) + (setcar tasks (push (cons "end" time-string) task)))) + ;; add a default version if none was given + (unless (assoc "version" (car tasks)) + (let ((task (car tasks)) + (version org-export-taskjuggler-default-project-version)) + (setcar tasks (push (cons "version" version) task)))) + (with-current-buffer buffer + (erase-buffer) + (org-taskjuggler-open-project (car tasks)) + (dolist (resource resources nil) + (let ((level (cdr (assoc "level" resource)))) + (org-taskjuggler-close-maybe level) + (org-taskjuggler-open-resource resource) + (setq old-level level))) + (org-taskjuggler-close-maybe 1) + (setq old-level 0) + (dolist (task tasks nil) + (let ((level (cdr (assoc "level" task)))) + (org-taskjuggler-close-maybe level) + (org-taskjuggler-open-task task) + (setq old-level level))) + (org-taskjuggler-close-maybe 1)))) + +(defun org-taskjuggler-components () + "" + (let* ((props (org-entry-properties)) + (components (org-heading-components)) + (level (car components)) + (headline (nth 4 components))) + (push (cons "level" level) props) + (push (cons "headline" headline) props))) + +(defun org-taskjuggler-clean-id (id) + (and id (replace-regexp-in-string "[^a-zA-Z0-9_]" "_" id))) + +(defun org-taskjuggler-open-project (project) + (let ((id (org-taskjuggler-clean-id (cdr (assoc "ID" project)))) + (headline (cdr (assoc "headline" project))) + (version (cdr (assoc "version" project))) + (start (cdr (assoc "start" project))) + (end (cdr (assoc "end" project)))) + (insert + (concat + "project " + (or id "FIXME") + " \"" headline "\" \"" version "\" " start " - " end " {\n " "}\n")))) + +(defun org-taskjuggler-open-resource (resource) + (let ((id (org-taskjuggler-clean-id (cdr (assoc "ID" resource)))) + (headline (cdr (assoc "headline" resource)))) + (insert + (concat "resource " id " \"" headline "\" {\n ")))) + +(defun org-taskjuggler-clean-effort (effort) + (cond + ((null effort) effort) + ((string-match "\\([0-9]+\\):\\([0-9]+\\)" effort) + (concat (match-string 1 effort) "." (match-string 2 effort) "h")) + ((string-match "\\([0-9]+\\).\\([0-9]+\\)" effort) (concat effort "d")) + (t (error "Not a valid effort (%s)" effort)))) + +(defun org-taskjuggler-open-task (task) + (let ((id (org-taskjuggler-clean-id (cdr (assoc "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))) + (account (cdr (assoc "account" task))) + (start (cdr (assoc "start" task))) + (complete (cdr (assoc "complete" task))) + (note (cdr (assoc "note" task))) + (priority (cdr (assoc "priority" task)))) + (insert + (concat + "task " + (or id (concat "id" (number-to-string (setq current-id (1+ current-id))))) + " \"" headline "\" {" + (and effort (concat "\n effort " effort)) + (and depends (concat "\n depends " depends)) + (and allocate (concat "\n purge allocations\n allocate " allocate)) + (and account (concat "\n account " account)) + (and start (concat "\n start " start)) + (and complete (concat "\n complete " complete)) + (and note (concat "\n note " note)) + (and priority (concat "\n priority " priority)) + "\n")))) + +(defun org-taskjuggler-close-maybe (level) + (while (> old-level level) + (insert "}\n") + (setq old-level (1- old-level))) + (when (= old-level level) + (insert "}\n"))) + + +(provide 'org-taskjuggler) + +;; arch-tag: a24a127c-d365-4c2a-9e9b-f7dcb0ebfdc3 +;;; org-taskjuggler.el ends here From 91246da18949e1c26988cf75141e4a435197beba Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 16 Mar 2010 10:27:37 +0100 Subject: [PATCH 02/54] Use the first resource for default allocations --- lisp/org-taskjuggler.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index b7b9c5bcf..4ec994c70 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -118,10 +118,11 @@ `((("ID" . ,(user-login-name)) ("headline" . ,user-full-name) ("level" . 1))))) - ;; add a default allocation if none was given + ;; add a default allocation to the first task if none was given (unless (assoc "allocate" (car tasks)) - (let ((task (car tasks))) - (setcar tasks (push (cons "allocate" (user-login-name)) task)))) + (let ((task (car tasks)) + (resource-id (cdr (assoc "ID" (car resources))))) + (setcar tasks (push (cons "allocate" resource-id) task)))) ;; add a default start date to the first task if none was given (unless (assoc "start" (car tasks)) (let ((task (car tasks)) @@ -159,7 +160,6 @@ (org-taskjuggler-close-maybe 1)))) (defun org-taskjuggler-components () - "" (let* ((props (org-entry-properties)) (components (org-heading-components)) (level (car components)) From 2b906dcfd765ac36340c1decd506544e1549c3db Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 16 Mar 2010 10:35:29 +0100 Subject: [PATCH 03/54] Use cl function to increment a variable --- lisp/org-taskjuggler.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 4ec994c70..d32067fba 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -199,7 +199,7 @@ (defun org-taskjuggler-open-task (task) (let ((id (org-taskjuggler-clean-id (cdr (assoc "ID" task)))) (headline (cdr (assoc "headline" task))) - (effort (org-taskjuggler-clean-effort(cdr (assoc org-effort-property task)))) + (effort (org-taskjuggler-clean-effort (cdr (assoc org-effort-property task)))) (depends (cdr (assoc "depends" task))) (allocate (cdr (assoc "allocate" task))) (account (cdr (assoc "account" task))) @@ -210,7 +210,7 @@ (insert (concat "task " - (or id (concat "id" (number-to-string (setq current-id (1+ current-id))))) + (or id (concat "id" (number-to-string (incf current-id)))) " \"" headline "\" {" (and effort (concat "\n effort " effort)) (and depends (concat "\n depends " depends)) From 0b0ada4beb6edf7aa22cf16074ee6095e2dedaa9 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 16 Mar 2010 14:14:24 +0100 Subject: [PATCH 04/54] Add support for inserting and defining reports --- lisp/org-taskjuggler.el | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index d32067fba..5249de4ae 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -79,11 +79,28 @@ :group 'org-export-taskjuggler :type 'string) -(defcustom org-export-taskjuggler-default-project-duration 365 +(defcustom org-export-taskjuggler-default-project-duration 180 "." :group 'org-export-taskjuggler :type 'integer) +(defcustom org-export-taskjuggler-default-reports + '("taskreport \"Gantt Chart\" { + headline \"Project Gantt Chart\" + columns hierarchindex, name, start, end, effort, duration, completed, chart + timeformat \"%a %Y-%m-%d\" + loadunit days +}" +"resourcereport \"Resource Graph\" { + headline \"Resource Allocation Graph\" + columns no, name, rate, utilization, freeload, chart + loadunit days + hidetask 1 +}") + "" + :group 'org-export-taskjuggler + :type '(repeat (string :tag "Report"))) + ;;; Hooks (defvar org-export-taskjuggler-final-hook nil @@ -145,19 +162,20 @@ (with-current-buffer buffer (erase-buffer) (org-taskjuggler-open-project (car tasks)) - (dolist (resource resources nil) + (dolist (resource resources) (let ((level (cdr (assoc "level" resource)))) (org-taskjuggler-close-maybe level) (org-taskjuggler-open-resource resource) (setq old-level level))) (org-taskjuggler-close-maybe 1) (setq old-level 0) - (dolist (task tasks nil) + (dolist (task tasks) (let ((level (cdr (assoc "level" task)))) (org-taskjuggler-close-maybe level) (org-taskjuggler-open-task task) (setq old-level level))) - (org-taskjuggler-close-maybe 1)))) + (org-taskjuggler-close-maybe 1) + (org-taskjuggler-insert-reports)))) (defun org-taskjuggler-components () (let* ((props (org-entry-properties)) @@ -229,6 +247,10 @@ (when (= old-level level) (insert "}\n"))) +(defun org-taskjuggler-insert-reports () + (let (report) + (dolist (report org-export-taskjuggler-default-reports) + (insert report "\n")))) (provide 'org-taskjuggler) From 01d0aab493978d858e8ccffa2cce4bfee342a1fe Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 16 Mar 2010 14:41:24 +0100 Subject: [PATCH 05/54] Add a command to open the project with TaskJugglerUI --- lisp/org-taskjuggler.el | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 5249de4ae..69b67df8d 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -175,7 +175,19 @@ (org-taskjuggler-open-task task) (setq old-level level))) (org-taskjuggler-close-maybe 1) - (org-taskjuggler-insert-reports)))) + (org-taskjuggler-insert-reports) + (save-buffer) + (or (org-export-push-to-kill-ring "TaskJuggler") + (message "Exporting... done")) + (current-buffer)))) + +;;;###autoload +(defun org-export-as-taskjuggler-and-open () + "Export the current buffer as a TaskJuggler file and open it with the TaskJuggler GUI." + (interactive) + (let ((file-name (buffer-file-name (org-export-as-taskjuggler))) + (command "TaskJugglerUI")) + (start-process-shell-command command nil command file-name))) (defun org-taskjuggler-components () (let* ((props (org-entry-properties)) From b8e9c4a11bd87ba145b5f160f081b2d8c5d059e1 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 17 Mar 2010 14:13:03 +0100 Subject: [PATCH 06/54] Major overhaul. ORDERED dependencies sort of work --- lisp/org-taskjuggler.el | 103 ++++++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 19 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 69b67df8d..b0b5d2122 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -35,10 +35,6 @@ ;; ;; M-x `org-export-as-taskjuggler' ;; M-x `org-export-as-taskjuggler-and-open' -;; M-x `org-export-as-taskjuggler-batch' -;; M-x `org-export-as-taskjuggler-to-buffer' -;; M-x `org-export-region-as-taskjuggler' -;; M-x `org-replace-region-by-taskjuggler' ;; ;;; Code: @@ -48,10 +44,6 @@ (require 'org) (require 'org-exp) -;;; Variables: - -(declare-function org-id-find-id-file "org-id" (id)) - ;;; User variables: (defgroup org-export-taskjuggler nil @@ -115,8 +107,10 @@ (message "Exporting...") (let* ((tasks - (org-map-entries '(org-taskjuggler-components) - org-export-taskjuggler-project-tag nil 'archive 'comment)) + (org-taskjuggler-resolve-dependencies + (org-taskjuggler-assign-ids + (org-map-entries '(org-taskjuggler-components) + org-export-taskjuggler-project-tag nil 'archive 'comment)))) (resources (org-map-entries '(org-taskjuggler-components) org-export-taskjuggler-resource-tag nil 'archive 'comment)) @@ -127,7 +121,6 @@ org-export-taskjuggler-extension))) (buffer (find-file-noselect filename)) (old-level 0) - (current-id 0) task resource) ;; add a default resource (unless resources @@ -189,14 +182,84 @@ (command "TaskJugglerUI")) (start-process-shell-command command nil command file-name))) +(defun org-taskjuggler-parent-is-ordered-p () + (save-excursion + (and (org-up-heading-safe) (org-entry-get (point) "ORDERED")))) + (defun org-taskjuggler-components () (let* ((props (org-entry-properties)) (components (org-heading-components)) (level (car components)) - (headline (nth 4 components))) + (headline (nth 4 components)) + (parent-ordered (org-taskjuggler-parent-is-ordered-p))) (push (cons "level" level) props) - (push (cons "headline" headline) props))) + (push (cons "headline" headline) props) + (push (cons "parent-ordered" parent-ordered) props))) +(defun org-taskjuggler-assign-ids (tasks) + (let ((previous-level 0) + unique-ids + path + task resolved-tasks tmp) + (dolist (task tasks resolved-tasks) + (let ((level (cdr (assoc "level" task))) + (unique-id (org-taskjuggler-get-unique-id task (car unique-ids)))) + (cond + ((< previous-level level) + (dotimes (tmp (- level previous-level)) + (push (list unique-id) unique-ids) + (push unique-id path))) + ((= previous-level level) + (push unique-id (car unique-ids))) + ((> previous-level level) + (dotimes (tmp (- previous-level level)) + (pop unique-ids) + (pop path)))) + (push (cons "unique-id" unique-id) task) + (push (cons "path" (mapconcat 'identity (reverse path) ".")) task) + (setq previous-level level) + (setq resolved-tasks (append resolved-tasks (list task))))))) + +(defun org-taskjuggler-resolve-dependencies (tasks) + (let ((previous-level 0) + siblings + task resolved-tasks) + (dolist (task tasks resolved-tasks) + (let ((level (cdr (assoc "level" task))) + (depends (cdr (assoc "depends" task))) + (parent-ordered (cdr (assoc "parent-ordered" task))) + previous-sibling) + (cond + ((< 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) + (dotimes (tmp (- previous-level level)) + (pop siblings)) + (setq previous-sibling (car siblings)) + (setcar siblings task))) + (when (and previous-sibling parent-ordered) + (push + (cons "depends" + (format "!%s" (cdr (assoc "unique-id" previous-sibling)))) task)) + (setq previous-level level) + (setq resolved-tasks (append resolved-tasks (list task))))))) + +(defun org-taskjuggler-get-unique-id (task unique-ids) + (let* ((headline (cdr (assoc "headline" task))) + (parts (split-string headline)) + (id (downcase (pop parts)))) + ; try to add more parts of the headline to make it unique + (while (member id unique-ids) + (setq id (concat id "_" (downcase (pop parts))))) + ; if its still not unique add "_" + (while (member id unique-ids) + (setq id (concat id "_"))) + (org-taskjuggler-clean-id id))) + (defun org-taskjuggler-clean-id (id) (and id (replace-regexp-in-string "[^a-zA-Z0-9_]" "_" id))) @@ -227,7 +290,7 @@ (t (error "Not a valid effort (%s)" effort)))) (defun org-taskjuggler-open-task (task) - (let ((id (org-taskjuggler-clean-id (cdr (assoc "ID" 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))) @@ -236,14 +299,16 @@ (start (cdr (assoc "start" task))) (complete (cdr (assoc "complete" task))) (note (cdr (assoc "note" task))) - (priority (cdr (assoc "priority" task)))) + (priority (cdr (assoc "priority" task))) + (parent-ordered (cdr (assoc "parent-ordered" task))) + (previous-sibling (cdr (assoc "previous-sibling" task)))) (insert (concat - "task " - (or id (concat "id" (number-to-string (incf current-id)))) - " \"" headline "\" {" + "task " unique-id " \"" headline "\" {" (and effort (concat "\n effort " effort)) - (and depends (concat "\n depends " depends)) + (if (and parent-ordered previous-sibling) + (concat "\n depends " previous-sibling) + (and depends (concat "\n depends " depends))) (and allocate (concat "\n purge allocations\n allocate " allocate)) (and account (concat "\n account " account)) (and start (concat "\n start " start)) From e0e433f1d7752a072a029391d42bd62cb0d8aabe Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 17 Mar 2010 15:02:01 +0100 Subject: [PATCH 07/54] Make sure the unique id resolving works --- lisp/org-taskjuggler.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index b0b5d2122..c32f3a186 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -202,19 +202,21 @@ path task resolved-tasks tmp) (dolist (task tasks resolved-tasks) - (let ((level (cdr (assoc "level" task))) - (unique-id (org-taskjuggler-get-unique-id task (car unique-ids)))) + (let ((level (cdr (assoc "level" task)))) (cond ((< 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) + (setq unique-id (org-taskjuggler-get-unique-id task (car unique-ids))) (push unique-id (car unique-ids))) ((> previous-level level) (dotimes (tmp (- previous-level level)) (pop unique-ids) - (pop path)))) + (pop path)) + (setq unique-id (org-taskjuggler-get-unique-id task (car unique-ids))))) (push (cons "unique-id" unique-id) task) (push (cons "path" (mapconcat 'identity (reverse path) ".")) task) (setq previous-level level) From d0556719b9bfb1330325f28717bc44a73f74c539 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 17 Mar 2010 15:05:10 +0100 Subject: [PATCH 08/54] Fix the id generation of a project --- lisp/org-taskjuggler.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index c32f3a186..42b7065d5 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -266,15 +266,14 @@ (and id (replace-regexp-in-string "[^a-zA-Z0-9_]" "_" id))) (defun org-taskjuggler-open-project (project) - (let ((id (org-taskjuggler-clean-id (cdr (assoc "ID" project)))) + (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 (concat - "project " - (or id "FIXME") + "project " unique-id " \"" headline "\" \"" version "\" " start " - " end " {\n " "}\n")))) (defun org-taskjuggler-open-resource (resource) From b3a244baa807b239e8d207d9c819fa672614e96f Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 17 Mar 2010 15:44:54 +0100 Subject: [PATCH 09/54] Assign unique ids to resources --- lisp/org-taskjuggler.el | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 42b7065d5..7e9fa45a4 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -108,12 +108,13 @@ (message "Exporting...") (let* ((tasks (org-taskjuggler-resolve-dependencies - (org-taskjuggler-assign-ids + (org-taskjuggler-assign-task-ids (org-map-entries '(org-taskjuggler-components) org-export-taskjuggler-project-tag nil 'archive 'comment)))) (resources - (org-map-entries '(org-taskjuggler-components) - org-export-taskjuggler-resource-tag nil 'archive 'comment)) + (org-taskjuggler-assign-resource-ids + (org-map-entries '(org-taskjuggler-components) + org-export-taskjuggler-resource-tag nil 'archive 'comment))) (filename (expand-file-name (concat (file-name-sans-extension @@ -196,7 +197,7 @@ (push (cons "headline" headline) props) (push (cons "parent-ordered" parent-ordered) props))) -(defun org-taskjuggler-assign-ids (tasks) +(defun org-taskjuggler-assign-task-ids (tasks) (let ((previous-level 0) unique-ids path @@ -222,6 +223,16 @@ (setq previous-level level) (setq resolved-tasks (append resolved-tasks (list task))))))) +(defun org-taskjuggler-assign-resource-ids (resources) + (let (unique-ids + unique-id + resource resolved-resources) + (dolist (resource resources resolved-resources) + (setq unique-id (org-taskjuggler-get-unique-id resource unique-ids)) + (push unique-id unique-ids) + (push (cons "unique-id" unique-id) resource) + (setq resolved-resources (append resolved-resources (list resource)))))) + (defun org-taskjuggler-resolve-dependencies (tasks) (let ((previous-level 0) siblings @@ -278,9 +289,10 @@ (defun org-taskjuggler-open-resource (resource) (let ((id (org-taskjuggler-clean-id (cdr (assoc "ID" resource)))) + (unique-id (org-taskjuggler-clean-id (cdr (assoc "unique-id" resource)))) (headline (cdr (assoc "headline" resource)))) (insert - (concat "resource " id " \"" headline "\" {\n ")))) + (concat "resource " (or id unique-id) " \"" headline "\" {\n ")))) (defun org-taskjuggler-clean-effort (effort) (cond From cae01fcbed5f5cd243cc4bd613cd9fa497628af2 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 17 Mar 2010 16:01:23 +0100 Subject: [PATCH 10/54] Map more resource attributes --- lisp/org-taskjuggler.el | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 7e9fa45a4..dfc6e9f14 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -285,14 +285,18 @@ (insert (concat "project " unique-id - " \"" headline "\" \"" version "\" " start " - " end " {\n " "}\n")))) + " \"" headline "\" \"" version "\" " start " - " end " {\n }\n")))) (defun org-taskjuggler-open-resource (resource) (let ((id (org-taskjuggler-clean-id (cdr (assoc "ID" resource)))) (unique-id (org-taskjuggler-clean-id (cdr (assoc "unique-id" resource)))) - (headline (cdr (assoc "headline" resource)))) + (headline (cdr (assoc "headline" resource))) + (limits (cdr (assoc "limits" resource))) + (vacation (cdr (assoc "vacation" resource)))) (insert - (concat "resource " (or id unique-id) " \"" headline "\" {\n ")))) + (concat "resource " (or id unique-id) " \"" headline "\" {\n " + (and limits (concat "\n limits { " limits " }\n")) + (and vacation (concat "\n vacation " vacation "\n")))))) (defun org-taskjuggler-clean-effort (effort) (cond From d437e01372f1cf700d8b8d7a64627d557aa72d7e Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 17 Mar 2010 16:36:36 +0100 Subject: [PATCH 11/54] Add TODO items and fix a problem with unique id assignment --- lisp/org-taskjuggler.el | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index dfc6e9f14..4f1d109dc 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -36,6 +36,13 @@ ;; M-x `org-export-as-taskjuggler' ;; M-x `org-export-as-taskjuggler-and-open' ;; +;;; TODO: +;; * derive completeness info from TODO state +;; * Handle explicit dependencies such as BLOCKER and depends attribute +;; * Code cleanup +;; * Add documentation +;; * Try using plists instead of alists +;; ;;; Code: (eval-when-compile @@ -217,7 +224,8 @@ (dotimes (tmp (- previous-level level)) (pop unique-ids) (pop path)) - (setq unique-id (org-taskjuggler-get-unique-id task (car unique-ids))))) + (setq unique-id (org-taskjuggler-get-unique-id task (car unique-ids))) + (push unique-id (car unique-ids)))) (push (cons "unique-id" unique-id) task) (push (cons "path" (mapconcat 'identity (reverse path) ".")) task) (setq previous-level level) @@ -348,5 +356,4 @@ (provide 'org-taskjuggler) -;; arch-tag: a24a127c-d365-4c2a-9e9b-f7dcb0ebfdc3 ;;; org-taskjuggler.el ends here From fd22a6f4e019e147e4b51205f0c5614ab3c7db6b Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 17 Mar 2010 17:05:28 +0100 Subject: [PATCH 12/54] Derive completeness info from TODO state --- lisp/org-taskjuggler.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 4f1d109dc..bfb32510d 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -37,7 +37,6 @@ ;; M-x `org-export-as-taskjuggler-and-open' ;; ;;; TODO: -;; * derive completeness info from TODO state ;; * Handle explicit dependencies such as BLOCKER and depends attribute ;; * Code cleanup ;; * Add documentation @@ -113,6 +112,7 @@ (interactive) (message "Exporting...") + (setq-default org-done-keywords org-done-keywords) (let* ((tasks (org-taskjuggler-resolve-dependencies (org-taskjuggler-assign-task-ids @@ -315,14 +315,16 @@ (t (error "Not a valid effort (%s)" effort)))) (defun org-taskjuggler-open-task (task) - (let ((unique-id (cdr (assoc "unique-id" 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))) (account (cdr (assoc "account" task))) (start (cdr (assoc "start" task))) - (complete (cdr (assoc "complete" task))) + (state (cdr (assoc "TODO" task))) + (complete (or (and (member state org-done-keywords) "100") + (cdr (assoc "complete" task)))) (note (cdr (assoc "note" task))) (priority (cdr (assoc "priority" task))) (parent-ordered (cdr (assoc "parent-ordered" task))) From fcf43d1e94b184a503d94126e6d7b7b3831b917d Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Thu, 18 Mar 2010 10:07:38 +0100 Subject: [PATCH 13/54] Add some documentation --- lisp/org-taskjuggler.el | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index bfb32510d..926cf353d 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -63,22 +63,26 @@ :type 'string) (defcustom org-export-taskjuggler-project-tag "project" - "." + "Tag, property or todo used to find the tree containing all +the tasks for the project." :group 'org-export-taskjuggler :type 'string) (defcustom org-export-taskjuggler-resource-tag "resource" - "." + "Tag, property or todo used to find the tree containing all the +resources for the project." :group 'org-export-taskjuggler :type 'string) (defcustom org-export-taskjuggler-default-project-version "1.0" - "." + "Default version string for the project." :group 'org-export-taskjuggler :type 'string) (defcustom org-export-taskjuggler-default-project-duration 180 - "." + "Default project duration if no start and end date have been defined +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 :type 'integer) @@ -95,7 +99,7 @@ loadunit days hidetask 1 }") - "" + "Default reports for the project." :group 'org-export-taskjuggler :type '(repeat (string :tag "Report"))) @@ -108,7 +112,17 @@ ;;;###autoload (defun org-export-as-taskjuggler () - "Export the current buffer as a TaskJuggler file." + "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 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 +the taskjuggler project will be created with default reports as +defined in `org-export-taskjuggler-default-reports'." (interactive) (message "Exporting...") @@ -307,9 +321,16 @@ (and vacation (concat "\n vacation " vacation "\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:00 it +will be assumed to be hours and will be translated into 5.0h. +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 ((null effort) effort) ((string-match "\\([0-9]+\\):\\([0-9]+\\)" effort) + ; FIXME: translate the minutes to a decimal (concat (match-string 1 effort) "." (match-string 2 effort) "h")) ((string-match "\\([0-9]+\\).\\([0-9]+\\)" effort) (concat effort "d")) (t (error "Not a valid effort (%s)" effort)))) From 8d42317b4ca1650bb8b549a53d913743f8b83263 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Thu, 18 Mar 2010 10:22:49 +0100 Subject: [PATCH 14/54] Add more documentation --- lisp/org-taskjuggler.el | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 926cf353d..36eda0ef8 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -205,10 +205,15 @@ defined in `org-export-taskjuggler-default-reports'." (start-process-shell-command command nil command file-name))) (defun org-taskjuggler-parent-is-ordered-p () + "Return true if the parent of the current node has a property +\"ORDERED\". Return nil otherwise." (save-excursion (and (org-up-heading-safe) (org-entry-get (point) "ORDERED")))) (defun org-taskjuggler-components () + "Return an alist containing all the pertinent information for +the current node such as the headline, the level, todo state +information, all the properties, etc." (let* ((props (org-entry-properties)) (components (org-heading-components)) (level (car components)) @@ -219,6 +224,10 @@ defined in `org-export-taskjuggler-default-reports'." (push (cons "parent-ordered" parent-ordered) 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. +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) unique-ids path @@ -246,6 +255,8 @@ defined in `org-export-taskjuggler-default-reports'." (setq resolved-tasks (append resolved-tasks (list task))))))) (defun org-taskjuggler-assign-resource-ids (resources) + "Given a list of resources return the same list, assigning a +unique id to each resource." (let (unique-ids unique-id resource resolved-resources) @@ -283,8 +294,12 @@ defined in `org-export-taskjuggler-default-reports'." (setq previous-level level) (setq resolved-tasks (append resolved-tasks (list task))))))) -(defun org-taskjuggler-get-unique-id (task unique-ids) - (let* ((headline (cdr (assoc "headline" task))) +(defun org-taskjuggler-get-unique-id (item unique-ids) + "Return a unique id for an ITEM which can be a task or a resource. +The id is derived from the headline and made unique against +UNIQUE-IDS. If the first part of the headline is not unique try to add +more parts of the headline or finally add more underscore characters (\"_\")." + (let* ((headline (cdr (assoc "headline" item))) (parts (split-string headline)) (id (downcase (pop parts)))) ; try to add more parts of the headline to make it unique @@ -296,6 +311,7 @@ defined in `org-export-taskjuggler-default-reports'." (org-taskjuggler-clean-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))) (defun org-taskjuggler-open-project (project) From 5841a1f4690a02c3271105507c82ea18c4d85fb7 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Thu, 18 Mar 2010 22:22:30 +0100 Subject: [PATCH 15/54] Add support for explicit dependencies --- lisp/org-taskjuggler.el | 51 +++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 36eda0ef8..afff2df55 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -242,7 +242,8 @@ a path to the current task." (push unique-id path))) ((= previous-level level) (setq unique-id (org-taskjuggler-get-unique-id task (car unique-ids))) - (push unique-id (car unique-ids))) + (push unique-id (car unique-ids)) + (setcar path unique-id)) ((> previous-level level) (dotimes (tmp (- previous-level level)) (pop unique-ids) @@ -271,10 +272,18 @@ unique id to each resource." siblings task resolved-tasks) (dolist (task tasks resolved-tasks) - (let ((level (cdr (assoc "level" task))) - (depends (cdr (assoc "depends" task))) - (parent-ordered (cdr (assoc "parent-ordered" task))) - previous-sibling) + (let* ((level (cdr (assoc "level" task))) + (depends (cdr (assoc "depends" task))) + (parent-ordered (cdr (assoc "parent-ordered" task))) + (blocker (cdr (assoc "BLOCKER" task))) + (blocked-on-previous (and blocker (string-match "previous-sibling" blocker))) + (dependencies + (org-taskjuggler-resolve-explicit-dependencies + (append + (and depends (split-string depends "[, \f\t\n\r\v]+" t)) + (and blocker (split-string blocker "[, \f\t\n\r\v]+" t))) tasks)) + previous-sibling) + ; update previous sibling info (cond ((< previous-level level) (dotimes (tmp (- level previous-level)) @@ -287,13 +296,37 @@ unique id to each resource." (pop siblings)) (setq previous-sibling (car siblings)) (setcar siblings task))) - (when (and previous-sibling parent-ordered) - (push - (cons "depends" - (format "!%s" (cdr (assoc "unique-id" previous-sibling)))) task)) + ; 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 + (push (cons "depends" (mapconcat 'identity dependencies ", ")) task)) (setq previous-level level) (setq resolved-tasks (append resolved-tasks (list task))))))) +(defun org-taskjuggler-resolve-explicit-dependencies (dependencies tasks) + (let (path) + (cond + ((null dependencies) nil) + ; 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 + ((setq path (org-taskjuggler-find-task-with-id (car dependencies) tasks)) + (cons path (org-taskjuggler-resolve-explicit-dependencies (cdr dependencies) tasks))) + ; silently ignore all other dependencies + (t (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 return nil." + (cond + ((null tasks) nil) + ((equal (cdr (assoc "ID" (car tasks))) id) + (cdr (assoc "path" (car tasks)))) + (t (org-taskjuggler-find-task-with-id id (cdr tasks))))) + (defun org-taskjuggler-get-unique-id (item unique-ids) "Return a unique id for an ITEM which can be a task or a resource. The id is derived from the headline and made unique against From d78315790726d826a7dc4ad12a20bd56b81de014 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Fri, 19 Mar 2010 14:46:37 +0100 Subject: [PATCH 16/54] Slightly change the default reports --- lisp/org-taskjuggler.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index afff2df55..bd2d63062 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -90,12 +90,13 @@ with `org-export-taskjuggler-project-tag'" '("taskreport \"Gantt Chart\" { headline \"Project Gantt Chart\" columns hierarchindex, name, start, end, effort, duration, completed, chart - timeformat \"%a %Y-%m-%d\" + timeformat \"%Y-%m-%d\" + hideresource 1 loadunit days }" "resourcereport \"Resource Graph\" { headline \"Resource Allocation Graph\" - columns no, name, rate, utilization, freeload, chart + columns no, name, utilization, freeload, chart loadunit days hidetask 1 }") From d01a04a269c39d903fb5fc77e5348530da0e9e03 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Fri, 19 Mar 2010 17:11:10 +0100 Subject: [PATCH 17/54] Update TODOs to reflect the fact that dependencies are done --- lisp/org-taskjuggler.el | 1 - 1 file changed, 1 deletion(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index bd2d63062..15eacbead 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -37,7 +37,6 @@ ;; M-x `org-export-as-taskjuggler-and-open' ;; ;;; TODO: -;; * Handle explicit dependencies such as BLOCKER and depends attribute ;; * Code cleanup ;; * Add documentation ;; * Try using plists instead of alists From 4e988afcf18542af417b7d4f13beef9db5af2371 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Fri, 19 Mar 2010 17:12:22 +0100 Subject: [PATCH 18/54] Improve the default resource allocation graph --- lisp/org-taskjuggler.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 15eacbead..d60a91cbf 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -97,7 +97,8 @@ with `org-export-taskjuggler-project-tag'" headline \"Resource Allocation Graph\" columns no, name, utilization, freeload, chart loadunit days - hidetask 1 + sorttasks startup + hidetask ~isleaf() }") "Default reports for the project." :group 'org-export-taskjuggler From aa1a0daac2d3d2e56d9443bd0b6526c1bd7611d1 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Fri, 19 Mar 2010 17:13:17 +0100 Subject: [PATCH 19/54] Fix a problem with path calculation when the level is lowering --- lisp/org-taskjuggler.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index d60a91cbf..d98ec0c72 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -250,7 +250,8 @@ a path to the current task." (pop unique-ids) (pop path)) (setq unique-id (org-taskjuggler-get-unique-id task (car unique-ids))) - (push unique-id (car unique-ids)))) + (push unique-id (car unique-ids)) + (setcar path unique-id))) (push (cons "unique-id" unique-id) task) (push (cons "path" (mapconcat 'identity (reverse path) ".")) task) (setq previous-level level) From 3a30bc763be9aef6cc349d573371d68517dc92be Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Fri, 19 Mar 2010 17:35:33 +0100 Subject: [PATCH 20/54] Some small correction from the imperative to a more functional style after some reading of sicp --- lisp/org-taskjuggler.el | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index d98ec0c72..f59b457a6 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -257,17 +257,17 @@ a path to the current task." (setq previous-level level) (setq resolved-tasks (append resolved-tasks (list task))))))) -(defun org-taskjuggler-assign-resource-ids (resources) +(defun org-taskjuggler-assign-resource-ids (resources &optional unique-ids) "Given a list of resources return the same list, assigning a unique id to each resource." - (let (unique-ids - unique-id - resource resolved-resources) - (dolist (resource resources resolved-resources) - (setq unique-id (org-taskjuggler-get-unique-id resource unique-ids)) - (push unique-id unique-ids) + (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) - (setq resolved-resources (append resolved-resources (list resource)))))) + (cons resource (org-taskjuggler-assign-resource-ids (cdr resources) + (cons unique-id unique-ids))))))) (defun org-taskjuggler-resolve-dependencies (tasks) (let ((previous-level 0) @@ -323,11 +323,12 @@ unique id to each resource." (defun org-taskjuggler-find-task-with-id (id tasks) "Find ID in tasks. If found return the path of task. Otherwise return nil." - (cond - ((null tasks) nil) - ((equal (cdr (assoc "ID" (car tasks))) id) - (cdr (assoc "path" (car tasks)))) - (t (org-taskjuggler-find-task-with-id id (cdr tasks))))) + (let ((task-id (cdr (assoc "ID" (car tasks)))) + (path (cdr (assoc "path" (car tasks))))) + (cond + ((null tasks) nil) + ((equal task-id id) path) + (t (org-taskjuggler-find-task-with-id id (cdr tasks)))))) (defun org-taskjuggler-get-unique-id (item unique-ids) "Return a unique id for an ITEM which can be a task or a resource. From 84de8a85f6dea88747bd1341926d2a67ef584e70 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Mon, 22 Mar 2010 17:27:53 +0100 Subject: [PATCH 21/54] Add support for global properties --- lisp/org-taskjuggler.el | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index f59b457a6..42a16dd0d 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -104,6 +104,22 @@ with `org-export-taskjuggler-project-tag'" :group 'org-export-taskjuggler :type '(repeat (string :tag "Report"))) +(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 +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. + +The global properties are inserted after the project declaration +but before any resource and task declarations." + :group 'org-export-taskjuggler + :type '(string :tag "Preamble")) + ;;; Hooks (defvar org-export-taskjuggler-final-hook nil @@ -178,6 +194,7 @@ defined in `org-export-taskjuggler-default-reports'." (with-current-buffer buffer (erase-buffer) (org-taskjuggler-open-project (car tasks)) + (insert org-export-taskjuggler-default-global-properties) (dolist (resource resources) (let ((level (cdr (assoc "level" resource)))) (org-taskjuggler-close-maybe level) @@ -230,7 +247,7 @@ 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) - unique-ids + unique-ids unique-id path task resolved-tasks tmp) (dolist (task tasks resolved-tasks) From 020bb514a45edc7008613a51efcaafa2e9d4eb20 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 23 Mar 2010 09:46:46 +0100 Subject: [PATCH 22/54] Integrate org-taskjuggler in the rest of org-mode --- Makefile | 2 ++ lisp/org.el | 1 + 2 files changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 604aeda08..07fe4bbf4 100644 --- a/Makefile +++ b/Makefile @@ -107,6 +107,7 @@ LISPF = org.el \ org-rmail.el \ org-src.el \ org-table.el \ + org-taskjuggler.el \ org-timer.el \ org-vm.el \ org-w3m.el \ @@ -388,6 +389,7 @@ lisp/org-remember.elc: lisp/org.el lisp/org-rmail.elc: lisp/org.el lisp/org-src.elc: lisp/org-macs.el lisp/org-compat.el lisp/org-table.elc: lisp/org.el +lisp/org-taskjuggler.elc: lisp/org.el lisp/org-timer.elc: lisp/org.el lisp/org-vm.elc: lisp/org.el lisp/org-w3m.elc: lisp/org.el diff --git a/lisp/org.el b/lisp/org.el index ebd3dc9f9..5cbd10a7e 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -259,6 +259,7 @@ to add the symbol `xyz', and the package must have a call to (const :tag "C sqlinsert: Convert Org-mode tables to SQL insertions" orgtbl-sqlinsert) (const :tag "C toc: Table of contents for Org-mode buffer" org-toc) (const :tag "C track: Keep up with Org-mode development" org-track) + (const :tag "C TaskJuggler: Export tasks to a TaskJuggler project" org-taskjuggler) (repeat :tag "External packages" :inline t (symbol :tag "Package")))) (defcustom org-support-shift-select nil From fd89c73c6a4d5fcf17c941cc1e5514c11ea7ca35 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 23 Mar 2010 11:09:10 +0100 Subject: [PATCH 23/54] Add support for more resource attributes --- lisp/org-taskjuggler.el | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 42a16dd0d..9c169603d 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -382,12 +382,22 @@ more parts of the headline or finally add more underscore characters (\"_\")." (let ((id (org-taskjuggler-clean-id (cdr (assoc "ID" resource)))) (unique-id (org-taskjuggler-clean-id (cdr (assoc "unique-id" resource)))) (headline (cdr (assoc "headline" resource))) - (limits (cdr (assoc "limits" resource))) - (vacation (cdr (assoc "vacation" resource)))) + (attributes '(limits vacation shift booking efficiency journalentry rate))) (insert - (concat "resource " (or id unique-id) " \"" headline "\" {\n " - (and limits (concat "\n limits { " limits " }\n")) - (and vacation (concat "\n vacation " vacation "\n")))))) + (concat + "resource " (or id unique-id) " \"" headline "\" {\n " + (mapconcat + 'identity + (remq + nil + (mapcar + (lambda (attribute) + (let ((value (cdr (assoc (symbol-name attribute) resource)))) + (and value + (if (equal attribute 'limits) + (format "%s { %s }" (symbol-name attribute) value) + (format "%s %s" (symbol-name attribute) value))))) + attributes)) "\n") "\n")))) (defun org-taskjuggler-clean-effort (effort) "Translate effort strings into a format acceptable to taskjuggler, From 5458afe24bdc458c84281a259c8e6a001b1a6f4b Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 23 Mar 2010 13:11:09 +0100 Subject: [PATCH 24/54] Fix an error with unique id calculation --- lisp/org-taskjuggler.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 9c169603d..571808607 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -354,14 +354,14 @@ UNIQUE-IDS. If the first part of the headline is not unique try to add more parts of the headline or finally add more underscore characters (\"_\")." (let* ((headline (cdr (assoc "headline" item))) (parts (split-string headline)) - (id (downcase (pop parts)))) + (id (org-taskjuggler-clean-id (downcase (pop parts))))) ; try to add more parts of the headline to make it unique (while (member id unique-ids) - (setq id (concat id "_" (downcase (pop parts))))) + (setq id (concat id "_" (org-taskjuggler-clean-id (downcase (pop parts)))))) ; if its still not unique add "_" (while (member id unique-ids) (setq id (concat id "_"))) - (org-taskjuggler-clean-id id))) + id)) (defun org-taskjuggler-clean-id (id) "Clean and return ID to make it acceptable for taskjuggler." From 96c3c0c194f1ff133fafa486a2cca226150221bd Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 23 Mar 2010 14:02:48 +0100 Subject: [PATCH 25/54] Fix a bug with project end calculation and refactor attribute insertion - Attributes are now insetred generically based on an item (resource or task) a list of attrbutes - project end is now solely calculated and no longer inserted in the node --- lisp/org-taskjuggler.el | 88 +++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 571808607..e1ed12b1e 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -177,15 +177,6 @@ defined in `org-export-taskjuggler-default-reports'." (let ((task (car tasks)) (time-string (format-time-string "%Y-%m-%d"))) (setcar tasks (push (cons "start" time-string) task)))) - ;; add a default end date to the first task if none was given - (unless (assoc "end" (car tasks)) - (let* ((task (car tasks)) - (now (current-time)) - (duration - (days-to-time org-export-taskjuggler-default-project-duration)) - (time-string - (format-time-string "%Y-%m-%d" (time-add now duration)))) - (setcar tasks (push (cons "end" time-string) task)))) ;; add a default version if none was given (unless (assoc "version" (car tasks)) (let ((task (car tasks)) @@ -368,15 +359,43 @@ more parts of the headline or finally add more underscore characters (\"_\")." (and id (replace-regexp-in-string "[^a-zA-Z0-9_]" "_" id))) (defun org-taskjuggler-open-project (project) - (let ((unique-id (cdr (assoc "unique-id" project))) + "Insert a the beginning of 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)))) + (end (cdr (assoc "end" project))) + (now (current-time)) + (duration + (days-to-time org-export-taskjuggler-default-project-duration)) + (time-string + (format-time-string "%Y-%m-%d" (time-add now duration)))) (insert (concat "project " unique-id - " \"" headline "\" \"" version "\" " start " - " end " {\n }\n")))) + " \"" headline "\" \"" version "\" " start " - " + (or end time-string)" {\n }\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 listed in +ATTRIBUTES." + (mapconcat + 'identity + (remq + nil + (mapcar + (lambda (attribute) + (let ((value (cdr (assoc (symbol-name attribute) item)))) + (and value + (if (equal attribute 'limits) + (format "%s { %s }" (symbol-name attribute) value) + (format "%s %s" (symbol-name attribute) value))))) + attributes)) "\n")) (defun org-taskjuggler-open-resource (resource) (let ((id (org-taskjuggler-clean-id (cdr (assoc "ID" resource)))) @@ -386,18 +405,7 @@ more parts of the headline or finally add more underscore characters (\"_\")." (insert (concat "resource " (or id unique-id) " \"" headline "\" {\n " - (mapconcat - 'identity - (remq - nil - (mapcar - (lambda (attribute) - (let ((value (cdr (assoc (symbol-name attribute) resource)))) - (and value - (if (equal attribute 'limits) - (format "%s { %s }" (symbol-name attribute) value) - (format "%s %s" (symbol-name attribute) value))))) - attributes)) "\n") "\n")))) + (org-taskjuggler-get-attributes resource attributes) "\n")))) (defun org-taskjuggler-clean-effort (effort) "Translate effort strings into a format acceptable to taskjuggler, @@ -420,28 +428,30 @@ supports (like weeks, months and years) are currently not supported." (effort (org-taskjuggler-clean-effort (cdr (assoc org-effort-property task)))) (depends (cdr (assoc "depends" task))) (allocate (cdr (assoc "allocate" task))) - (account (cdr (assoc "account" task))) - (start (cdr (assoc "start" task))) + (priority (and (cdr (assoc "PRIORITY" task)) + (org-get-priority (cdr (assoc "PRIORITY" task))))) (state (cdr (assoc "TODO" task))) (complete (or (and (member state org-done-keywords) "100") (cdr (assoc "complete" task)))) - (note (cdr (assoc "note" task))) - (priority (cdr (assoc "priority" task))) (parent-ordered (cdr (assoc "parent-ordered" task))) - (previous-sibling (cdr (assoc "previous-sibling" 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))) (insert (concat - "task " unique-id " \"" headline "\" {" - (and effort (concat "\n effort " effort)) + "task " unique-id " \"" headline "\" {\n" (if (and parent-ordered previous-sibling) - (concat "\n depends " previous-sibling) - (and depends (concat "\n depends " depends))) - (and allocate (concat "\n purge allocations\n allocate " allocate)) - (and account (concat "\n account " account)) - (and start (concat "\n start " start)) - (and complete (concat "\n complete " complete)) - (and note (concat "\n note " note)) - (and priority (concat "\n priority " priority)) + (format " depends %s\n" previous-sibling) + (and depends (format " depends %s\n" depends))) + (and allocate (format " purge allocations\n allocate %s\n" allocate)) + (and complete (format " complete %s\n" complete)) + (and effort (format " effort %s\n" effort)) + (and priority (format " priority %s\n" priority)) + + (org-taskjuggler-get-attributes task attributes) "\n")))) (defun org-taskjuggler-close-maybe (level) From 68cb44ccd47e66143bfe8372ee6799be5b6d9c59 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 23 Mar 2010 15:40:02 +0100 Subject: [PATCH 26/54] Simplify the calculation of the project end --- lisp/org-taskjuggler.el | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index e1ed12b1e..4884e4271 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -367,17 +367,11 @@ specified it is calculated (headline (cdr (assoc "headline" project))) (version (cdr (assoc "version" project))) (start (cdr (assoc "start" project))) - (end (cdr (assoc "end" project))) - (now (current-time)) - (duration - (days-to-time org-export-taskjuggler-default-project-duration)) - (time-string - (format-time-string "%Y-%m-%d" (time-add now duration)))) + (end (cdr (assoc "end" project)))) (insert - (concat - "project " unique-id - " \"" headline "\" \"" version "\" " start " - " - (or end time-string)" {\n }\n")))) + (format "project %s \"%s\" \"%s\" %s +%sd {\n }\n" + unique-id headline version start + org-export-taskjuggler-default-project-duration)))) (defun org-taskjuggler-get-attributes (item attributes) "Return all attribute as a single formated string. ITEM is an alist From 24745790a22ad31fc289664b3b61f7ff9199f8dd Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 23 Mar 2010 15:40:52 +0100 Subject: [PATCH 27/54] Convert org priority properly to taskjuggler priority --- lisp/org-taskjuggler.el | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 4884e4271..d4b2a770c 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -416,14 +416,20 @@ supports (like weeks, months and years) are currently not supported." ((string-match "\\([0-9]+\\).\\([0-9]+\\)" effort) (concat effort "d")) (t (error "Not a valid effort (%s)" effort)))) +(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))) + (- 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 (and (cdr (assoc "PRIORITY" task)) - (org-get-priority (cdr (assoc "PRIORITY" 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)))) From c1508e9310f9753b691505e5fa31c1318841bcba Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 23 Mar 2010 16:41:05 +0100 Subject: [PATCH 28/54] Add support for handling of multiple attributes values in the same node e.g. multiple shift definitions in a resource are now properly handled. --- lisp/org-taskjuggler.el | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index d4b2a770c..84ac06fae 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -186,6 +186,7 @@ defined in `org-export-taskjuggler-default-reports'." (erase-buffer) (org-taskjuggler-open-project (car tasks)) (insert org-export-taskjuggler-default-global-properties) + (insert "\n") (dolist (resource resources) (let ((level (cdr (assoc "level" resource)))) (org-taskjuggler-close-maybe level) @@ -373,23 +374,34 @@ specified it is calculated unique-id headline version start org-export-taskjuggler-default-project-duration)))) +(defun org-taskjuggler-filter-and-join (items) + (and (remq nil items) (mapconcat 'identity (remq nil 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 listed in ATTRIBUTES." - (mapconcat - 'identity - (remq - nil - (mapcar - (lambda (attribute) - (let ((value (cdr (assoc (symbol-name attribute) item)))) - (and value - (if (equal attribute 'limits) - (format "%s { %s }" (symbol-name attribute) value) - (format "%s %s" (symbol-name attribute) value))))) - attributes)) "\n")) + (org-taskjuggler-filter-and-join + (mapcar + (lambda (attribute) + (org-taskjuggler-filter-and-join + (org-taskjuggler-get-attribute item attribute))) + attributes))) + +(defun org-taskjuggler-get-attribute (item attribute) + "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 + ((null item) nil) + ((equal (symbol-name attribute) (car (car item))) + (cons (or + (and (equal attribute 'limits) + (format "%s { %s }" (symbol-name attribute) (cdr (car item)))) + (format "%s %s" (symbol-name attribute) (cdr (car item)))) + (org-taskjuggler-get-attribute (cdr item) attribute))) + (t (org-taskjuggler-get-attribute (cdr item) attribute)))) (defun org-taskjuggler-open-resource (resource) (let ((id (org-taskjuggler-clean-id (cdr (assoc "ID" resource)))) From 8e8bd469db22fd3b1a6907018e642dda2979cba5 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 24 Mar 2010 09:20:10 +0100 Subject: [PATCH 29/54] Use a let instead of calculating a value twice. --- lisp/org-taskjuggler.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 84ac06fae..cde1fec35 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -375,7 +375,10 @@ specified it is calculated org-export-taskjuggler-default-project-duration)))) (defun org-taskjuggler-filter-and-join (items) - (and (remq nil items) (mapconcat 'identity (remq nil items) "\n"))) + "Filter all nil elements from ITEMS and join the remaining ones +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 From 79b15d10739c9b8e8ec427530ddbe6ab70516b92 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 24 Mar 2010 09:35:46 +0100 Subject: [PATCH 30/54] changed some defcustom to contain more sensible values --- lisp/org-taskjuggler.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index cde1fec35..a7885d55e 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -39,7 +39,6 @@ ;;; TODO: ;; * Code cleanup ;; * Add documentation -;; * Try using plists instead of alists ;; ;;; Code: @@ -61,13 +60,13 @@ :group 'org-export-taskjuggler :type 'string) -(defcustom org-export-taskjuggler-project-tag "project" +(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 :type 'string) -(defcustom org-export-taskjuggler-resource-tag "resource" +(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 From 1990e408167fe3c6f61c2b2a3f8f352ffd852320 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 24 Mar 2010 11:59:43 +0100 Subject: [PATCH 31/54] Change the default project and resource tag to something that org mode accepts --- lisp/org-taskjuggler.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index a7885d55e..5e4cdc2ae 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -60,13 +60,13 @@ :group 'org-export-taskjuggler :type 'string) -(defcustom org-export-taskjuggler-project-tag "taskjuggler-project" +(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 :type 'string) -(defcustom org-export-taskjuggler-resource-tag "taskjuggler-resource" +(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 From 3e0ce7b355b4c1d05707d09eb1a12f496bf0308e Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 24 Mar 2010 12:01:54 +0100 Subject: [PATCH 32/54] Make sure the reports are more flexible by changing the loadunit to shortauto --- lisp/org-taskjuggler.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 5e4cdc2ae..fc2b89ac4 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -90,12 +90,12 @@ with `org-export-taskjuggler-project-tag'" columns hierarchindex, name, start, end, effort, duration, completed, chart timeformat \"%Y-%m-%d\" hideresource 1 - loadunit days + loadunit shortauto }" "resourcereport \"Resource Graph\" { headline \"Resource Allocation Graph\" columns no, name, utilization, freeload, chart - loadunit days + loadunit shortauto sorttasks startup hidetask ~isleaf() }") From d073c313aa5ac1752ab678a56bf15b368259e1e8 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 24 Mar 2010 12:02:37 +0100 Subject: [PATCH 33/54] Fix a problem with effort calculation based on hh:mm efforts --- lisp/org-taskjuggler.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index fc2b89ac4..dca56a128 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -417,16 +417,17 @@ If the ATTRIBUTE is not in ITEM return nil." (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:00 it -will be assumed to be hours and will be translated into 5.0h. +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 ((null effort) effort) ((string-match "\\([0-9]+\\):\\([0-9]+\\)" effort) - ; FIXME: translate the minutes to a decimal - (concat (match-string 1 effort) "." (match-string 2 effort) "h")) + (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)))) From f2d66428b4e9298a4dfbbef74cb02967cfd96d9f Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 24 Mar 2010 16:46:58 +0100 Subject: [PATCH 34/54] Add usage documentation --- lisp/org-taskjuggler.el | 74 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index dca56a128..a82eca558 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -28,7 +28,23 @@ ;; Commentary: ;; -;; This library implements a TaskJuggler exporter for org-mode. +;; This library implements a TaskJuggler exporter for org-mode. It is +;; a bit different from other exporters, such as the HTML and LaTeX +;; exporters for example, in that it does not export all the nodes of +;; a document or strictly follow the order of the nodes in the +;; document. +;; +;; 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 +;; and the attributes defined in all the nodes. +;; +;; * Installation +;; +;; Put this file into your load-path and the following line into your +;; ~/.emacs: +;; +;; (require 'org-taskjuggler) ;; ;; The interactive functions are similar to those of the HTML and LaTeX ;; exporters: @@ -36,9 +52,59 @@ ;; M-x `org-export-as-taskjuggler' ;; M-x `org-export-as-taskjuggler-and-open' ;; -;;; TODO: -;; * Code cleanup -;; * Add documentation +;; * Tasks +;; +;; Let's illustrate this with a small example. Create your tasks as +;; you usually do. 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. +;; Now mark the top node of your tasks with a tag named +;; "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 +;; TaskJugglerUI. +;; +;; * Resources +;; +;; Next you can define resources and assign these to work on specific +;; 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 ID to the resources (using the standard org +;; properties commands) or you can let the exporter generate IDs +;; automatically (the exporter picks the first word of the headline as +;; the ID as long as it is unique). Using that ID 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 the task +;; type +;; +;; C-c C-x p allocate RET RET +;; +;; Once the allocations are done you can again export to TaskJuggler +;; and check in the Resource Allocation Graph which person is working +;; on what task at what time. +;; +;; * Export of properties +;; +;; The exporter also takes TODO state information into consideration, +;; i.e. if a task is marked as done it will have the corresponding +;; attribute in TaskJuggler (complete 100). Also it will export any +;; property on a task resource or resource node which is known to +;; TaskJuggler, such as limits, vacation, shift, booking, efficiency, +;; journalentry, rate for resources or account, start, note, duration, +;; end, journalentry, milestone, reference, responsible, scheduling, +;; etc for tasks. +;; +;; * Dependecies +;; +;; 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 +;; depends attribute can be either "previous-sibling" or a reference +;; to an ID which is defined for another task in the project. ;; ;;; Code: From f8fa4a2b3b1da667a3154a72206a59e8030221bb Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Thu, 25 Mar 2010 10:15:14 +0100 Subject: [PATCH 35/54] Add some TODOs --- lisp/org-taskjuggler.el | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index a82eca558..17a1dc2ae 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -106,6 +106,11 @@ ;; depends attribute can be either "previous-sibling" or a reference ;; to an ID which is defined for another task in the project. ;; +;; * TODO +;; - Look at org-file-properties, org-global-properties and org-global-properties-fixed +;; - What about property inheritance and org-property-inherit-p? +;; - Use TYPE_TODO as an way to assign resources +;; ;;; Code: (eval-when-compile From a81d93a355f5911a7910e96bb403efe643e1d7c9 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 30 Mar 2010 10:11:13 +0200 Subject: [PATCH 36/54] Do not treat the limits attribute any different than the others i.e. you have to specify the limits attribute with {} so that taskjuggler understands it properly. This is for the sake of consistency. --- lisp/org-taskjuggler.el | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 17a1dc2ae..71e9ca8cb 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -110,6 +110,8 @@ ;; - Look at org-file-properties, org-global-properties and 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 +;; previous-sibling and on a specific ID) in multiple attributes are properly exported. ;; ;;; Code: @@ -469,10 +471,7 @@ If the ATTRIBUTE is not in ITEM return nil." (cond ((null item) nil) ((equal (symbol-name attribute) (car (car item))) - (cons (or - (and (equal attribute 'limits) - (format "%s { %s }" (symbol-name attribute) (cdr (car item)))) - (format "%s %s" (symbol-name attribute) (cdr (car item)))) + (cons (format "%s %s" (symbol-name attribute) (cdr (car item))) (org-taskjuggler-get-attribute (cdr item) attribute))) (t (org-taskjuggler-get-attribute (cdr item) attribute)))) From f6cb86ee5bd1966a14958ccb09db9879f529c4ba Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 30 Mar 2010 11:23:41 +0200 Subject: [PATCH 37/54] Improve the documentation --- lisp/org-taskjuggler.el | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 71e9ca8cb..e887d33b1 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -28,11 +28,18 @@ ;; Commentary: ;; -;; This library implements a TaskJuggler exporter for org-mode. It is -;; a bit different from other exporters, such as the HTML and LaTeX -;; exporters for example, in that it does not export all the nodes of -;; a document or strictly follow the order of the nodes in the -;; document. +;; 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 +;; 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 +;; platform. +;; +;; The exporter is a bit different from other exporters, such as the +;; HTML and LaTeX exporters for example, in that it does not export +;; all the nodes of a document or strictly follow the order of the +;; nodes in the document. ;; ;; Instead the TaskJuggler exporter looks for a tree that defines the ;; tasks and a optionally tree that defines the resources for this @@ -54,21 +61,21 @@ ;; ;; * Tasks ;; -;; Let's illustrate this with a small example. Create your tasks as -;; you usually do. 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 +;; 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. ;; Now mark the top node of your tasks with a tag named ;; "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 -;; TaskJugglerUI. +;; TaskJugglerUI. ;; ;; * Resources ;; -;; Next you can define resources and assign these to work on specific +;; Next you can define resources and assign those to work on specific ;; 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 @@ -90,14 +97,14 @@ ;; ;; The exporter also takes TODO state information into consideration, ;; i.e. if a task is marked as done it will have the corresponding -;; attribute in TaskJuggler (complete 100). Also it will export any +;; attribute in TaskJuggler ("complete 100"). Also it will export any ;; property on a task resource or resource node which is known to ;; TaskJuggler, such as limits, vacation, shift, booking, efficiency, ;; journalentry, rate for resources or account, start, note, duration, ;; end, journalentry, milestone, reference, responsible, scheduling, ;; etc for tasks. ;; -;; * Dependecies +;; * Dependencies ;; ;; The exporter will handle dependencies that are defined in the tasks ;; either with the ORDERED attribute (see TODO dependencies in the Org @@ -107,11 +114,15 @@ ;; to an ID which is defined for another task in the project. ;; ;; * TODO -;; - Look at org-file-properties, org-global-properties and org-global-properties-fixed +;; - Look at org-file-properties, org-global-properties and +;; 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 -;; previous-sibling and on a specific ID) in multiple attributes are properly exported. +;; previous-sibling and on a specific ID) in multiple attributes +;; are properly exported. +;; - Fix compiler warnings about reference and assignment to free +;; variable `old-level' in org-taskjuggler-close-maybe ;; ;;; Code: From 4baa13b9810adbfabd0e5b5b13c4a446acebfd9c Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 30 Mar 2010 11:24:02 +0200 Subject: [PATCH 38/54] Add a changelog entry for org-taskjuggler --- lisp/ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 6ba1b9ec7..5adea66ce 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -825,6 +825,11 @@ * org-faces.el (org-faces): Change Customize group variable name +2010-03-30 Christian Egli + + * org-taskjuggler.el: Added a new exporter to export org-mode + projects to taskjuggler files. + 2010-03-29 Carsten Dominik * org-agenda.el (org-diary-last-run-time): New variable. From 020731ec2c6f65f191dbaf480e62a1b718697052 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 7 Apr 2010 11:49:10 +0200 Subject: [PATCH 39/54] Layout fixes --- lisp/org-taskjuggler.el | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index e887d33b1..206a34348 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -230,12 +230,14 @@ defined in `org-export-taskjuggler-default-reports'." (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-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-export-taskjuggler-resource-tag nil 'archive 'comment))) + (org-map-entries + '(org-taskjuggler-components) + org-export-taskjuggler-resource-tag nil 'archive 'comment))) (filename (expand-file-name (concat (file-name-sans-extension @@ -291,7 +293,8 @@ defined in `org-export-taskjuggler-default-reports'." ;;;###autoload (defun org-export-as-taskjuggler-and-open () - "Export the current buffer as a TaskJuggler file and open it with the TaskJuggler GUI." + "Export the current buffer as a TaskJuggler file and open it +with the TaskJuggler GUI." (interactive) (let ((file-name (buffer-file-name (org-export-as-taskjuggler))) (command "TaskJugglerUI")) @@ -358,8 +361,9 @@ unique id to each resource." (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))))))) + (cons resource + (org-taskjuggler-assign-resource-ids (cdr resources) + (cons unique-id unique-ids))))))) (defun org-taskjuggler-resolve-dependencies (tasks) (let ((previous-level 0) @@ -425,8 +429,9 @@ unique id to each resource." (defun org-taskjuggler-get-unique-id (item unique-ids) "Return a unique id for an ITEM which can be a task or a resource. The id is derived from the headline and made unique against -UNIQUE-IDS. If the first part of the headline is not unique try to add -more parts of the headline or finally add more underscore characters (\"_\")." +UNIQUE-IDS. If the first part of the headline is not unique try +to add more parts of the headline or finally add more underscore +characters (\"_\")." (let* ((headline (cdr (assoc "headline" item))) (parts (split-string headline)) (id (org-taskjuggler-clean-id (downcase (pop parts))))) @@ -464,10 +469,10 @@ with separator \"\n\"." (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 listed in -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 +listed in ATTRIBUTES." (org-taskjuggler-filter-and-join (mapcar (lambda (attribute) @@ -501,8 +506,9 @@ If the ATTRIBUTE is not in ITEM return nil." 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." +days and will be translated into 3.0d. Other formats that +taskjuggler supports (like weeks, months and years) are currently +not supported." (cond ((null effort) effort) ((string-match "\\([0-9]+\\):\\([0-9]+\\)" effort) From d6de24e44bf704e344b8a126ef4d2f7c143e3b55 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 7 Apr 2010 11:50:08 +0200 Subject: [PATCH 40/54] Added support for optional depends attributes --- lisp/org-taskjuggler.el | 55 ++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 206a34348..75a375b38 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -374,12 +374,14 @@ unique id to each resource." (depends (cdr (assoc "depends" task))) (parent-ordered (cdr (assoc "parent-ordered" task))) (blocker (cdr (assoc "BLOCKER" task))) - (blocked-on-previous (and blocker (string-match "previous-sibling" blocker))) + (blocked-on-previous + (and blocker (string-match "previous-sibling" blocker))) (dependencies (org-taskjuggler-resolve-explicit-dependencies (append - (and depends (split-string depends "[, \f\t\n\r\v]+" t)) - (and blocker (split-string blocker "[, \f\t\n\r\v]+" t))) tasks)) + (and depends (org-taskjuggler-tokenize-dependencies depends)) + (and blocker (org-taskjuggler-tokenize-dependencies blocker))) + tasks)) previous-sibling) ; update previous sibling info (cond @@ -404,21 +406,44 @@ unique id to each resource." (setq previous-level level) (setq resolved-tasks (append resolved-tasks (list task))))))) +(defun org-taskjuggler-tokenize-dependencies (dependencies) + "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 + ((string-match "^ *$" dependencies) nil) + ((string-match "^[ \t]*\\([-a-zA-Z0-9_]+\\([ \t]*{[^}]+}\\)?\\)[ \t,]*" dependencies) + (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))))) + (defun org-taskjuggler-resolve-explicit-dependencies (dependencies tasks) - (let (path) - (cond - ((null dependencies) nil) - ; 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 - ((setq path (org-taskjuggler-find-task-with-id (car dependencies) tasks)) - (cons path (org-taskjuggler-resolve-explicit-dependencies (cdr dependencies) tasks))) - ; silently ignore all other dependencies - (t (org-taskjuggler-resolve-explicit-dependencies (cdr dependencies) tasks))))) + (unless (null dependencies) + (let* + ;; the dependency might have optional attributes such as "{ + ;; gapduration 5d }", so only use the first string as id for the + ;; dependency + ((id (car (split-string (car dependencies)))) + (optional-attributes + (mapconcat 'identity (cdr (split-string (car dependencies))) " ")) + (path (org-taskjuggler-find-task-with-id id tasks))) + (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)) + (cons (mapconcat 'identity (list path optional-attributes) " ") + (org-taskjuggler-resolve-explicit-dependencies + (cdr dependencies) tasks))) + ;; silently ignore all other dependencies + (t (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 return nil." + "Find ID in tasks. If found return the path of task. Otherwise +return nil." (let ((task-id (cdr (assoc "ID" (car tasks)))) (path (cdr (assoc "path" (car tasks))))) (cond From 97f3465176667b23ef5b6907f7fe92a68c08b3bc Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 13 Apr 2010 16:25:40 +0200 Subject: [PATCH 41/54] Add some documentation about optional attributes for dependencies --- lisp/ChangeLog | 5 +++++ lisp/org-taskjuggler.el | 39 +++++++++++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 5adea66ce..512bbe620 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -687,6 +687,11 @@ (org-export-latex-default-packages-alist): hyperref must be loaded late. +2010-04-07 Christian Egli + + * org-taskjuggler.el (org-taskjuggler-tokenize-dependencies): Add + support for optional attributes on dependencies. + 2010-04-07 Carsten Dominik * org-latex.el (org-export-latex-preprocess): Do not yet protect diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 75a375b38..ffa6850e9 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -111,9 +111,32 @@ ;; mode manual) or with the BLOCKER attribute (see org-depend.el) or ;; alternatively with a depends attribute. Both the BLOCKER and the ;; depends attribute can be either "previous-sibling" or a reference -;; to an ID which is defined for another task in the project. +;; to an 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 also specify optional +;; attributes on the dependency by simply appending it. The following +;; examples should illustrate this: ;; -;; * TODO +;; * Training material +;; :PROPERTIES: +;; :ID: training_material +;; :ORDERED: t +;; :END: +;; ** Markup Guidelines +;; :PROPERTIES: +;; :Effort: 2.0 +;; :END: +;; ** Workflow Guidelines +;; :PROPERTIES: +;; :Effort: 2.0 +;; :END: +;; * Presentation +;; :PROPERTIES: +;; :Effort: 2.0 +;; :BLOCKER: training_material { gapduration 1d } some_other_task +;; :END: +;; +;;;; * TODO ;; - Look at org-file-properties, org-global-properties and ;; org-global-properties-fixed ;; - What about property inheritance and org-property-inherit-p? @@ -420,14 +443,22 @@ dependencies. A dependency will have to match `[-a-zA-Z0-9_]+'." (t (error (format "invalid dependency id %s" dependencies))))) (defun org-taskjuggler-resolve-explicit-dependencies (dependencies tasks) + "For each dependency in DEPENDENCIES try to find a +corresponding task with a matching ID in TASKS. Return a list +containing the resolved links for all DEPENDENCIES 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 silently ignored." (unless (null dependencies) (let* ;; the dependency might have optional attributes such as "{ ;; gapduration 5d }", so only use the first string as id for the ;; dependency - ((id (car (split-string (car dependencies)))) + ((dependency (car dependencies)) + (id (car (split-string dependency))) (optional-attributes - (mapconcat 'identity (cdr (split-string (car dependencies))) " ")) + (mapconcat 'identity (cdr (split-string dependency)) " ")) (path (org-taskjuggler-find-task-with-id id tasks))) (cond ;; ignore previous sibling dependencies From 1a2229d7855876ab2fb78cfbdb868dc3f75692cd Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 20 Apr 2010 09:59:30 +0200 Subject: [PATCH 42/54] Fail more gracefully if no tasks are defined --- lisp/org-taskjuggler.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index ffa6850e9..f69399df5 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -269,6 +269,8 @@ defined in `org-export-taskjuggler-default-reports'." (buffer (find-file-noselect filename)) (old-level 0) task resource) + (unless tasks + (error "No tasks specified")) ;; add a default resource (unless resources (setq resources From aae911593845e13c9ffbe8bce3ef29a0ef8324ed Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 20 Apr 2010 10:01:24 +0200 Subject: [PATCH 43/54] Make export work even if `org-odd-levels-only' is in use --- lisp/org-taskjuggler.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index f69399df5..6b6157e31 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -337,7 +337,7 @@ the current node such as the headline, the level, todo state information, all the properties, etc." (let* ((props (org-entry-properties)) (components (org-heading-components)) - (level (car components)) + (level (nth 1 components)) (headline (nth 4 components)) (parent-ordered (org-taskjuggler-parent-is-ordered-p))) (push (cons "level" level) props) From 5c89333555a0d23f9b9489faf62991c387bdf3c7 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 20 Apr 2010 10:02:13 +0200 Subject: [PATCH 44/54] Fix a typo --- lisp/org-taskjuggler.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 6b6157e31..b4dd0c949 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -597,7 +597,7 @@ org-mode priority string." (previous-sibling (cdr (assoc "previous-sibling" task))) (attributes '(account start note duration endbuffer endcredit end - flags journalentry, length maxend maxstart milestone + flags journalentry length maxend maxstart milestone minend minstart period reference responsible scheduling startbuffer startcredit statusnote))) (insert From 399844ee5233d6cd757ae6a4c170e26ae85324fe Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 21 Apr 2010 11:30:52 +0200 Subject: [PATCH 45/54] Add more documentation about resource id generation --- lisp/org-taskjuggler.el | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index b4dd0c949..ef8ebe1d8 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -82,7 +82,8 @@ ;; optionally assign an ID to the resources (using the standard org ;; properties commands) or you can let the exporter generate IDs ;; automatically (the exporter picks the first word of the headline as -;; the ID as long as it is unique). Using that ID you can then +;; the ID as long as it is unique, see the documentation of +;; `org-taskjuggler-get-unique-id'). Using that ID 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 the task ;; type @@ -184,7 +185,7 @@ resources for the project." :group 'org-export-taskjuggler :type 'string) -(defcustom org-export-taskjuggler-default-project-duration 180 +(defcustom org-export-taskjuggler-default-project-duration 280 "Default project duration if no start and end date have been defined in the root node of the task tree, i.e. the tree that has been marked with `org-export-taskjuggler-project-tag'" @@ -487,9 +488,9 @@ return nil." (defun org-taskjuggler-get-unique-id (item unique-ids) "Return a unique id for an ITEM which can be a task or a resource. The id is derived from the headline and made unique against -UNIQUE-IDS. If the first part of the headline is not unique try -to add more parts of the headline or finally add more underscore -characters (\"_\")." +UNIQUE-IDS. If the (downcased) first token of the headline is not +unique try to add more (downcased) tokens of the headline or +finally add more underscore characters (\"_\")." (let* ((headline (cdr (assoc "headline" item))) (parts (split-string headline)) (id (org-taskjuggler-clean-id (downcase (pop parts))))) From 68068aa661fa2e19dbf4bb79a2ef801827d77109 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 28 Apr 2010 09:11:09 +0200 Subject: [PATCH 46/54] Do not use the ID property frivolously use a property named "task_id" instead as we do not search for task ids across files. For resources use a property named "resource_id" or only as a fall back the ID property. Also issue a warning if a dependency cannot be resolved. --- lisp/ChangeLog | 8 ++++++ lisp/org-taskjuggler.el | 64 ++++++++++++++++++++++------------------- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 512bbe620..5000f2f4e 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -291,6 +291,14 @@ * org-html.el (org-format-org-table-html): Test all columns for number content. +2010-04-28 Christian Egli + + * org-taskjuggler.el (org-taskjuggler-find-task-with-id): Do not + use the ID property frivolously, i.e. use a property named + "task_id" instead as we do not search for ids across files. + (org-taskjuggler-resolve-explicit-dependencies): Issue a warning + if a dependency cannot be resolved. + 2010-04-28 Carsten Dominik * org-latex.el (org-export-latex-treat-sub-super-char): Make diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index ef8ebe1d8..f29251348 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -79,16 +79,17 @@ ;; 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 ID to the resources (using the standard org -;; properties commands) or you can let the exporter generate IDs -;; automatically (the exporter picks the first word of the headline as -;; the ID as long as it is unique, see the documentation of -;; `org-taskjuggler-get-unique-id'). Using that ID 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 the task -;; type +;; optionally assign an identifier (named "resource_id") to the +;; resources (using the standard org properties commands) or you can +;; let the exporter generate identifiers automatically (the exporter +;; 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 +;; the task type ;; -;; C-c C-x p allocate RET RET +;; C-c C-x p allocate RET RET ;; ;; Once the allocations are done you can again export to TaskJuggler ;; and check in the Resource Allocation Graph which person is working @@ -112,15 +113,15 @@ ;; mode manual) or with the BLOCKER attribute (see org-depend.el) or ;; alternatively with a depends attribute. Both the BLOCKER and the ;; depends attribute can be either "previous-sibling" or a reference -;; to an 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 also specify optional -;; attributes on the dependency by simply appending it. The following -;; examples should illustrate this: +;; 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 +;; also specify optional attributes on the dependency by simply +;; appending it. The following examples should illustrate this: ;; ;; * Training material ;; :PROPERTIES: -;; :ID: training_material +;; :task_id: training_material ;; :ORDERED: t ;; :END: ;; ** Markup Guidelines @@ -143,8 +144,8 @@ ;; - 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 -;; previous-sibling and on a specific ID) in multiple attributes -;; are properly exported. +;; previous-sibling and on a specific task_id) in multiple +;; attributes are properly exported. ;; - Fix compiler warnings about reference and assignment to free ;; variable `old-level' in org-taskjuggler-close-maybe ;; @@ -275,13 +276,13 @@ defined in `org-export-taskjuggler-default-reports'." ;; add a default resource (unless resources (setq resources - `((("ID" . ,(user-login-name)) + `((("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)) (let ((task (car tasks)) - (resource-id (cdr (assoc "ID" (car resources))))) + (resource-id (cdr (assoc "resource_id" (car resources))))) (setcar tasks (push (cons "allocate" resource-id) task)))) ;; add a default start date to the first task if none was given (unless (assoc "start" (car tasks)) @@ -447,12 +448,12 @@ dependencies. A dependency will have to match `[-a-zA-Z0-9_]+'." (defun org-taskjuggler-resolve-explicit-dependencies (dependencies tasks) "For each dependency in DEPENDENCIES try to find a -corresponding task with a matching ID in TASKS. Return a list -containing the resolved links for all DEPENDENCIES where a -matching tasks was found. If the dependency is +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 \"previous-sibling\" it is ignored (as this is dealt with in `org-taskjuggler-resolve-dependencies'). If there is no matching -task the dependency is silently ignored." +task the dependency is ignored and a warning is displayed ." (unless (null dependencies) (let* ;; the dependency might have optional attributes such as "{ @@ -472,13 +473,16 @@ task the dependency is silently ignored." (cons (mapconcat 'identity (list path optional-attributes) " ") (org-taskjuggler-resolve-explicit-dependencies (cdr dependencies) tasks))) - ;; silently ignore all other dependencies - (t (org-taskjuggler-resolve-explicit-dependencies (cdr dependencies) tasks)))))) + ;; warn about dangling dependency but otherwise ignore it + (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 return nil." - (let ((task-id (cdr (assoc "ID" (car tasks)))) + (let ((task-id (cdr (assoc "task_id" (car tasks)))) (path (cdr (assoc "path" (car tasks))))) (cond ((null tasks) nil) @@ -551,13 +555,15 @@ If the ATTRIBUTE is not in ITEM return nil." (t (org-taskjuggler-get-attribute (cdr item) attribute)))) (defun org-taskjuggler-open-resource (resource) - (let ((id (org-taskjuggler-clean-id (cdr (assoc "ID" resource)))) - (unique-id (org-taskjuggler-clean-id (cdr (assoc "unique-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 - "resource " (or id unique-id) " \"" headline "\" {\n " + "resource " id " \"" headline "\" {\n " (org-taskjuggler-get-attributes resource attributes) "\n")))) (defun org-taskjuggler-clean-effort (effort) From c01263755592b77518bfbb3be14ce1181ff977ab Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Wed, 28 Apr 2010 09:14:43 +0200 Subject: [PATCH 47/54] Add some documentation. --- lisp/ChangeLog | 1 + lisp/org-taskjuggler.el | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 5000f2f4e..907f72485 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -298,6 +298,7 @@ "task_id" instead as we do not search for ids across files. (org-taskjuggler-resolve-explicit-dependencies): Issue a warning if a dependency cannot be resolved. + (org-taskjuggler-open-resource): Add documentation. 2010-04-28 Carsten Dominik diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index f29251348..469b6ee2c 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -511,7 +511,7 @@ finally add more underscore characters (\"_\")." (and id (replace-regexp-in-string "[^a-zA-Z0-9_]" "_" id))) (defun org-taskjuggler-open-project (project) - "Insert a the beginning of project declaration. All valid + "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." @@ -555,6 +555,12 @@ If the ATTRIBUTE is not in ITEM return nil." (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 +defines a property \"resource_id\" it will be used as the id for +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)) From 421423f0c2f7d5a7547285d0151fd8ee2f36001a Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Thu, 27 May 2010 09:36:30 +0200 Subject: [PATCH 48/54] Add a TODO about supporting SCHEDULED and DEADLINE information --- lisp/org-taskjuggler.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 469b6ee2c..5c0d4bd6e 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -139,13 +139,15 @@ ;; :END: ;; ;;;; * TODO +;; - Use SCHEDULED and DEADLINE information (not just start and end +;; properties). ;; - Look at org-file-properties, org-global-properties and ;; 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 ;; previous-sibling and on a specific task_id) in multiple -;; attributes are properly exported. +;; attributes are properly exported. ;; - Fix compiler warnings about reference and assignment to free ;; variable `old-level' in org-taskjuggler-close-maybe ;; From 964f8dab5beac0f27cb690595698a97e80fb290f Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Fri, 4 Jun 2010 17:22:13 +0200 Subject: [PATCH 49/54] Add documentation for TaskJuggler export --- doc/ChangeLog | 5 ++ doc/org.texi | 145 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 144 insertions(+), 6 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 4a9132aa6..11d7c0308 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,8 @@ +2010-06-03 Christian Egli + + * org.texi (TaskJuggler export): Added documentation for the + TaskJuggler exporter. + 2010-05-19 Carsten Dominik * org.texi (Column attributes): Document that the ":" operator diff --git a/doc/org.texi b/doc/org.texi index 50e6dfc90..0ff76b6f1 100644 --- a/doc/org.texi +++ b/doc/org.texi @@ -339,6 +339,7 @@ Exporting * HTML export:: Exporting to HTML * LaTeX and PDF export:: Exporting to La@TeX{}, and processing to PDF * DocBook export:: Exporting to DocBook +* TaskJuggler export:: Exporting to TaskJuggler * Freemind export:: Exporting to Freemind mind maps * XOXO export:: Exporting to XOXO * iCalendar export:: Exporting in iCalendar format @@ -8957,10 +8958,11 @@ the web, while the XOXO format provides a solid base for exchange with a broad range of other applications. La@TeX{} export lets you use Org mode and its structured editing functions to easily create La@TeX{} files. DocBook export makes it possible to convert Org files to many other formats using -DocBook tools. To incorporate entries with associated times like deadlines -or appointments into a desktop calendar program like iCal, Org mode can also -produce extracts in the iCalendar format. Currently Org mode only supports -export, not import of these different formats. +DocBook tools. For project management you can create gantt and resource +charts by using TaskJuggler export. To incorporate entries with associated +times like deadlines or appointments into a desktop calendar program like +iCal, Org mode can also produce extracts in the iCalendar format. Currently +Org mode only supports export, not import of these different formats. Org supports export of selected regions when @code{transient-mark-mode} is enabled (default in Emacs 23). @@ -8973,6 +8975,7 @@ enabled (default in Emacs 23). * HTML export:: Exporting to HTML * LaTeX and PDF export:: Exporting to La@TeX{}, and processing to PDF * DocBook export:: Exporting to DocBook +* TaskJuggler export:: Exporting to TaskJuggler * Freemind export:: Exporting to Freemind mind maps * XOXO export:: Exporting to XOXO * iCalendar export:: Exporting in iCalendar format @@ -9898,7 +9901,7 @@ Here is a simple example Org document that is intended for beamer export. For more information, see the documentation on Worg. -@node DocBook export, Freemind export, LaTeX and PDF export, Exporting +@node DocBook export, TaskJuggler export, LaTeX and PDF export, Exporting @section DocBook export @cindex DocBook export @cindex PDF export @@ -10098,7 +10101,137 @@ special characters included in XHTML entities: " @end example -@node Freemind export, XOXO export, DocBook export, Exporting +@node TaskJuggler export, Freemind export, DocBook export, Exporting +@section TaskJuggler export +@cindex TaskJuggler export +@cindex Project management + +@uref{http://www.taskjuggler.org/, TaskJuggler} is a project management tool. +It provides an optimizing scheduler that computes your project time lines and +resource assignments based on the project outline and the constraints that +you have provided. + +The TaskJuggler exporter is a bit different from other exporters, such as the +HTML and LaTeX exporters for example, in that it does not export all the +nodes of a document or strictly follow the order of the nodes in the +document. + +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 and the attributes defined in +all the nodes. + +@subsection TaskJuggler export commands + +@table @kbd +@kindex C-c C-e j +@item C-c C-e j +Export as TaskJuggler file. + +@kindex C-c C-e J +@item C-c C-e J +Export as TaskJuggler file and then open the file with TaskJugglerUI. +@end table + +@subsection Tasks + +@vindex org-export-taskjuggler-project-tag +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 +@url{http://www.contextualdevelopment.com/static/artifacts/articles/2008/project-planning/project-planning.org}. +Now mark the top node of your tasks with a tag named +@code{:taskjuggler_project:} (or whatever you customized +@code{org-export-taskjuggler-project-tag} to). You are now ready to export +the project plan with @kbd{C-c C-e J} which will export the project plan and +open a gantt chart in TaskJugglerUI. + +@subsection Resources + +@vindex org-export-taskjuggler-resource-tag +Next you can define resources and assign those to work on specific tasks. You +can group your resources hierarchically. Tag the top node of the resources +with @code{:taskjuggler_resource:} (or whatever you customized +@code{org-export-taskjuggler-resource-tag} to). You can optionally assign an +identifier (named @samp{resource_id}) to the resources (using the standard +Org properties commands, @pxref{Property syntax}) or you can let the exporter +generate identifiers automatically (the exporter picks the first word of the +headline as the identifier as long as it is unique, see the documentation of +@code{org-taskjuggler-get-unique-id}). Using that identifier you can then +allocate resources to tasks. This is again done with the @samp{allocate} +property on the tasks. Do this in column view or when on the task type +@kbd{C-c C-x p allocate @key{RET} @key{RET}}. + +Once the allocations are done you can again export to TaskJuggler and check +in the Resource Allocation Graph which person is working on what task at what +time. + +@subsection Export of properties + +The exporter also takes TODO state information into consideration, i.e. if a +task is marked as done it will have the corresponding attribute in +TaskJuggler (@samp{complete 100}). Also it will export any property on a task +resource or resource node which is known to TaskJuggler, such as +@samp{limits}, @samp{vacation}, @samp{shift}, @samp{booking}, +@samp{efficiency}, @samp{journalentry}, @samp{rate} for resources or +@samp{account}, @samp{start}, @samp{note}, @samp{duration}, @samp{end}, +@samp{journalentry}, @samp{milestone}, @samp{reference}, @samp{responsible}, +@samp{scheduling}, etc for tasks. + +@subsection Dependencies + +The exporter will handle dependencies that are defined in the tasks either +with the @samp{ORDERED} attribute (@pxref{TODO dependencies}), with the +@samp{BLOCKER} attribute (see org-depend.el) or alternatively with a +@samp{depends} attribute. Both the @samp{BLOCKER} and the @samp{depends} +attribute can be either @samp{previous-sibling} or a reference to an +identifier (named @samp{task_id}) which is defined for another task in the +project. @samp{BLOCKER} and the @samp{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: + +@example +* Preparation + :PROPERTIES: + :task_id: preparation + :ORDERED: t + :END: +* Training material + :PROPERTIES: + :task_id: training_material + :ORDERED: t + :END: +** Markup Guidelines + :PROPERTIES: + :Effort: 2.0 + :END: +** Workflow Guidelines + :PROPERTIES: + :Effort: 2.0 + :END: +* Presentation + :PROPERTIES: + :Effort: 2.0 + :BLOCKER: training_material @{ gapduration 1d @} preparation + :END: +@end example + +@subsection Reports + +@vindex org-export-taskjuggler-default-reports +TaskJuggler can produce many kinds of reports (e.g. gantt chart, resource +allocation, etc). The user defines what kind of reports should be generated +for a project in the TaskJuggler file. The exporter will automatically insert +some default reports in the file. These defaults are defined in +@code{org-export-taskjuggler-default-reports}. They can be modified using +customize along with a number of other options. For a more complete list, see +@kbd{M-x customize-group @key{RET} org-export-taskjuggler @key{RET}}. + +For more information and examples see the Org-taskjuggler tutorial at +@uref{http://orgmode.org/worg/org-tutorials/org-taskjuggler.php}. + +@node Freemind export, XOXO export, TaskJuggler export, Exporting @section Freemind export @cindex Freemind export @cindex mind map From 8b7fd7d8ae0e3a79af078d2103e4d8fb157ef3f4 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Mon, 7 Jun 2010 17:09:12 +0200 Subject: [PATCH 50/54] Added taskjuggler export to the export dispatcher --- lisp/ChangeLog | 5 +++++ lisp/org-exp.el | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 907f72485..99c3ec3a5 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,8 @@ +2010-06-07 Christian Egli + + * org-exp.el (org-export): Added taskjuggler export to the export + dispatcher. + 2010-05-28 Bastien Guerry * org-timer.el (org-timer-set-timer): Use a prefix argument. diff --git a/lisp/org-exp.el b/lisp/org-exp.el index 483d04748..3d9541bf4 100644 --- a/lisp/org-exp.el +++ b/lisp/org-exp.el @@ -862,6 +862,8 @@ value of `org-export-run-in-background'." \[D] export as DocBook [V] export as DocBook, process to PDF, and open +\[j] export as TaskJuggler [J] ... and open + \[m] export as Freemind mind map \[x] export as XOXO \[g] export using Wes Hardaker's generic exporter @@ -888,6 +890,8 @@ value of `org-export-run-in-background'." (?g org-export-generic t) (?D org-export-as-docbook t) (?V org-export-as-docbook-pdf-and-open t) + (?j org-export-as-taskjuggler t) + (?J org-export-as-taskjuggler-and-open t) (?m org-export-as-freemind t) (?l org-export-as-latex t) (?p org-export-as-pdf t) From a85cb37d47f525bf3f40cda8813f32e2257011d5 Mon Sep 17 00:00:00 2001 From: Bastien Guerry Date: Tue, 8 Jun 2010 09:35:07 +0200 Subject: [PATCH 51/54] * org-timer.el (org-timer-set-timer): Fix typo in the docstring. --- lisp/ChangeLog | 4 ++++ lisp/org-timer.el | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 6ba1b9ec7..3034f56fa 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,7 @@ +2010-06-08 Bastien Guerry + + * org-timer.el (org-timer-set-timer): Fix typo in the docstring. + 2010-05-28 Bastien Guerry * org-timer.el (org-timer-set-timer): Use a prefix argument. diff --git a/lisp/org-timer.el b/lisp/org-timer.el index 88ae69a79..e0817c8df 100644 --- a/lisp/org-timer.el +++ b/lisp/org-timer.el @@ -320,10 +320,10 @@ prompt the use if she wants to replace it. Called with a numeric prefix argument, use this numeric value as the duration of the timer. -Called with a `C-u' prefix argument, use `org-timer-default-timer' +Called with a `C-u' prefix arguments, use `org-timer-default-timer' without prompting the user for a duration. -With two `C-u' prefix argument, use `org-timer-default-timer' +With two `C-u' prefix arguments, use `org-timer-default-timer' without prompting the user for a duration and automatically replace any running timer." (interactive "P") From e04b71649eb598a5be9f0c2cec84e7e5a4cc6ab8 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 8 Jun 2010 09:37:23 +0200 Subject: [PATCH 52/54] TaksJuggler export is dependent on org-exp.el --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 07fe4bbf4..12759cda8 100644 --- a/Makefile +++ b/Makefile @@ -389,7 +389,7 @@ lisp/org-remember.elc: lisp/org.el lisp/org-rmail.elc: lisp/org.el lisp/org-src.elc: lisp/org-macs.el lisp/org-compat.el lisp/org-table.elc: lisp/org.el -lisp/org-taskjuggler.elc: lisp/org.el +lisp/org-taskjuggler.elc: lisp/org.el lisp/org-exp.el lisp/org-timer.elc: lisp/org.el lisp/org-vm.elc: lisp/org.el lisp/org-w3m.elc: lisp/org.el From e1de4db669ab033b9fbcd82391c8a69ebf92a4bc Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Tue, 8 Jun 2010 15:13:50 +0200 Subject: [PATCH 53/54] Avoid compiler warnings about reference to free variable `old-level' --- lisp/ChangeLog | 5 +++++ lisp/org-taskjuggler.el | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 99c3ec3a5..4d927318a 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,8 @@ +2010-06-08 Christian Egli + + * org-taskjuggler.el (org-export-taskjuggler-old-level): + define local variable to avoid compiler warning. + 2010-06-07 Christian Egli * org-exp.el (org-export): Added taskjuggler export to the export diff --git a/lisp/org-taskjuggler.el b/lisp/org-taskjuggler.el index 5c0d4bd6e..f64138e20 100644 --- a/lisp/org-taskjuggler.el +++ b/lisp/org-taskjuggler.el @@ -148,8 +148,6 @@ ;; - Make sure multiple dependency definitions (i.e. BLOCKER on ;; previous-sibling and on a specific task_id) in multiple ;; attributes are properly exported. -;; - Fix compiler warnings about reference and assignment to free -;; variable `old-level' in org-taskjuggler-close-maybe ;; ;;; Code: @@ -237,6 +235,9 @@ but before any resource and task declarations." ;;; Autoload functions: +;; avoid compiler warning about free variable +(defvar org-export-taskjuggler-old-level) + ;;;###autoload (defun org-export-as-taskjuggler () "Export parts of the current buffer as a TaskJuggler file. @@ -271,7 +272,7 @@ defined in `org-export-taskjuggler-default-reports'." (file-name-nondirectory buffer-file-name)) org-export-taskjuggler-extension))) (buffer (find-file-noselect filename)) - (old-level 0) + (org-export-taskjuggler-old-level 0) task resource) (unless tasks (error "No tasks specified")) @@ -305,14 +306,14 @@ defined in `org-export-taskjuggler-default-reports'." (let ((level (cdr (assoc "level" resource)))) (org-taskjuggler-close-maybe level) (org-taskjuggler-open-resource resource) - (setq old-level level))) + (setq org-export-taskjuggler-old-level level))) (org-taskjuggler-close-maybe 1) - (setq old-level 0) + (setq org-export-taskjuggler-old-level 0) (dolist (task tasks) (let ((level (cdr (assoc "level" task)))) (org-taskjuggler-close-maybe level) (org-taskjuggler-open-task task) - (setq old-level level))) + (setq org-export-taskjuggler-old-level level))) (org-taskjuggler-close-maybe 1) (org-taskjuggler-insert-reports) (save-buffer) @@ -630,10 +631,10 @@ org-mode priority string." "\n")))) (defun org-taskjuggler-close-maybe (level) - (while (> old-level level) + (while (> org-export-taskjuggler-old-level level) (insert "}\n") - (setq old-level (1- old-level))) - (when (= old-level level) + (setq org-export-taskjuggler-old-level (1- org-export-taskjuggler-old-level))) + (when (= org-export-taskjuggler-old-level level) (insert "}\n"))) (defun org-taskjuggler-insert-reports () From 51f37b5e0f324ddf715f78193d726184b4aac72b Mon Sep 17 00:00:00 2001 From: Carsten Dominik Date: Tue, 8 Jun 2010 18:40:06 +0200 Subject: [PATCH 54/54] Clean up the todo.org file It will be removed once the content is integrated into the issue file --- ORGWEBPAGE/todo.org | 98 ++++----------------------------------------- 1 file changed, 7 insertions(+), 91 deletions(-) diff --git a/ORGWEBPAGE/todo.org b/ORGWEBPAGE/todo.org index 8b158f593..adab17b53 100644 --- a/ORGWEBPAGE/todo.org +++ b/ORGWEBPAGE/todo.org @@ -41,25 +41,19 @@ something. ** Structure *** TODO Get rid of all the \r instances, which were used only for XEmacs. -*** TODO proper visibility cycling for items +*** WISH proper visibility cycling for items Make them not hide the text after the final list item. This is not trivial, we cannot usenormal outline stuff, - needs a separate implementaiton. -*** WISH Inline TODO entries - A way to put a TODO entry without starting a new section. + needs a separate implementation. ** Agenda issues -*** WISH Make more modes changeable from the agenda - These could be made available for toggling, just like - follow-mode. Examples: - - org-agenda-todo-list-sublevels - - org-tags-match-list-sublevels - - org-agenda-todo-ignore-scheduled ** Links *** WISH Variable of standard links for completion with C-c C-l Or something like that, to make standard links fast. + *** IDEA Find all links to a specific file + *** IDEA Make info HTML links work for links to Info files Info links of course only work inside Emacs. However, many info documents are on the web, so the HTML exporter could try to be @@ -70,11 +64,6 @@ something. question is, is this URL going to be stable so that it makes sense to actually put this into org.el? -*** TODO Remove irretrievable links from /published/ HTML output - This is on David's table, and he will hopefully integrate a - mechanism for this into org-publish.el. The discussion about this - was started by [[http://thread.gmane.org/gmane.emacs.orgmode/281][Austin Frank]] - *** TODO Document the character protection in links I don't think this is really covered anywhere. Maybe we also should protect characters in the visible part, to @@ -90,80 +79,18 @@ something. @4=..... *** WISH Make a variable that current line should be recomputed always - in each table, skipping headers of course. + In each table. Skipping headers of course. -*** BUG When computing in a narrowed column, aligning may go wrong. - Computing changes fields and does not yet see correctly if the column - width has changed, in the case of a narrowed column. - -** Compatibility issues -*** Emacs 21 compatibility - This is being phased out. Almost everything works under Emacs 21, - but in the future I will make little effort to support it. - -*** XEmacs compatibility ** Exporting -*** IDEA Convert links to footnotes for ASCII export. - But the question is: where should these footnotes be placed? ** Publishing -We need to simplify the publishing options. Here are some of the -options for publishing projects -*** Publishing options - -:`:base-directory' Directory containing publishing source files -: -:`:publishing-directory' Directory (possibly remote) where output files -: will be published. -: -:`:preparation-function' Function called before starting publishing -: process, for example to run `make' for updating -: files to be published. -: -:`:base-extension' Extension (without the dot!) of source files. This -: actually is a regular expression. -: -:`:exclude' Regular expression to match file names that should -: not be published, even though they have been selected -: on the basis of their extension. -: -:`:include' List of files to be included regardless of -: `:base-extension' and `:exclude'. -: -:`:publishing-function' Function executing the publication of a file. -: This may also be a list of functions, which will -: all be called in turn. -: -:`:link-validation-function' Function to validate links -: -:`:auto-index' When non-nil, publish an index during -: org-publish-current-project or org-publish-all. -: -:`:index-filename' Filename for output of index. Defaults to `index.org' -: (which becomes `index.html'). -: -:`:index-title' Title of index page. Defaults to name of file. -: -:`:index-function' Plug-in function to use for generation of index. -: Defaults to `org-publish-org-index', which generates -: a plain list of links to all files in the project. - -*** TODO Document the :recursive option -*** QUESTION Does anyone use the index related options -*** QUESTION Remove :base-extension and only use :include -*** WISH Simple interaction between :include :exclude :recursive -*** QUESTION Use an export-directory option per file? - -For now we use publishing-directory but this is not consistent with the -convention of using "publishing" when there is a /project/ to publish. +*** TODO Document the :recursive option for org-publish ** Miscellaneous Stuff *** BUG Comments cannot be filled -*** WISH Inlining of images in Org-mode files -*** TODO Fixup outline-magic.el, so that it can be used. *** TODO Use the new argument of bibtex-url Roland Winkler was kind enough to implement a new argument to the @@ -180,6 +107,7 @@ convention of using "publishing" when there is a /project/ to publish. *** Priorities Here is some information about priorities, which is not yet documented. Actually, I am not sur if the list here is correct + either **** QUOTE Priorities TODO entries: 1 or 1,2,... DEADLINE is 10-ddays, i.e. it is 10 on the due day @@ -195,22 +123,10 @@ convention of using "publishing" when there is a /project/ to publish. Priority * 1000 -*** INCONSISTENCY: items don't grow/shrink due to promotion. - In plain lists, multiple demote/promote commands executed directly - after each other don't change the scope of the command - the - initially selected text continues to be selected. This is - inconsistent with the behavior of outline sections, were the subtree - for promotion/demotion is newly defined after each command. Which - convention is better? Should this be consistent between trees and - plain lists? - *** QUESTION grep on directory does not yet work. I am actually not sure, I might have addressed this already, but my memory is failing me. Needs some checking. - -* Archive -** Archived Tasks * COMMENT HTML style specifications # Local Variables: