The TaskJuggler Manual | ||
---|---|---|
Prev | Chapter 4. Tutorial: Your First Project | Next |
Let's focus on the real work now. The project should solve a problem: the creation of an accounting software. Because the job is quite complicated, we break it down into several subtasks. We need to do a specification, develop the software, test the software, and write a manual. In TaskJuggler syntax this would look as follows:
task AcSo "Accounting Software" { task spec "Specification" task software "Software Development" task test "Software testing" task deliveries "Milestones" }
Similar to resources, tasks are declared by using the task keyword followed by an ID and a name string. All TaskJuggler properties have their own namespaces. This means, that it is quite OK to have a resource and a task with the same ID. Tasks may have optional attributes which can be tasks again, so tasks can be nested. In contrast to all other TaskJuggler properties, task IDs inherit the ID of the enclosing task as a prefix to the ID. The full ID of the spec task is AcSo.spec.
To track important milestones of the project, we also added a task called Milestones. This task, like most of the other tasks will get some subtasks later on. We consider the specification task simple enough, so we don't have to break it into further subtasks. So let's add some more details to it.
task spec "Specification" { effort 20d ${allocate_developers} depends !deliveries.start }
The effort to complete the task is specified with 20 man-days. Alternatively we could have used the length attribute or the duration attribute. length specifies the duration of the task in working days while duration specifies the duration in calendar days. Contrary to effort, these two don't have to have a specification of the involved resources. Since effort specifies the duration in man-days, we need to say who should be allocated to the task. The task won't finish before the resources could be allocated long enough to reach the specified effort. Tasks with length or duration criteria and allocated resources will last exactly as long as requested. Resources will be allocated only if available.
Here we use the allocate_developers macro mentioned above. The expression
${allocate_developers}is simply expanded to
allocate dev1 allocate dev2 { limits { dailymax 4h } } allocate dev3
If you need to allocate the same bunch of people to several tasks, the macro saves you some typing. You could have written the allocate attributes directly instead of using the macro. Since the allocation of multiple resources to a task is a very common place for macro usage, we found it a good idea to use it in this example as well.
One more interesting thing to note is the fact that we like the resource dev2 only to work 50% of the day on this task, so we use the optional attribute limits to specify this.
For TaskJuggler to schedule a task, it needs to know either the start and end criteria of a task, or one of them and a duration specification. The start and end criteria can either be fixed dates or relative dates. Relative dates are specifications of the type "task B starts after task A has finished". Or in other words, task B depends on task A. In this example the spec task depends on a subtasks of the deliveries task. We have not specified it yet, but it has the local ID start.
To specify the dependency between the two tasks, we use the depends attribute. This attribute must be followed by one or more task IDs. If more than one ID is specified, each ID has to be separated with a comma from the previous one. Task IDs can be either absolute IDs or relative IDs. An absolute ID of a task is the ID of this task prepended by the IDs of all enclosing tasks. The task IDs are separated by a dot from each other. The absolute ID of the specification task would be AcSo.spec.
Relative IDs always start with one or more exclamation marks. Each exclamation mark moves the scope to the next enclosing task. So !deliveries.start is expanded to AcSo.deliveries.start since AcSo is the enclosing task of deliveries. Relative task IDs are a little bit confusing at first, but have a real advantage over absolute IDs. Sooner or later you want to move tasks around in your project and then it's a lot less likely that you have to fix dependency specifications of relative IDs.
The software development task is still too complex to specify it directly. So we split it further into subtasks.
task software "Software Development" { priority 1000 task database "Database coupling" task gui "Graphical User Interface" task backend "Back-End Functions" }
We use the priority attribute to mark the importance of the tasks. 500 is the default priority of top-level tasks. Setting the priority to 1000 marks the task as most important task, since the possible range is 1 (not important at all) to 1000 (ultimately important). priority is an attribute that is handed down to subtasks if specified before the subtasks' declaration. So all subtasks of software have a priority of 1000 as well, unless they have their own priority definition.
task database "Database coupling" { effort 20d depends !!spec allocate dev1, dev2 }
The work on the database coupling should not start before the specification has been finished. So we again use the depends attribute to let TaskJuggler know about this. This time we use two exclamation marks for the relative ID. The first one puts us in the scope of the enclosing software task. The second one is to get into the AcSo scope that contains the spec tasks. This time we allocate resources directly without using a macro.
task gui "Graphical User Interface" { effort 35d delayed:effort 40d depends !database, !backend allocate dev2, dev3 }
TaskJuggler can schedule your project for two different scenarios. We have called the first scenario "plan" scenario and the second "delayed" scenario. Many of the reports allow you to put the values of both scenarios side by side to each other, so you can compare the two scenarios. All scenario-specific values that are not explicitly stated for the delayed scenario are taken from the plan scenario. So the user only has to specify the values that differ in the delayed scenario. The two scenarios must have the same task structure and the same dependencies. But the start and end dates of tasks as well as the duration may vary. In the example we have planned the work on the graphical user interface to be 35 man-days. It turned out that we actually needed 40 man-days. By prefixing the start effort attribute with delayed:, the effort value for the delayed scenario can be specified.
task backend "Back-End Functions" { effort 30d complete 95 depends !database, !!spec allocate dev1 allocate dev2 }
By default, TaskJuggler assumes that all tasks are on schedule. Sometimes you want to generate reports that show how much of a task has actually been completed. TaskJuggler uses the current date for this, unless you have specified another date using the now attribute. If a task is ahead of schedule or late, this can be specified using the complete attribute. This specifies how many percent of the task have been completed up to the current date. In our case the back-end implementation is slightly ahead of schedule as we will see from the report.
task test "Software testing" { task alpha "Alpha Test" { effort 1w depends !!software allocate test, dev2 } task beta "Beta Test" { effort 4w depends !alpha allocate test, dev1 } }
The software testing task has been split up into an alpha and a beta test task. The interesting thing here is, that efforts can not only be specified as man-days, but also man-weeks, man-hours, etc. By default, TaskJuggler assumes a man-week is 40 man-hours or 5 man-days. These values can be changed using the dailyworkinghours attribute.
Let's go back to the outermost task again. At the beginning of the example we stated that we want to credit all development work to one account with ID dev and all documentation work to the account doc. To achieve this, we use the attribute account to credit all tasks to the dev account.
task AcSo "Accounting Software" { account dev task software "Software Development" {
Since we specify the attribute for the top-level task before we declare any subtasks, this attribute will be inherited by all subtasks and their subtasks and so on. The only exception is the writing of the manual. We need to change the account for this task again, as it is also a subtask of AcSo.
task manual "Manual" { effort 10w depends !deliveries.start allocate doc, dev3 account doc }