Skip to content

Instantly share code, notes, and snippets.

@snhobbs
Created October 3, 2024 16:42
Show Gist options
  • Select an option

  • Save snhobbs/11f48685c58f98c08e470c3b25db67c8 to your computer and use it in GitHub Desktop.

Select an option

Save snhobbs/11f48685c58f98c08e470c3b25db67c8 to your computer and use it in GitHub Desktop.
Project Planning & Reports

Project Libre used to be my go to project planning software. Unfortunately it has seriously degraded in quality and is not getting updates.

I use project planning in a few ways:

  1. Scheduling: Used for forward and reverse planning, to highlight critical path items, and schedule time.
  2. Client Reports: Generate cost and time estimates for clients
  3. Keep a detailed to do list when juggling many tasks and moving parts

For scheduling I like GanttProject which will do the forward / backwards scheduling and critical task list well. It can be used to manage the task list also. It creates the worst reports and the resource management is attrocious. It does export CSV and XLS export which means it can be used for internal planning and then the spreadsheet export can be turned into a presentable report.

There are other options like all the SAAS options and things like OpenProject. Jira, OpenProject, Zoho Tasks etc. seem good for scheduling software but are a poor fit to the kind of scheduling I need to do.

Mermaid makes some nice gantt charts from a text input but the SVG input doesn't work with inkscape. They can be exported as a PDF from a web browser as a PDF then imported into inkscape.

Plotly has a timeline chart that is decent:

import plotly.figure_factory as ff
import importlib
import pandas as pd
import time
import datetime

importlib.reload(ff)

df = pd.DataFrame([dict(Task="Job-1", Start='2017-01-01', Finish='2017-02-02', Resource='Complete'),
      dict(Task="Job-1", Start='2017-02-15', Finish='2017-03-15', Resource='Incomplete'),
      dict(Task="Job-2", Start='2017-01-17', Finish='2017-02-17', Resource='Not Started'),
      dict(Task="Job-2", Start='2017-01-17', Finish='2017-02-17', Resource='Complete'),
      dict(Task="Job-3", Start='2017-03-10', Finish='2017-03-20', Resource='Not Started'),
      dict(Task="Job-3", Start='2017-04-01', Finish='2017-04-20', Resource='Not Started'),
      dict(Task="Job-3", Start='2017-05-18', Finish='2017-06-18', Resource='Not Started'),
      dict(Task="Job-4", Start='2017-01-14', Finish='2017-03-14', Resource='Complete')])
df["Start"] = [datetime.datetime.strptime(pt, "%Y-%m-%d") for pt in df["Start"]]
df["Finish"] = [datetime.datetime.strptime(pt, "%Y-%m-%d") for pt in df["Finish"]]

colors = {'Not Started': 'rgb(220, 0, 0)',
          'Incomplete': (1, 0.9, 0.16),
          'Complete': 'rgb(0, 255, 100)'}

import plotly
fig = ff.create_gantt(df, colors=colors, index_col='Resource', show_colorbar=True,
                      group_tasks=True)
fig.show()

We can draw our own dependancy arrows:

def draw_arrow_between_jobs(fig, first_job_dict, second_job_dict):
    ## retrieve tick text and tick vals
    job_yaxis_mapping = dict(zip(fig.layout.yaxis.ticktext,fig.layout.yaxis.tickvals))
    jobs_delta = second_job_dict['Start'] - first_job_dict['Finish']
    ## horizontal line segment
    fig.add_shape(
        x0=first_job_dict['Finish'], y0=job_yaxis_mapping[first_job_dict['Task']], 
        x1=first_job_dict['Finish'] + jobs_delta/2, y1=job_yaxis_mapping[first_job_dict['Task']],
        line=dict(color="blue", width=2)
    )
    ## vertical line segment
    fig.add_shape(
        x0=first_job_dict['Finish'] + jobs_delta/2, y0=job_yaxis_mapping[first_job_dict['Task']], 
        x1=first_job_dict['Finish'] + jobs_delta/2, y1=job_yaxis_mapping[second_job_dict['Task']],
        line=dict(color="blue", width=2)
    )
    ## horizontal line segment
    fig.add_shape(
        x0=first_job_dict['Finish'] + jobs_delta/2, y0=job_yaxis_mapping[second_job_dict['Task']], 
        x1=second_job_dict['Start'], y1=job_yaxis_mapping[second_job_dict['Task']],
        line=dict(color="blue", width=2))
        ## draw an arrow
    fig.add_annotation(
        x=second_job_dict['Start'], y=job_yaxis_mapping[second_job_dict['Task']],
        xref="x",yref="y",
        showarrow=True,
        ax=-10,
        ay=0,
        arrowwidth=2,
        arrowcolor="blue",
        arrowhead=2,
    )
    return fig

Combining the spreadsheet output of ganttproject with some pandas and plotly seems to be the closest we can get. It would be nice to add scheduling calanders for blackout dates and to have basic resource leveling but that will be held until version 1.1.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment