guesstimate/client/src/util/stat.ts

91 lines
2.9 KiB
TypeScript

import { Task, TaskCategory, TaskCategoryID } from "../models/task";
import { Project } from "../models/project";
import { TaskCategoriesTableProps } from "../routes/project/task-categories-table";
import { Estimation } from "../hooks/use-project-estimations";
import { getTaskCategoryCost } from "../models/params";
export function getTaskWeightedMean(t: Task): number {
return (t.estimations.optimistic + (4*t.estimations.likely) + t.estimations.pessimistic) / 6;
}
export function getTaskStandardDeviation(t: Task): number {
return (t.estimations.pessimistic - t.estimations.optimistic) / 6;
}
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 };
}