emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Eric S Fraga <ucecesf@ucl.ac.uk>
To: emacs-orgmode@gnu.org
Subject: Re: Re: TaskJuggler 3, revisited
Date: Thu, 14 Oct 2010 07:24:19 +0100	[thread overview]
Message-ID: <87ocaxxpf0.wl%ucecesf@ucl.ac.uk> (raw)
In-Reply-To: <20101009130446.1cd88b6e@rechenknecht.site>

[-- Attachment #1: Type: text/plain, Size: 799 bytes --]

For the recent project I had to prepare, I did have to generate a
GANTT chart.  I tried using TJ2 via org but found the current
implementation not suitable for my way of working.  Instead of using
TJ, either 2 or 3, I simply created my own chart using tikz in latex.
To make my life easier, I created a number of latex commands for this.

I've now developed some code which will take an org table and create a
GANTT chart.  Some of you may find this useful so I've attached an org
document with all the codes (emacs lisp + latex) along with an
example.  I've also attached a PDF so you can see what I get.

The codes are not necessarily easily customisable so they may not do
what you want.  For the types of project proposals I have to prepare,
the level of detail I have here is sufficient.

eric

[-- Attachment #2: Type: text/plain, Size: 10320 bytes --]

#+TITLE:     GANTT charts with org-babel
#+AUTHOR:    Eric S Fraga
#+EMAIL:     e.fraga@ucl.ac.uk
#+DATE:      \url{$Revision: 1.12 $}
#+LASTCHANGE:      2010.10.13 21:08:08
#+DESCRIPTION: 
#+KEYWORDS: 
#+LANGUAGE:  en
#+OPTIONS:   H:3 num:nil toc:nil \n:nil @:t ::t |:t ^:t -:t f:t *:t <:t
#+OPTIONS:   TeX:t LaTeX:t skip:nil d:nil todo:t pri:nil tags:not-in-toc
#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
#+EXPORT_SELECT_TAGS: export
#+EXPORT_EXCLUDE_TAGS: noexport
#+LINK_UP:   
#+LINK_HOME: 

#+latex_header: \usepackage{pgf, pgfarrows, pgfnodes}
#+latex_header: \usepackage{tikz}

#+latex_header: \newcommand\ganttline[4]{% line, tag, start end
#+latex_header:   \node at (0,#1) [anchor=base east] {#2};
#+latex_header:   \fill[black!20!white] (#3/20,#1-.2) rectangle (#4/20,#1+.2);}

* table of tasks and milestones

  The following table describes the tasks and other relevant
  information for a project.  Each line is an /entry/ and there are
  three types of entry allowed in this table:

  - task :: an actual task that has a start time, a duration and an end time.
  - milestone :: a specific milestone in the project that has a start
               time alone
  - date :: a point in time that will be drawn as a vertical line in the
          GANTT chart (e.g. start of each year).

  Each element of the chart will be annotated with the content of the
  /label/ column of each entry.  The first column of the table is
  ignored but I use it to number the entries.  The last column, titled
  /align/, is used to determine where to place the /activity/ text for
  tasks, whether to the left or right of the bar or, if nothing is
  specified, centred within the bar itself.

#+tblname: gantttesttable
    |    | type      | label | activity      | depends | start | duration | end | align |
    |----+-----------+-------+---------------+---------+-------+----------+-----+-------|
    |  1 | date      | Start |               |         |     0 |          |   0 |       |
    |  2 | task      | 1.1   | Lit survey    |         |     0 |        3 |   3 | right |
    |  3 | task      | 1.2   | Develop model |       2 |     3 |        9 |  12 | right |
    |  4 | milestone | M1    | model         |       3 |    12 |          |  12 |       |
    |  5 | task      | 1.3   | Implement     |       3 |    12 |        6 |  18 | left  |
    |  6 | date      | Y1    |               |         |    12 |          |  12 |       |
    |  7 | milestone | M2    | software      |       5 |    18 |          |  18 |       |
    |----+-----------+-------+---------------+---------+-------+----------+-----+-------|
    |  8 | task      | 2.1   | Surrogate     |       3 |    15 |        6 |  21 | left  |
    |  9 | task      | 2.2   | Implement     |       7 |    21 |        3 |  24 | left  |
    | 10 | milestone | M3    | software      |       8 |    24 |          |  24 |       |
    | 11 | date      | End   |               |         |    24 |          |  24 |       |
    |----+-----------+-------+---------------+---------+-------+----------+-----+-------|
    #+TBLFM: $1=1+@-1::$8=$6+$7::@5$6=@-1$+2::@6$6=@-1$+2::@8$6=@-2$+2::@9$6=3+@4$+2::@10$6=@-1$+2

* some latex code for gantt charts

The following \LaTeX{} code is used to generate the actual elements of
the GANTT chart.  The three colours used, to indicate /tasks/,
/milestones/ and /date/ lines, can be change to suit your own tastes.  All
the rest of the code is not meant to be changed.  Ideally, this code
should be in a separate /style/ file (see [[actions][list of actions below]]) and an
environment should be defined.  As it stands, the code can only be
used once within a document unless the =gantttask= counter is reset
manually.

#+latex_header: \usepackage{tikz}

#+begin_latex
\newcounter{gantttask}
\newcommand{\gantttaskcolour}{blue!50!white}
\newcommand{\ganttmilestonecolour}{red!50!white}
\newcommand{\ganttdatelinecolour}{black!50!white}
\newcommand{\ganttprojecttime}{1}
\newcommand{\ganttntasks}{1}
\newcommand\gantttask[6]{% label, activity, start, end, labelpos, align
  \begin{pgfonlayer}{foreground}
    % \node at (0,\thegantttask) [anchor=base east] {#2};
    \stepcounter{gantttask}
    \node [left] at (0,\thegantttask) {#1};
    \draw[fill=\gantttaskcolour] (#3/\ganttprojecttime,\thegantttask-0.4) rectangle (#4/\ganttprojecttime,\thegantttask +0.4);
    \node at (#5/\ganttprojecttime,\thegantttask) #6 {#2};
  \end{pgfonlayer}
}
\newcommand\ganttpoint[3]{% line, tag, date
  \node at (0,#1) [anchor=base east] {#2};
  \fill[black] (#3/\ganttprojecttime,#1) circle (0.1/\ganttprojecttime);
}
\newcommand\ganttdateline[2]{% tag, date
  \begin{pgfonlayer}{background}
    \draw[\ganttdatelinecolour] (#2/\ganttprojecttime,0) -- (#2/\ganttprojecttime,\thegantttask+1);
    \node at (#2/\ganttprojecttime,0) [above] {#1};
  \end{pgfonlayer}
}
\newcommand\ganttmilestone[2]{% tag, date
  \begin{pgfonlayer}{foreground}
    \node at (#2/\ganttprojecttime,\thegantttask+1.0) [below] {#1};
    \draw[black,fill=\ganttmilestonecolour] (#2/\ganttprojecttime-0.1\ganttntasks/\ganttprojecttime,\thegantttask+0.9) rectangle (#2/\ganttprojecttime+0.1\ganttntasks/\ganttprojecttime,\thegantttask+1.1);
  \end{pgfonlayer}
}

#+END_LaTeX

* lisp code for creating latex that uses above			   :noexport:
#+source: elispgantt
#+begin_src emacs-lisp :var table=gantttesttable :results output raw :exports results
(defun esf/generate-gantt-chart (table)
  (let ((dates "")
	(entries (nthcdr 2 table))
	(milestones "")
	(nmilestones 0)
	(ntasks 0)
	(projecttime 0)
	(tasks "")
	(xlength 1)
	)
    (message "Initial: %s\n" table)
    (message "Entries: %s\n" entries)
    (while entries
      (let ((entry (first entries)))
	(if (listp entry)
	    (let ((id (first entry))
		  (type (nth 1 entry))
		  (label (nth 2 entry))
		  (task (nth 3 entry))
		  (dependencies (nth 4 entry))
		  (start (nth 5 entry))
		  (duration (nth 6 entry))
		  (end (nth 7 entry))
		  (alignment (nth 8 entry))
		  )
	      (if (> start projecttime) (setq projecttime start))
	      (if (string= type "task")
		  (let ((end (+ start duration))
			(textposition (+ start (/ duration 2)))
			(flush "")
			)
		    (if (string= alignment "left")
			(progn
			  (setq textposition start)
			  (setq flush "[left]"))
		      (if (string= alignment "right")
			  (progn
			    (setq textposition end)
			    (setq flush "[right]"))
			)
		      )
		    (setq tasks (format "%s  \\gantttask{%s}{%s}{%d}{%d}{%d}{%s}\n" tasks label task start end textposition flush))
		    (setq ntasks (+ 1 ntasks))
		    (if (> end projecttime)
			(setq projecttime end))
		    )
		(if (string= type "milestone")
		    (progn
		      (setq milestones (format "%s  \\ganttmilestone{$\\begin{array}{c}\\mbox{%s}\\\\ \\mbox{%s}\\end{array}$}{%d}\n" milestones label task start))
		      (setq nmilestones (+ 1 nmilestones)))
		  (if (string= type "date")
		      (setq dates (format "%s  \\ganttdateline{%s}{%d}\n" dates label start))
		    (message "Ignoring entry with type %s\n" type)
		    )
		  )
		)
	      )
	  (message "Ignoring non-list entry %s\n" entry)
	  ) ; end if list entry
	(setq entries (cdr entries))
	)
      ) ; end while entries left
    (format "#+begin_latex
\\pgfdeclarelayer{background}
\\pgfdeclarelayer{foreground}
\\pgfsetlayers{background,foreground}
\\renewcommand{\\ganttprojecttime}{%d}
\\renewcommand{\\ganttntasks}{%d}
\\noindent
\\begin{tikzpicture}[y=-0.75cm,x=0.75\\textwidth]
  \\begin{pgfonlayer}{background}
    \\draw[very thin, red!10!white] (0,1+\\ganttntasks) grid [ystep=0.75cm,xstep=1/\\ganttprojecttime] (1,0);
    \\draw[\\ganttdatelinecolour] (0,0) -- (1,0);
    \\draw[\\ganttdatelinecolour] (0,1+\\ganttntasks) -- (1,1+\\ganttntasks);
  \\end{pgfonlayer}
%s
%s
%s
\\end{tikzpicture}
,#+end_latex\n" projecttime ntasks tasks milestones dates)
    )
  )
(esf/generate-gantt-chart table)
#+end_src

#+   \\draw[help lines, very thin, red!20!white] (0,%s) grid (\\ganttprojecttime,0);
* The GANTT chart

The following GANTT chart is generated by the =emacs-lisp= code in the
section immediately above this one in the =org= file, a section that has
not been exported.

#+results: elispgantt
#+begin_latex
\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,foreground}
\renewcommand{\ganttprojecttime}{24}
\renewcommand{\ganttntasks}{5}
\noindent
\begin{tikzpicture}[y=-0.75cm,x=0.75\textwidth]
  \begin{pgfonlayer}{background}
    \draw[very thin, red!10!white] (0,1+\ganttntasks) grid [ystep=0.75cm,xstep=1/\ganttprojecttime] (1,0);
    \draw[\ganttdatelinecolour] (0,0) -- (1,0);
    \draw[\ganttdatelinecolour] (0,1+\ganttntasks) -- (1,1+\ganttntasks);
  \end{pgfonlayer}
  \gantttask{1.1}{Lit survey}{0}{3}{3}{[right]}
  \gantttask{1.2}{Develop model}{3}{12}{12}{[right]}
  \gantttask{1.3}{Implement}{12}{18}{12}{[left]}
  \gantttask{2.1}{Surrogate}{15}{21}{15}{[left]}
  \gantttask{2.2}{Implement}{21}{24}{21}{[left]}

  \ganttmilestone{$\begin{array}{c}\mbox{M1}\\ \mbox{model}\end{array}$}{12}
  \ganttmilestone{$\begin{array}{c}\mbox{M2}\\ \mbox{software}\end{array}$}{18}
  \ganttmilestone{$\begin{array}{c}\mbox{M3}\\ \mbox{software}\end{array}$}{24}

  \ganttdateline{Start}{0}
  \ganttdateline{Y1}{12}
  \ganttdateline{End}{24}

\end{tikzpicture}
#+end_latex


# <<actions>>
* TODO [0/5] outstanding issues or actions
  - [ ] encapsulate latex code within a package
  - [ ] figure out how to make indirect references in org's table
  - [ ] using org-store-link on a section like this one stores the
    special directive, in square brackets, as part of the link
    information but, of course, this changes as items are ticked off
    so the heading changes.
  - [ ] there seems to be some problem with indentation of description
    lists such as the one in the first section of this document.
  - [ ] although the export procedure prompts for whether the emacs
    lisp code should be executed, answering yes doesn't actually
    update the results section.  The code is executed (see *Messages*
    buffer).  The updating might not be happening because the code is
    within a section that is not going to be exported or because there
    is no export directive on the code?  I should try playing with
    =call= invocations of babel code.

[-- Attachment #3: gantt.pdf --]
[-- Type: application/pdf, Size: 81505 bytes --]

[-- Attachment #4: Type: text/plain, Size: 75 bytes --]

-- 
Eric S Fraga
GnuPG: 8F5C 279D 3907 E14A 5C29  570D C891 93D8 FFFC F67D

[-- Attachment #5: Type: text/plain, Size: 201 bytes --]

_______________________________________________
Emacs-orgmode mailing list
Please use `Reply All' to send replies to the list.
Emacs-orgmode@gnu.org
http://lists.gnu.org/mailman/listinfo/emacs-orgmode

  parent reply	other threads:[~2010-10-14  7:19 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-10-09  1:24 TaskJuggler 3, revisited John Hendy
2010-10-09 11:04 ` Detlef Steuer
2010-10-09 19:25   ` John Hendy
2010-10-14  6:24   ` Eric S Fraga [this message]
2010-10-18 14:54     ` Christian Egli
2010-10-18 17:46       ` Eric S Fraga
2010-10-09 13:00 ` Anthony Lander
2010-10-18 14:52   ` Christian Egli
2010-10-18 15:45     ` Louis Turk
2010-10-18 16:55     ` Anthony Lander
     [not found]     ` <98BDDE23-2AA0-4B2A-B089-688F7BC46A49@yahoo.com>
2010-10-19  9:29       ` Christian Egli
     [not found]         ` <0EBF5950-179C-44CC-958C-EB9CF06A44AF@yahoo.com>
2010-10-28  7:28           ` Christian Egli
2010-10-29 15:07 ` Christian Egli

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87ocaxxpf0.wl%ucecesf@ucl.ac.uk \
    --to=ucecesf@ucl.ac.uk \
    --cc=e.fraga@ucl.ac.uk \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).