~bzg/org-contrib

8e32471fc8d7b4edc07e416488e55f0f33c86f23 — Harold Ollivier 14 days ago 3062acd
ox-taskjuggler.el: Update

* lisp/ox-taskjuggler.el (org-taskjuggler-report-tag): New option.
(org-taskjuggler-valid-resource-attributes): New argument.
(org-taskjuggler-valid-account-attributes): New option.
(org-taskjuggler-valid-report-attributes): New arguments.
(org-taskjuggler-assign-account-ids): New function.
(org-taskjuggler-get-end): Use `org-taskjuggler-valid-task-attributes'.
(org-taskjuggler-project-plan): Use `org-taskjuggler-account-tag'.
(org-taskjuggler-project-plan): Rewrite.
(org-taskjuggler--build-account): New function.
(org-taskjuggler--build-report): Update.
1 files changed, 165 insertions(+), 73 deletions(-)

M lisp/ox-taskjuggler.el
M lisp/ox-taskjuggler.el => lisp/ox-taskjuggler.el +165 -73
@@ 177,6 177,13 @@ for the project."
  :group 'org-export-taskjuggler
  :type 'string)

(defcustom org-taskjuggler-account-tag "taskjuggler_account"
  "Tag marking project's accounts.
This tag is used to find the tree containing all the accounts
for the project."
  :group 'org-export-taskjuggler
  :type 'string)

(defcustom org-taskjuggler-report-tag "taskjuggler_report"
  "Tag marking project's reports.
This tag is used to find the tree containing all the reports for


@@ 330,16 337,23 @@ list."

(defcustom org-taskjuggler-valid-resource-attributes
  '(limits vacation shift booking efficiency journalentry rate
	   workinghours flags)
	   workinghours flags chargeset)
  "Valid attributes for Taskjuggler resources.
If one of these appears as a property for a headline, it will be
exported with the corresponding resource."
  :group 'org-export-taskjuggler
  :type '(repeat symbol))

(defcustom org-taskjuggler-valid-account-attributes
  '(aggregate credits flags)
  "Valid attributes for Taskjuggler accounts.
If one of these appears as a property for a headline, it will be
exported with the corresponding account."
  :group 'org-export-taskjuggler)

(defcustom org-taskjuggler-valid-report-attributes
  '(headline columns definitions timeformat hideresource hidetask
	     loadunit sorttasks formats period)
  '(headline columns definitions timeformat hideaccount hideresource hidetask
	     loadunit sorttasks formats period start end)
  "Valid attributes for Taskjuggler reports.
If one of these appears as a property for a headline, it will be
exported with the corresponding report."


@@ 446,6 460,19 @@ headlines and their associated ID."
          (cons resource id)))
      info)))

(defun org-taskjuggler-assign-account-ids (accounts info)
  "Assign a unique ID to each account within ACCOUNTS.
ACCOUNTS is a list of headlines.  INFO is a plist used as a
communication channel.  Return value is an alist between
headlines and their associated ID."
  (let (ids)
    (org-element-map accounts 'headline
      (lambda (account)
        (let ((id (org-taskjuggler--build-unique-id account ids)))
          (push id ids)
          (cons account id)))
      info)))



;;; Accessors


