pdm

package module
v1.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 8, 2026 License: MIT Imports: 14 Imported by: 0

README

Precedence Diagram Method for Go

This is a Go package that provides an implementation to help plan dependencies of a project via the Precedence Diagram Method.

CI Status codecov Docs

Table of Contents

Install

Install via go get. Note that Go 1.23 or newer is required.

# After: go mod init ...
go get -u github.com/gtantech/pdm

Features

  • Adding activities from a table of dependencies
  • Critical activity identification
  • Early Start, Early Finish, Late Start, Late Finish calculations of each activity
  • Total Float and Free Float calculations of each activity
  • Determining which activities are start, middle or final, or are isolated
  • Cycle detection (circular dependencies)
  • Project duration

Example

package main

import (
	"fmt"
	"time"

	"github.com/gtantech/pdm"
	"github.com/gtantech/pdm/activity"
	"github.com/gtantech/pdm/enums"
	"github.com/gtantech/pdm/relationship"
)

type Attributes struct {
	activity.Data
	Name string
}

func main() {
	project := pdm.New[Attributes]()
	// add activities (optional)
	A := project.AddActivity(activity.New(Attributes{Data: activity.NewData(5 * time.Hour), Name: "A"}))
	B := project.AddActivity(activity.New(Attributes{Data: activity.NewData(4 * time.Hour), Name: "B"}))
	C := project.AddActivity(activity.New(Attributes{Data: activity.NewData(5 * time.Hour), Name: "C"}))
	D := project.AddActivity(activity.New(Attributes{Data: activity.NewData(6 * time.Hour), Name: "D"}))
	E := project.AddActivity(activity.New(Attributes{Data: activity.NewData(3 * time.Hour), Name: "E"}))
	F := project.AddActivity(activity.New(Attributes{Data: activity.NewData(4 * time.Hour), Name: "F"}))

	// add dependencies to pdm
	//                                                    // A has no dependencies
	project.AddDependency(A, B, relationship.New(enums.FS)) // B depends on A
	project.AddDependency(A, C, relationship.New(enums.FS)) // C depends on A
	project.AddDependency(B, D, relationship.New(enums.FS)) //       .
	project.AddDependency(C, E, relationship.New(enums.FS)) //       .
	project.AddDependency(D, F, relationship.New(enums.FS)) // F depends on D and E
	project.AddDependency(E, F, relationship.New(enums.FS))

	// remember to call UpdateActivityTimestamps() to update the early/late start/finish of each activity
	project.UpdateActivityTimestamps()

	// print the early/late start/finish of each activity
	for _, activity := range []activity.Activity[Attributes]{A, B, C, D, E, F} {
		fmt.Printf("Activity %v:\n", activity.Data().Name)
		fmt.Printf("Early Start:%-5v \tEarly Finish:%-5v\n",
			activity.Early().Start(), activity.Early().Finish())
		fmt.Printf("Late Start:%-5v \tLate Finish:%-5v\n\n",
			activity.Late().Start(), activity.Late().Finish())
	}

}

Error Handling

CycleDetectedError

pdm.UpdateActivityTimestamps() features cycle detection and will return a CycleDetectedError when encountering a cycle within the network of activities. Below is an error handling example, continued from the above example.

err := project.UpdateActivityTimestamps()
if err != nil {
	var e *pdm.CycleDetectedError[activity.Activity[Attributes], relationship.Relationship]
	if errors.As(err, &e) {
		fmt.Printf("encountered cycle from %v to %v with relationship: %v",
			e.Predecessor.Data().Name,
			e.Successor.Data().Name,
			e.Relationship.Type())
	}
}

License

Licensed under MIT License

Acknowledgements

  • Engineer4Free youtube playlist
    • A big thank you to Engineer4Free (youtube) for their amazing course in project management. Most of the test cases used in this project were from their worked examples in their project management playlist.

Thanks!

Thanks for reading and happy coding! Add a star to the project if you find it useful!

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New[D activity.Data]() *pdm[D]

New returns a new *pdm

Added in pdm v1.0.0.

Types

type CycleDetectedError

type CycleDetectedError[V comparable, E any] struct {
	Relationship E
	Predecessor  V
	Successor    V
}

CycleDetectedError indicates that the function called detected a cycle within a graph

Added in pdm v1.0.0.

func (*CycleDetectedError[V, E]) Error

func (e *CycleDetectedError[V, E]) Error() string

Error returns the error message in e *CycleDetectedError

Added in pdm v1.0.0.

type PDM

