def plotCosts(data, color): start = WorkingWeek.fromString(project.FirstWeek).toordinal() - 7 x = [WorkingWeek.fromString(w[0]).toordinal()-start for w in data] x.insert(0, 0) y = [w[1] for w in data] y.insert(0, 0) y = pylab.array(y).cumsum() ax.plot(x, y, color)
def makeEarnedValueChart(session, project, items, ax): """ Makes three lines: * The planned costs for the project (blue) * The actual costs for the project (red) * The earned value for the project, based on the workitems completed (green) """ pe = PlannedEffort def plotCosts(data, color): start = WorkingWeek.fromString(project.FirstWeek).toordinal() - 7 x = [WorkingWeek.fromString(w[0]).toordinal()-start for w in data] x.insert(0, 0) y = [w[1] for w in data] y.insert(0, 0) y = pylab.array(y).cumsum() ax.plot(x, y, color) # Determine the planned costs for the project planned_costs = session.query(pe.Week, func.sum(pe.Hours)).\ filter(pe.Project==project.Id).\ filter(pe.IsActual==False).\ group_by(pe.Week).\ order_by(pe.Week).all() actual_costs = session.query(pe.Week, func.sum(pe.Hours)).\ filter(pe.Project==project.Id).\ filter(pe.IsActual==True).\ group_by(pe.Week).\ order_by(pe.Week).all() plotCosts(planned_costs, 'b') plotCosts(actual_costs, 'r') # Determine the initial estimate: the first estimate for each entry work_remaining = {} for it in items: for ch in reversed(it.StateChanges): if ch.Status not in OPEN_STATES: continue if not ch.TimeRemaining: continue work_remaining[it.Id] = ch.TimeRemaining break # Make a copy of the init initial_estimate = sum(work_remaining.values()) # Determine the earned value after each state change all_values = [0.0] all_timestamps = [0.0] start = WorkingWeek.fromString(project.FirstWeek).toordinal() for change in session.query(PlaneableStatus).order_by(PlaneableStatus.TimeStamp): if change.Planeable not in work_remaining: continue if change.Status in OPEN_STATES: work_remaining[change.Planeable] = change.TimeRemaining else: work_remaining[change.Planeable] = 0.0 remaining = sum(work_remaining.values()) value = project.Budget * (1 - remaining / initial_estimate) all_values.append(value) all_timestamps.append(change.TimeStamp.toordinal()-start) ax.plot(all_timestamps, all_values, 'g')