@@ 497,11 524,10 @@ doesn't have any start date defined."
ITEM is a headline.  Return value is a string or nil if ITEM
doesn't have any end date defined."
  (let ((deadline (org-element-property :deadline item)))
    (and deadline (funcall (eval-and-compile
			     (if (fboundp 'org-format-timestamp)
				 #'org-format-timestamp
			       (with-no-warnings #'org-timestamp-format)))
			   deadline "%Y-%02m-%02d"))))
    (or
     (and deadline (org-timestamp-format deadline "%Y-%02m-%02d"))
     (and (memq 'end org-taskjuggler-valid-task-attributes)
	  (org-element-property :END item)))))





@@ 654,89 680,115 @@ Return complete project plan as a string in TaskJuggler syntax."
     (org-taskjuggler--build-project project info)
     ;; 3. Insert global properties.
     (org-element-normalize-string org-taskjuggler-default-global-properties)
     ;; 4. Insert resources.  Provide a default one if none is
     ;; 3.5. Insert accounts.  Provide a default one if none is
     ;;    specified.
     (let ((main-resources
     (let ((main-accounts
            ;; Collect contents from various trees marked with
            ;; `org-taskjuggler-resource-tag'.  Only gather top level
            ;; resources.
            ;; `org-taskjuggler-account-tag'.  Only gather top level
            ;; accounts.
            (apply 'append
                   (org-element-map tree 'headline
                     (lambda (hl)
                       (and (member org-taskjuggler-resource-tag
                       (and (member org-taskjuggler-account-tag
                                    (org-export-get-tags hl info))
                            (org-element-map (org-element-contents hl) 'headline
                              'identity info nil 'headline)))
                     info nil 'headline))))
       ;; Assign a unique ID to each resource.  Store it under
       ;; Assign a unique ID to each account.  Store it under
       ;; `:taskjuggler-unique-ids' property in INFO.
       (setq info
             (plist-put info :taskjuggler-unique-ids
                        (org-taskjuggler-assign-resource-ids
                         main-resources info)))
                        (org-taskjuggler-assign-account-ids
                         main-accounts info)))
       (concat
        (if main-resources
        (if main-accounts
            (mapconcat
             (lambda (resource) (org-taskjuggler--build-resource resource info))
             main-resources "")
          (format "resource %s \"%s\" {\n}\n" (user-login-name) user-full-name))
        ;; 5. Insert tasks.
        (let ((main-tasks
               ;; If `org-taskjuggler-keep-project-as-task' is
               ;; non-nil, there is only one task.  Otherwise, every
               ;; direct children of PROJECT is a top level task.
               (if org-taskjuggler-keep-project-as-task (list project)
                 (or (org-element-map (org-element-contents project) 'headline
                       'identity info nil 'headline)
                     (error "No task specified")))))
          ;; Assign a unique ID to each task.  Add it to
          ;; `:taskjuggler-unique-ids' property in INFO.
          (setq info
                (plist-put info :taskjuggler-unique-ids
                           (append
                            (org-taskjuggler-assign-task-ids main-tasks info)
                            (plist-get info :taskjuggler-unique-ids))))
          ;; If no resource is allocated among tasks, allocate one to
          ;; the first task.
          (unless (org-element-map main-tasks 'headline
                    (lambda (task) (org-element-property :ALLOCATE task))
                    info t)
            (org-element-put-property
             (car main-tasks) :ALLOCATE
             (or (org-taskjuggler-get-id (car main-resources) info)
                 (user-login-name))))
          (mapconcat
           (lambda (task) (org-taskjuggler--build-task task info))
           main-tasks ""))
        ;; 6. Insert reports.  If no report is defined, insert default
        ;;    reports.
        (let ((main-reports
             (lambda (account) (org-taskjuggler--build-account account info))
             main-accounts "")
          (format "account %s \"%s\" {\n}\n" (user-login-name) user-full-name))
        ;; 4. Insert resources.  Provide a default one if none is
        ;;    specified.
        (let ((main-resources
               ;; Collect contents from various trees marked with
               ;; `org-taskjuggler-report-tag'.  Only gather top level
               ;; reports.
               ;; `org-taskjuggler-resource-tag'.  Only gather top level
               ;; resources.
               (apply 'append
                      (org-element-map tree 'headline
                        (lambda (hl)
                          (and (member org-taskjuggler-report-tag
                          (and (member org-taskjuggler-resource-tag
                                       (org-export-get-tags hl info))
                               (org-element-map (org-element-contents hl)
                                   'headline 'identity info nil 'headline)))
                               (org-element-map (org-element-contents hl) 'headline
                                 'identity info nil 'headline)))
                        info nil 'headline))))
          (if main-reports
              (mapconcat
               (lambda (report) (org-taskjuggler--build-report report info))
               main-reports "")
	    ;; insert title in default reports
	    (let* ((title (org-export-data (plist-get info :title) info))
		   (report-title (if (string= title "")
				     (org-taskjuggler-get-name project)
				   title)))
	      (mapconcat
	       'org-element-normalize-string
	       (mapcar
		(lambda (report)
		  (replace-regexp-in-string "%title" report-title  report t t))
		org-taskjuggler-default-reports) "")))))))))
          ;; Assign a unique ID to each resource.  Store it under
          ;; `:taskjuggler-unique-ids' property in INFO.
          (setq info
                (plist-put info :taskjuggler-unique-ids
                           (org-taskjuggler-assign-resource-ids
                            main-resources info)))
          (concat
           (if main-resources
               (mapconcat
                (lambda (resource) (org-taskjuggler--build-resource resource info))
                main-resources "")
             (format "resource %s \"%s\" {\n}\n" (user-login-name) user-full-name))
           ;; 5. Insert tasks.
           (let ((main-tasks
                  ;; If `org-taskjuggler-keep-project-as-task' is
                  ;; non-nil, there is only one task.  Otherwise, every
                  ;; direct children of PROJECT is a top level task.
                  (if org-taskjuggler-keep-project-as-task (list project)
                    (or (org-element-map (org-element-contents project) 'headline
                          'identity info nil 'headline)
                        (error "No task specified")))))
             ;; Assign a unique ID to each task.  Add it to
             ;; `:taskjuggler-unique-ids' property in INFO.
             (setq info
                   (plist-put info :taskjuggler-unique-ids
                              (append
                               (org-taskjuggler-assign-task-ids main-tasks info)
                               (plist-get info :taskjuggler-unique-ids))))
             ;; If no resource is allocated among tasks, allocate one to
             ;; the first task.
             (unless (org-element-map main-tasks 'headline
                       (lambda (task) (org-element-property :ALLOCATE task))
                       info t)
               (org-element-put-property
                (car main-tasks) :ALLOCATE
                (or (org-taskjuggler-get-id (car main-resources) info)
                    (user-login-name))))
             (mapconcat
              (lambda (task) (org-taskjuggler--build-task task info))
              main-tasks ""))
           ;; 6. Insert reports.  If no report is defined, insert default
           ;;    reports.
           (let ((main-reports
                  ;; Collect contents from various trees marked with
                  ;; `org-taskjuggler-report-tag'.  Only gather top level
                  ;; reports.
                  (apply 'append
                         (org-element-map tree 'headline
                           (lambda (hl)
                             (and (member org-taskjuggler-report-tag
                                          (org-export-get-tags hl info))
                                  (org-element-map (org-element-contents hl)
                                      'headline 'identity info nil 'headline)))
                           info nil 'headline))))
             (if main-reports
                 (mapconcat
                  (lambda (report) (org-taskjuggler--build-report report info))
                  main-reports "")
	       ;; insert title in default reports
	       (let* ((title (org-export-data (plist-get info :title) info))
		      (report-title (if (string= title "")
				        (org-taskjuggler-get-name project)
				      title)))
	         (mapconcat
	          'org-element-normalize-string
	          (mapcar
		   (lambda (report)
		     (replace-regexp-in-string "%title" report-title  report t t))
		   org-taskjuggler-default-reports) "")))))))))))

(defun org-taskjuggler--build-project (project info)
  "Return a project declaration.


@@ 799,20 851,60 @@ neither is defined a unique id will be associated to it."
   ;; Closing resource.
   "}\n"))

(defun org-taskjuggler--build-account (account info)
  "Return a account declaration.

ACCOUNT is a headline.  INFO is a plist used as a communication
channel.

All valid attributes from ACCOUNT are inserted.  If ACCOUNT
defines a property \"account_id\" it will be used as the id for
this account.  Otherwise it will use the ID property.  If
neither is defined a unique id will be associated to it."
  (concat
   ;; Opening account.
   (format "account %s \"%s\" {\n"
           (org-taskjuggler--clean-id
            (or (org-element-property :ACCOUNT_ID account)
                (org-element-property :ID account)
                (org-taskjuggler-get-id account info)))
           (org-taskjuggler-get-name account))
   ;; Add attributes.
   (org-taskjuggler--indent-string
    (org-taskjuggler--build-attributes
     account org-taskjuggler-valid-account-attributes))
   ;; Add inner accounts.
   (org-taskjuggler--indent-string
    (mapconcat
     'identity
     (org-element-map (org-element-contents account) 'headline
       (lambda (hl) (org-taskjuggler--build-account hl info))
       info nil 'headline)
     ""))
   ;; Closing account.
   "}\n"))

(defun org-taskjuggler--build-report (report info)
  "Return a report declaration.
REPORT is a headline.  INFO is a plist used as a communication
channel."
  (concat
   ;; Opening report.
   (format "%s \"%s\" {\n"
   (format "%s %s \"%s\" {\n"
           (or (org-element-property :REPORT_KIND report) "taskreport")
           (or (org-element-property :REPORT_ID report)
                (org-element-property :ID report)
                (org-taskjuggler-get-id report info))
           (org-taskjuggler-get-name report))
   ;; Add attributes.
   (org-taskjuggler--indent-string
    (org-taskjuggler--build-attributes
     report org-taskjuggler-valid-report-attributes))
   ;; Add inner reports.
   ;; Add core of report, ie the first paragraph after the headline
   ;; and before the next sub-headline
   (format "%s" (if (eq (org-element-type (car (org-element-contents report))) 'section)
                    (org-element-interpret-data (car (org-element-contents report)))
                  ""))
   (org-taskjuggler--indent-string
    (mapconcat
     'identity