Something went wrong. Try again.
A functional toolkit for authoring resource-leveling algorithms — compose constraint and scoring blocks, enumerate feasible schedules
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250import { Duration } from "./model/Duration.ts";import type { Assignment } from "./model/Assignment.ts";import type { Calendar } from "./model/Calendar.ts";import type { ProjectFile } from "./model/Project.ts";import type { Relation } from "./model/Relation.ts";import type { Resource } from "./model/Resource.ts";import type { Task } from "./model/Task.ts";import { parseProjectDate } from "./dateTime.ts";import { RelationType, ResourceType, type ProjectProperties,} from "./model/types.ts";
interface FixtureProjectData { project: { title: string | null; author: string | null; start_date: string | null; finish_date: string | null; status_date: string | null; }; tasks: FixtureTask[]; resources: FixtureResource[]; assignments: FixtureAssignment[]; calendars: FixtureCalendar[];}
interface FixtureTask { id: number | null; unique_id: number | null; name: string | null; wbs: string | null; outline_level: number | null; start: string | null; finish: string | null; duration: string | null; percent_complete: string | null; summary: boolean | null; milestone: boolean | null; critical: boolean | null; notes: string | null; priority: string | null; actual_start: string | null; actual_finish: string | null; baseline_start: string | null; baseline_finish: string | null; baseline_duration: string | null; cost: string | null; work: string | null; predecessors: FixtureRelation[];}
interface FixtureRelation { predecessor_unique_id: number | null; type: string | null; lag: string | null;}
interface FixtureResource { id: number | null; unique_id: number | null; name: string | null; type: string | null; email: string | null; group: string | null; max_units: string | null; cost: string | null;}
interface FixtureAssignment { task_unique_id: number | null; resource_unique_id: number | null; work: string | null; units: string | null; start: string | null; finish: string | null;}
interface FixtureCalendar { unique_id: number | null; name: string | null;}
export function projectFromFixtureData(data: FixtureProjectData): ProjectFile { return { properties: mapProperties(data.project), tasks: data.tasks.map(mapTask), resources: data.resources.map(mapResource), assignments: data.assignments.map(mapAssignment), calendars: data.calendars.map(mapCalendar), };}
export function projectFromFixtureJson(json: string): ProjectFile { return projectFromFixtureData(JSON.parse(json) as FixtureProjectData);}
function mapProperties( input: FixtureProjectData["project"],): ProjectProperties { return { title: input.title, author: input.author, startDate: parseProjectDate(input.start_date), finishDate: parseProjectDate(input.finish_date), statusDate: parseProjectDate(input.status_date), defaultCalendarUniqueId: null, minutesPerDay: 480, minutesPerWeek: 2400, daysPerMonth: 20, saveVersion: null, };}
function mapTask(input: FixtureTask): Task { return { id: input.id, uniqueId: input.unique_id, name: input.name, wbs: input.wbs, outlineLevel: input.outline_level, start: parseProjectDate(input.start), finish: parseProjectDate(input.finish), duration: Duration.parseSimple(input.duration), percentComplete: parseNumber(input.percent_complete), summary: input.summary, milestone: input.milestone, critical: input.critical, notes: input.notes, priority: parsePriority(input.priority), cost: parseNumber(input.cost), work: Duration.parseSimple(input.work), actualStart: parseProjectDate(input.actual_start), actualFinish: parseProjectDate(input.actual_finish), baselineStart: parseProjectDate(input.baseline_start), baselineFinish: parseProjectDate(input.baseline_finish), baselineDuration: Duration.parseSimple(input.baseline_duration), actualWork: null, constraintType: null, freeSlack: null, totalSlack: null, earlyStart: null, earlyFinish: null, lateStart: null, lateFinish: null, levelingDelay: null, deadline: null, splits: null, predecessors: input.predecessors.map((predecessor) => mapRelation(predecessor, input.unique_id), ), };}
function mapRelation( input: FixtureRelation, successorUniqueId: number | null,): Relation { return { predecessorUniqueId: input.predecessor_unique_id, successorUniqueId, type: parseRelationType(input.type), lag: Duration.parseSimple(input.lag), };}
function mapResource(input: FixtureResource): Resource { return { id: input.id, uniqueId: input.unique_id, name: input.name, type: parseResourceType(input.type), email: input.email, group: input.group, maxUnits: parseNumber(input.max_units), cost: parseNumber(input.cost), work: null, resourcePool: null, };}
function mapAssignment(input: FixtureAssignment): Assignment { return { taskUniqueId: input.task_unique_id, resourceUniqueId: input.resource_unique_id, work: Duration.parseSimple(input.work), units: parseNumber(input.units), start: parseProjectDate(input.start), finish: parseProjectDate(input.finish), actualWork: null, remainingWork: null, };}
function mapCalendar(input: FixtureCalendar): Calendar { return { uniqueId: input.unique_id, name: input.name, weekDays: [], exceptions: [], };}
function parseNumber(raw: string | null): number | null { if (raw === null || raw === "") { return null; } const value = Number(raw.replaceAll(",", "")); return Number.isFinite(value) ? value : null;}
function parsePriority(raw: string | null): number | null { if (!raw) { return null; } const match = /value=(\d+)/.exec(raw); return match ? Number(match[1]) : parseNumber(raw);}
function parseRelationType(raw: string | null): RelationType { switch (raw) { case "START_START": case "SS": return RelationType.StartToStart; case "FINISH_FINISH": case "FF": return RelationType.FinishToFinish; case "START_FINISH": case "SF": return RelationType.StartToFinish; case "FINISH_START": case "FS": default: return RelationType.FinishToStart; }}
function parseResourceType(raw: string | null): ResourceType { switch (raw) { case "Material": return ResourceType.Material; case "Cost": return ResourceType.Cost; case "Work": default: return ResourceType.Work; }}