48 lines
1.4 KiB
TypeScript
48 lines
1.4 KiB
TypeScript
export interface WorkEffortItem {
|
|
estimateHours: number;
|
|
actualHours: number;
|
|
progress: number;
|
|
}
|
|
|
|
export interface WorkEffortSummary {
|
|
estimateHours: number;
|
|
actualHours: number;
|
|
progress: number;
|
|
}
|
|
|
|
export function roundHalfHour(hours: number): number {
|
|
if (!Number.isFinite(hours) || hours <= 0) return 0;
|
|
return Math.round(hours * 2) / 2;
|
|
}
|
|
|
|
export function roundEstimateHours(hours: number): number {
|
|
if (!Number.isFinite(hours) || hours <= 0) return 0;
|
|
return Number(hours.toFixed(2));
|
|
}
|
|
|
|
export function aggregateWorkEffort(items: WorkEffortItem[]): WorkEffortSummary {
|
|
if (items.length === 0) return { estimateHours: 0, actualHours: 0, progress: 0 };
|
|
|
|
const estimateHours = roundEstimateHours(items.reduce((sum, item) => sum + Math.max(0, item.estimateHours || 0), 0));
|
|
const actualHours = roundHalfHour(items.reduce((sum, item) => sum + Math.max(0, item.actualHours || 0), 0));
|
|
|
|
if (estimateHours <= 0) {
|
|
return {
|
|
estimateHours,
|
|
actualHours,
|
|
progress: Math.round(items.reduce((sum, item) => sum + item.progress, 0) / items.length),
|
|
};
|
|
}
|
|
|
|
const weightedProgress = items.reduce((sum, item) => {
|
|
const estimate = Math.max(0, item.estimateHours || 0);
|
|
return sum + estimate * item.progress;
|
|
}, 0);
|
|
|
|
return {
|
|
estimateHours,
|
|
actualHours,
|
|
progress: Math.round(weightedProgress / estimateHours),
|
|
};
|
|
}
|