HOME/Articles/

matplotlib example buildGantt (snippet)

Article Outline

Python matplotlib example 'buildGantt'

Functions in program:

  • def create_gantt_chart(taskList, outFileName):
  • def get_task_list(AreaToChart):
  • def _create_date(datetxt):
  • def make_data_string(task):
  • def get_date_string(ts):

Modules used in program:

  • import os
  • import sqlite3
  • import datetime
  • import numpy as np
  • import matplotlib.dates
  • import matplotlib.font_manager as font_manager
  • import matplotlib.pyplot as plt
  • import datetime as dt

python buildGantt

Python matplotlib example: buildGantt

# Tom Foutz
# 2/19/2018
# Adapted from:
# ThingsCommandline Interface Arjan van der Gaag and Alexander Willner
#   https://github.com/AlexanderWillner/things.sh
# Quick Gantt Chart with Matplotlib by sukhbinder
#   https://sukhbinder.wordpress.com/2016/05/10/quick-gantt-chart-with-matplotlib/

import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.font_manager as font_manager
import matplotlib.dates
from matplotlib.dates import WEEKLY,MONTHLY, DateFormatter, rrulewrapper, RRuleLocator 
import numpy as np
import datetime
import sqlite3
import os

def get_date_string(ts):
    if ts: return datetime.datetime.utcfromtimestamp(ts).strftime('%d-%m-%Y')
    else: return None

def make_data_string(task):
    if (not task[2]) and (task[4] == 2):
        return None
    return (task[0],get_date_string(task[2]),get_date_string(task[3]),3)

def _create_date(datetxt):
    """Creates the date"""
    if datetxt:
        day,month,year=datetxt.split('-')
        date = dt.datetime(int(year), int(month), int(day))
        mdate = matplotlib.dates.date2num(date) 
        return mdate
    else:
        return None

def get_task_list(AreaToChart):
    fnameTemp = "~/Library/Containers/com.culturedcode.ThingsMac/Data/Library/Application Support/Cultured Code/Things/Things.sqlite3" 
    fname = os.path.expanduser(fnameTemp)

    fd = os.open(fname, os.O_RDONLY)
    c = sqlite3.connect('/dev/fd/%d' % fd)
    os.close(fd)    
    taskList = []
    projId = c.execute("""SELECT uuid FROM TMArea WHERE title = "%s";""" % AreaToChart).fetchall()[0][0]
    researchProjects=c.execute("""SELECT title,uuid,startDate,dueDate,start FROM TMTask WHERE trashed = 0 AND status = 0 AND type = 1 AND area = '%s' ORDER BY startdate,dueDate;""" % projId).fetchall()

    ## Primary Project SubTasks 
    for researchProject in researchProjects:
        researchProjectTitle = researchProject[0]
        taskList.append(["%s" % researchProjectTitle,None,None,1])
        researchProjectID = researchProject[1]
        subTasks = c.execute("""SELECT title,uuid,startDate,dueDate,start FROM TMTask WHERE trashed = 0 AND status = 0 AND type = 0 AND project = '%s' ORDER BY startdate,dueDate;""" % (researchProjectID)).fetchall()
        for task in subTasks: taskList.append(make_data_string(task))
        subHeadings = c.execute("""SELECT title,uuid,start FROM TMTask WHERE trashed = 0 AND status = 0 AND type = 2 AND project = '%s' ORDER BY startdate,dueDate;""" % (researchProjectID)).fetchall()
        for subHeading in subHeadings:
            subHeadingId = subHeading[1]
            taskList.append([subHeading[0],None,None,2])
            subHeadingTasks = c.execute("""SELECT title,uuid,startDate,dueDate,start FROM TMTask WHERE trashed = 0 AND status = 0 AND type = 0 AND actionGroup = '%s' ORDER BY startdate,dueDate;""" % (subHeadingId)).fetchall()
            for task in subHeadingTasks: taskList.append(make_data_string(task))
    c.close()
    return taskList

def create_gantt_chart(taskList, outFileName):
    ylabels = []; customDates = []; entryTypes = []
    for tx in taskList:
        if tx:
            ylabel,startdate,enddate,entryType=tx
            if entryType == 1:
                heading = ''
            elif entryType == 2:
                heading = ylabel + ":"
                #print(heading)
            if entryType in [1,3]:
                ylabels.append(heading + ylabel)
                entryTypes.append(entryType)
                customDates.append([_create_date(startdate),_create_date(enddate)])
    ilen=len(ylabels)
    taskDates = {}
    for i,task in enumerate(ylabels):
        taskDates[task] = customDates[i]
    fig = plt.figure(figsize=(12.8, 13.32))
    ax = fig.add_subplot(111)
    heading = ''
    barcolors = iter(plt.get_cmap('Set1').colors)
    for i in range(len(ylabels)):
        start_date,end_date = taskDates[ylabels[i]]
        if entryTypes[i] == 1:
            barecol='k'
            barcol=barcolors.next()
            fcol='k'
            fw='bold'
            fbox = None
            end_date = np.array(customDates).max()
            if not start_date:
                c = np.array(customDates,dtype=np.float) # convert to array
                start_date = c[np.isfinite(c)].min() # select lowest date that exists
            balpha = 0.8
        elif entryTypes[i] == 3:
            if start_date:
                if not end_date: # Start Date, but no End Date
                    end_date = start_date + 1
            else:
                if end_date: # Start date but not end_date, so use start_date as end_date
                    start_date = end_date - 1
                else: # No start_date or end_date, so use today
                    start_date = end_date = matplotlib.dates.date2num(dt.datetime.now()) 
            barecol=barcol
            fw='normal'
            fbox = None
            balpha = 0.4
        ax.barh((i*0.5)+0.5, end_date - start_date, left=start_date, 
            height=0.3,
            align='center', 
            edgecolor=barecol,
            color=barcol,
            alpha = balpha)
        ax.text(start_date+0.5,(i*0.5)+0.5, heading + ylabels[i], 
            #horizontalalignment='center',
            bbox=fbox,
            verticalalignment='center',
            weight=fw,color=fcol)
    # Draw Line for Today:
    nowLine = matplotlib.dates.date2num(dt.datetime.now())
    ax.plot([nowLine,nowLine],[-0.5,0.5+ilen/2.0], "--",color="red", linewidth=0.75, alpha = 0.5)
    ax.set_ylim(ymin = -0.1, ymax = ilen*0.5+0.5)
    ax.grid(color = 'gray', linestyle = ':', linewidth=0.5, alpha = 0.5)
    ax.xaxis_date()
    rule = rrulewrapper(WEEKLY, interval=1)
    #rule = rrulewrapper(MONTHLY, interval=1)
    loc = RRuleLocator(rule)
    #formatter = DateFormatter("%d-%b '%y")
    formatter = DateFormatter("%d-%b")
    ax.xaxis.set_major_locator(loc)
    ax.xaxis.set_major_formatter(formatter)
    labelsx = ax.get_xticklabels()
    plt.setp(labelsx, rotation=30, fontsize=10)
    font = font_manager.FontProperties(size='small')
    ax.legend(loc=1,prop=font)
    ax.invert_yaxis()
    fig.autofmt_xdate()
    plt.subplots_adjust(left=0.03, right=0.93, top=0.99, bottom=0.06)
    #plt.show()
    plt.savefig(os.path.expanduser(outFileName))

if __name__=="__main__":
    taskList = get_task_list("Research")
    create_gantt_chart(taskList,"~/Desktop/Research.png")