Python interface to TaskJuggler 3.6 to import, run, export the plan
Unix: Windows:
Metrics:
Usage:
It's [whatever current year] and still most of the tasks/project management tools lack support for any means of automated planning. This library helps to integrate automated planner that's been available for over a decade, with a shot of suporting different front-ends, complex scheduling strategies and potentially different planners.
Realize your craziest time management dreams!
python_taskjuggler
module provides python interfaces to TaskJuggler 3 planner. It is a set of base classes that provide object abstractions that TaskJuggler internally uses. The module comes with example implementation of JSON project description parser.
It is still work in progress and currently supports:
"me"
The package comes with an example command line utility tjp-client
that provides automatic planning for
tasks defined as records in airtable table.
Working with google sheets, jira, trello, todoist, smartsheet and others could be implemented the same way.
The utility allows to immediately re-schedule to reflect any changes to the plans that may arise due to new fixed appointments, dependencies, priority amendments or required efforts re-evaluation.
$ tjp-client -a airtable -k <airtable_api_key> -b <airtable_database> -t <table_name> -v <view_name>
1. id - the integer (number) field used as task ID. "Auto Number" type recommended.
2. summary - single line text: task summary / title
3. effort - integer number: task effort duration measured in hours. Default value of 1 recommended
4. priority - single select: field with values "CRITICAL", "High", "Low", "info"
5. preference - integer number: optional additional number 0-99 for higher granularity of priorities
6. depends - single line text: with dependencies listed as id's, like: 2,3,4
7. appointment - date field with time: the fixed tasks or appointments that can not be moved
8. deadline - date: the desired deadline value. Current strategy will use it to emphasize priority if missed.
9. booking - date+time: this is where output will be written to. Sort your table by this column
Other columns are just ignored.
You can add nice calculations to the table like time difference between deadline and calculated booking.
Work
with all the tasks with status "Done" filtered out (it is left as an exercize for the reader to create a new column and a filter for it)
booking
fieldAPI key
, database ID
, note your table name and view name should be Tasks
and Work
respectively$ tjp-client -a airtable -k keyAnIuVcuhhkeAkc -b appA8ZtLosVV7HGXy -t Tasks -v Work
taskjuggler_python
moving your tasks around!Now try changing priorities, adding appointments and re-scheduling the plan.
Install TaskJuggler with gem:
$ gem install taskjuggler
Install taskjuggler_python with pip:
$ pip install taskjuggler_python
or directly from the source code:
$ git clone https://github.com/grandrew/taskjuggler-python.git
$ cd taskjuggler-python
$ python setup.py install
Basic usage concepts include:
Task
, referred to as issue
throughout the code
id
which is used to identify and map the tasks - a property of JugglerTask
instanceeffort
measured in units set as UNIT
class attribute of JugglerTaskEffort
start
date (a.k.a. fixed appointment)priority
measured as interger 0-1000
to set scheduling preference. No priority is scheduled always first.JugglerTask
's property object(s)The minimal invocation will look like:
>>> from taskjuggler_python import juggler
>>> jg = juggler.GenericJuggler()
>>> t = juggler.JugglerTask()
>>> t.set_property(juggler.JugglerTaskEffort(1)) # hours by default
>>> jg.add_task(t)
>>> jg.run()
>>> t.walk(juggler.JugglerBooking)[0].decode()
[datetime.datetime(2017, 10, 12, 13, 0, tzinfo=<UTC>), datetime.datetime(2017, 10, 12, 14, 0, tzinfo=<UTC>)]
$ python
>>> from taskjuggler_python import jsonjuggler
>>> my_tasks = """[
{
"id": 2,
"depends": [
1
],
"allocate": "me",
"effort": 1.2
},
{
"id": 1,
"effort": 3,
"allocate": "me",
"summary": "test"
}
]"""
>>> jg = jsonjuggler.JsonJuggler(my_tasks)
>>> jg.run()
>>> jg.toJSON()
[
{
"allocate": "me",
"booking": "2017-10-10T11:00:00+00:00",
"depends": [
1
],
"effort": 1.2,
"id": 2
},
{
"allocate": "me",
"booking": "2017-10-10T08:00:00+00:00",
"effort": 3,
"id": 1,
"summary": "test"
}
]
As an example, let's create interface to automatically schedule tasks that are defined as airtable records using Airtable API wrapper:
We are using the fact that airtable's API already emits nicely formatted JSON in fields
field.
We only have to name the table columns with correct field names that jsonjuggler example wrapper expects
from airtable import Airtable
from taskjuggler_python import juggler, jsonjuggler
airtable = Airtable("appA8ZtLosVV7HGXy", "Tasks", api_key="keyAnIuVcuhhkeAkc")
# use DictJuggler example wrapper from jsonjuggler module, directly feed what the API emits in "fields"
JUGGLER = jsonjuggler.DictJuggler([x["fields"] for x in airtable.get_all(view="Work")])
# run taskjuggler and calculate bookings
JUGGLER.run()
# walk through all tasks objects
for t in JUGGLER.walk(juggler.JugglerTask):
airtable.update_by_field("id", t.get_id(),
{"booking": t.walk(juggler.JugglerBooking)[0].decode()[0].isoformat()})
# the last line finds first booking in this task, decodes it to datatime list and encodes to isoformat
After executing this code you should have time assigned to all of your tasks, none of them overlapping, respecting dependencies, taking into account default time shifts, appointments and no overwork allowed.
Imagine that you want your older tasks to increase their percieved priority so that every task with any priority level gets a chance to be scheduled in the foreseeable future:
# recalculate JSON issue priorities based on deadlines
for rec in json_issues:
if "priority" in rec and "deadline" in rec and not rec["priority"] >= 300:
diff_days = (datetime.datetime.now() - dateutil.parser.parse(rec["deadline"])).days
if diff_days < 0: diff_days = 0 # deny lowering priority
rec["priority"] += diff_days * 3 # after 30 days priority is up by 90
if rec["priority"] >= 250: # limit maximum percieved priority
rec["priority"] = 250 # > 300 is critical in our strategy
You can find the fully working example here.
See code for more examples of how to use the interfaces.
X = effort; limits { maximum Xh }
??), split punishingpython-planner
package?)