type PDM[D activity.Data] interface {
	// AddActivity adds an activity into [PDM] and returns the same activity.
	AddActivity(activity activity.Activity[D]) activity.Activity[D]

	// RemoveActivity removes the specified activity from [PDM].
	RemoveActivity(activity activity.Activity[D])

	// AddDependency adds a dependency from a predecessor activity to the successor activity, joined by the dependsVia relationship.
	AddDependency(predecessor activity.Activity[D], successor activity.Activity[D], dependsVia relationship.Relationship)

	// AddDependencies will add each element in the slice of [dependency.Dependency]
	AddDependencies(dependencies []dependency.Dependency[D]) error

	// AddDependenciesFromTable adds all [table.PredecessorDependency]
	AddDependenciesFromTable(table table.DependencyTable[D]) error

	// RemoveDependency removes a dependency from a predecessor activity to the successor activity.
	RemoveDependency(predecessor activity.Activity[D], successor activity.Activity[D])

	// OutgoingDependencies returns an iterator over all successor [activity.Activities] connected by [relationship.Relationship] to the predecessor in [PDM]. The iteration order is not specified and is not guaranteed to be the same from one call to the next.
	OutgoingDependencies(predecessor activity.Activity[D]) func(yield func(activity.Activity[D], relationship.Relationship) bool)

	// IncomingDependencies returns an iterator over all predecessor [activity.Activities] connected by [relationship.Relationship] to the successor in [PDM]. The iteration order is not specified and is not guaranteed to be the same from one call to the next.
	IncomingDependencies(successor activity.Activity[D]) func(yield func(activity.Activity[D], relationship.Relationship) bool)

	// GetRelationship returns the [relationship.Relationship] between the predecessor activity and successor activity.
	GetRelationship(predecessor activity.Activity[D], successor activity.Activity[D]) (relationship.Relationship, bool)

	// Activities returns an iterator over all activities in [PDM]. The iteration order is not specified and is not guaranteed to be the same from one call to the next.
	Activities() func(yield func(activity.Activity[D]) bool)

	// Successors returns an iterator over succeeding activities to the activity specified. The iteration order is not specified and is not guaranteed to be the same from one call to the next.
	Successors(activity activity.Activity[D]) func(yield func(activity.Activity[D]) bool)

	// Predecessors returns an iterator over preceeding activities to the activity specified. The iteration order is not specified and is not guaranteed to be the same from one call to the next.
	Predecessors(activity activity.Activity[D]) func(yield func(activity.Activity[D]) bool)

	// InitialPredecessorActivities returns an iterator of all starting activities in [PDM]. The iteration order is not specified and is not guaranteed to be the same from one call to the next.
	InitialPredecessorActivities() func(yield func(activity.Activity[D]) bool)

	// LoneActivities returns an iterator of all activities in [PDM] with no predecessor or successor in [PDM]. The iteration order is not specified and is not guaranteed to be the same from one call to the next.
	LoneActivities() func(yield func(activity.Activity[D]) bool)

	// IntermediaryActivities returns an iterator over activities between the start and final activities. The iteration order is not specified and is not guaranteed to be the same from one call to the next.
	IntermediaryActivities() func(yield func(activity.Activity[D]) bool)

	// FinalSuccessorActivities returns an iterator of all final activities in [PDM]. The iteration order is not specified and is not guaranteed to be the same from one call to the next.
	FinalSuccessorActivities() func(yield func(activity.Activity[D]) bool)

	// CriticalActivities returns an iterator over activities that are considered critical (any delay will affect the project end date). The iteration order is not specified and is not guaranteed to be the same from one call to the next.
	//
	// The threshold parameter is the value at which if the total float of an activity is less than equal to the threshold value, it is considered critical.
	CriticalActivities(threshold time.Duration) func(yield func(activity.Activity[D]) bool)

	// UpdateActivityTimestamps updates all start/finish intervals for all activities in p. Returns CycleDetectedError if a cycle is detected in [PDM].
	UpdateActivityTimestamps() error

	// FreeFloat returns the maximum amount of time the specified activity can be delayed before the early start of any succeeding activity is delayed. (free float <= total float).
	FreeFloat(activity activity.Activity[D]) time.Duration

	// TotalFloat returns the float in activity [activity.Activity]. This is the amount of time the activity can be delayed without delaying the project end date
	TotalFloat(activity activity.Activity[D]) time.Duration

	// Duration returns the duration of the project [PDM] , ie. the project end date.
	Duration() time.Duration
}

PDM stores and manages all activites and dependencies. Provides functions to manage a project via Precedence Diagram Method.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL