From 7e89fc43f97be3149aa771b5215870eae10f343f Mon Sep 17 00:00:00 2001 From: William Petit Date: Thu, 23 Apr 2020 09:15:27 +0200 Subject: [PATCH] Add weighted repartition preview --- src/routes/project/estimation-tab.tsx | 53 +++++++++++++--------- src/routes/project/repartition-preview.tsx | 48 ++++++++++++++++++++ src/util/stat.ts | 30 +++++++----- 3 files changed, 98 insertions(+), 33 deletions(-) create mode 100644 src/routes/project/repartition-preview.tsx diff --git a/src/routes/project/estimation-tab.tsx b/src/routes/project/estimation-tab.tsx index 46b16c8..28c0cf5 100644 --- a/src/routes/project/estimation-tab.tsx +++ b/src/routes/project/estimation-tab.tsx @@ -5,6 +5,7 @@ import TimePreview from "./time-preview"; import FinancialPreview from "./financial-preview"; import { addTask, updateTaskEstimation, removeTask, updateProjectLabel, updateTaskLabel, ProjectReducerActions } from "../../hooks/use-project-reducer"; import { Task, TaskID, EstimationConfidence } from "../../models/task"; +import RepartitionPreview from "./repartition-preview"; export interface EstimationTabProps { project: Project @@ -29,32 +30,40 @@ const EstimationTab: FunctionalComponent = ({ project, dispa }; return ( - + +
+
+
-
- -
-
- - -
+
+ +
+
+
+ + { - Object.keys(project.tasks).length <= 20 ? -
-
-

⚠️ Attention

-

Votre projet ne contient pas assez de tâches pour que les niveaux de confiance soient fiables. Un minimum de 20 tâches est conseillé pour obtenir une estimation pertinente.

-
-
: - null + Object.keys(project.tasks).length <= 20 ? +
+
+

⚠️ Attention

+
+

Votre projet ne contient pas assez de tâches pour que les niveaux de confiance soient fiables.

+
+

Un minimum de 20 tâches est conseillé pour obtenir une estimation pertinente.

+
+
: + null } - +
+
+
); }; diff --git a/src/routes/project/repartition-preview.tsx b/src/routes/project/repartition-preview.tsx new file mode 100644 index 0000000..1b113ee --- /dev/null +++ b/src/routes/project/repartition-preview.tsx @@ -0,0 +1,48 @@ +import { FunctionalComponent, h } from "preact"; +import { Project } from "../../models/project"; +import { useProjectEstimations } from "../../hooks/use-project-estimations"; +import { getCurrency, getRoundUpEstimations } from "../../models/params"; +import ProjectTimeUnit from "../../components/project-time-unit"; +import { getTaskCategoryWeightedMean, getProjectWeightedMean } from "../../util/stat"; + +export interface RepartitionPreviewProps { + project: Project +} + +const RepartitionPreview: FunctionalComponent = ({ project }) => { + const projectMean = getProjectWeightedMean(project); + return ( +
+ + + + + + + + + + + + { + Object.values(project.params.taskCategories).map(tc => { + let mean = getTaskCategoryWeightedMean(tc.id, project); + const percent = ((mean/projectMean) * 100).toFixed(0); + if (getRoundUpEstimations(project)) { + mean = Math.ceil(mean); + } + return ( + + + + + ); + }) + } + +
Répartition moyenne pondérée
CatégorieTemps
{tc.label}~ {mean} ({percent} %)
+
+ ); +}; + +export default RepartitionPreview; diff --git a/src/util/stat.ts b/src/util/stat.ts index 8644454..b26c510 100644 --- a/src/util/stat.ts +++ b/src/util/stat.ts @@ -1,24 +1,32 @@ -import { Task } from "../models/task"; +import { Task, TaskCategory, TaskCategoryID } from "../models/task"; import { Project } from "../models/project"; +import { TaskCategoriesTableProps } from "../routes/project/task-categories-table"; export function getTaskWeightedMean(t: Task): number { - return (t.estimations.optimistic + (4*t.estimations.likely) + t.estimations.pessimistic) / 6; + 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; + 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); + 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)); + return Math.sqrt(Object.values(p.tasks).reduce((sum: number, t: Task) => { + sum += Math.pow(getTaskStandardDeviation(t), 2); + return sum; + }, 0)); } \ No newline at end of file