guesstimate/client/src/types/project.ts

107 lines
2.9 KiB
TypeScript

import { Task, TaskCategory, TaskID, getTaskWeightedMean, TaskCategoryID, getTaskStandardDeviation } from './task';
import { Params, defaults, getTaskCategoryCost } from "./params";
import { Estimation } from '../hooks/useProjectEstimations';
export type ProjectID = string;
export interface Project {
id: ProjectID
label: string
description: string
tasks: Tasks
params: Params
}
export interface Tasks {
[id: string]: Task
}
export function newProject(): Project {
return {
id: "",
label: "",
description: "",
tasks: {},
params: {
...defaults
},
};
}
export function getProjectWeightedMean(p : Project): number {
return Object.values(p.tasks).reduce((sum: number, t: Task) => {
sum += getTaskWeightedMean(t);
return sum;
}, 0);
}
export function getTaskCategoryWeightedMean(taskCategoryId: TaskCategoryID, p : Project): number {
return Object.values(p.tasks).filter(t => t.category === taskCategoryId).reduce((sum: number, t: Task) => {
sum += getTaskWeightedMean(t);
return sum;
}, 0);
}
export function getProjectStandardDeviation(p : Project): number {
return Math.sqrt(Object.values(p.tasks).reduce((sum: number, t: Task) => {
sum += Math.pow(getTaskStandardDeviation(t), 2);
return sum;
}, 0));
}
export interface MeanRepartition {
[id: string]: number
}
export function getTaskCategoriesMeanRepartition(project: Project): MeanRepartition {
let projectMean = getProjectWeightedMean(project);
const repartition: MeanRepartition = {};
Object.values(project.params.taskCategories).forEach(tc => {
repartition[tc.id] = getTaskCategoryWeightedMean(tc.id, project) / projectMean;
if (Number.isNaN(repartition[tc.id])) repartition[tc.id] = 0;
});
return repartition;
}
export interface MinMaxCost {
max: Cost
min: Cost
}
export interface Cost {
totalCost: number
totalTime: number
details: { [taskCategoryId: string]: { time: number, cost: number } }
}
export function getMinMaxCosts(project: Project, estimation: Estimation): MinMaxCost {
const max: Cost = {totalCost: 0, totalTime: 0, details: {}};
const min: Cost = {totalCost: 0, totalTime: 0, details: {}};
const repartition = getTaskCategoriesMeanRepartition(project);
Object.values(project.params.taskCategories).forEach(tc => {
const cost = getTaskCategoryCost(tc);
const maxTime = Math.round((estimation.e + estimation.sd) * repartition[tc.id]);
max.details[tc.id] = {
time: maxTime,
cost: Math.ceil(maxTime) * cost,
};
max.totalTime += max.details[tc.id].time;
max.totalCost += max.details[tc.id].cost;
const minTime = Math.round((estimation.e - estimation.sd) * repartition[tc.id]);
min.details[tc.id] = {
time: minTime,
cost: Math.ceil(minTime) * cost,
};
min.totalTime += min.details[tc.id].time;
min.totalCost += min.details[tc.id].cost;
});
return { max, min };
}