extract and fix to be able to have a web worker
This commit is contained in:
@@ -1,10 +1,17 @@
|
|||||||
import { Feature } from '@/modules/feature/feature'
|
import { Feature } from '@/modules/feature/feature'
|
||||||
import { FeatureStep } from '@/modules/feature/feature-steps'
|
import { FeatureStep } from '@/modules/feature/feature-steps'
|
||||||
import { features } from '@/modules/feature/feature.fixture'
|
import { features as initialFeatures } from '@/modules/feature/feature.fixture'
|
||||||
|
import { State } from '@/modules/feature/store-type'
|
||||||
import { Strategy } from '@/modules/lean/strategy'
|
import { Strategy } from '@/modules/lean/strategy'
|
||||||
import { pickRandomElement, popNElement, shuffleArray } from '@/utils'
|
import {
|
||||||
|
pickRandomElement,
|
||||||
|
popNElement,
|
||||||
|
shuffleArray,
|
||||||
|
sumElements
|
||||||
|
} from '@/utils'
|
||||||
|
|
||||||
const MAX_FEATURES = 30
|
const MAX_FEATURES = 30
|
||||||
|
const HARD_STOP = 5000
|
||||||
|
|
||||||
const hasQualityIssue = ({
|
const hasQualityIssue = ({
|
||||||
complexity,
|
complexity,
|
||||||
@@ -26,7 +33,7 @@ const hasQualityIssue = ({
|
|||||||
return quality > qualityProbability / multiplicator
|
return quality > qualityProbability / multiplicator
|
||||||
}
|
}
|
||||||
|
|
||||||
export const newBoard = () => shuffleArray(features)
|
export const newBoard = () => shuffleArray(initialFeatures)
|
||||||
|
|
||||||
export const initBoard = (
|
export const initBoard = (
|
||||||
steps: FeatureStep[],
|
steps: FeatureStep[],
|
||||||
@@ -43,7 +50,7 @@ export const initBoard = (
|
|||||||
return initialFeatures
|
return initialFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
export const nextDay = ({
|
export const getFeaturesForNextDay = ({
|
||||||
backlog,
|
backlog,
|
||||||
features,
|
features,
|
||||||
initialStep,
|
initialStep,
|
||||||
@@ -57,13 +64,10 @@ export const nextDay = ({
|
|||||||
initialStep: number
|
initialStep: number
|
||||||
strategy: Strategy
|
strategy: Strategy
|
||||||
daysWithProblemSolving: number
|
daysWithProblemSolving: number
|
||||||
}): Feature[] => {
|
}): [Feature[], Feature[]] => {
|
||||||
features.forEach((feature) => {
|
features
|
||||||
const isFeatureLive = feature.step === 0 && feature.status === 'done'
|
.filter((feature) => feature.step > 0 || feature.status === 'doing')
|
||||||
if (isFeatureLive) {
|
.forEach((feature) => {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
feature.leadTime++
|
feature.leadTime++
|
||||||
|
|
||||||
if (strategy === 'problem-solving') {
|
if (strategy === 'problem-solving') {
|
||||||
@@ -151,7 +155,7 @@ export const nextDay = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return features
|
return [backlog, features]
|
||||||
}
|
}
|
||||||
|
|
||||||
const getOverburdenMultiplicator = (tasksInParallel: number) => {
|
const getOverburdenMultiplicator = (tasksInParallel: number) => {
|
||||||
@@ -201,9 +205,85 @@ const getQualityProbability = (
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// learnings
|
// team learning
|
||||||
probabilityOfGoodQuality =
|
probabilityOfGoodQuality =
|
||||||
probabilityOfGoodQuality + (1.2 * daysWithProblemSolving) / 100
|
probabilityOfGoodQuality + (1.2 * daysWithProblemSolving) / 100
|
||||||
|
|
||||||
return probabilityOfGoodQuality
|
return probabilityOfGoodQuality
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const nextDay = (state: State, strategy: Strategy): State => {
|
||||||
|
state.meta.totalDays++
|
||||||
|
state.meta.strategy[strategy]++
|
||||||
|
|
||||||
|
if (strategy === 'problem-solving') {
|
||||||
|
state.meta.daysWithProblemSolving++
|
||||||
|
}
|
||||||
|
|
||||||
|
const [backlog, features] = getFeaturesForNextDay({
|
||||||
|
backlog: state.backlog,
|
||||||
|
features: state.features,
|
||||||
|
steps: state.steps,
|
||||||
|
initialStep: state.steps[0].stepIndex,
|
||||||
|
strategy,
|
||||||
|
daysWithProblemSolving: state.meta.daysWithProblemSolving
|
||||||
|
})
|
||||||
|
|
||||||
|
state.backlog = backlog
|
||||||
|
state.features = features
|
||||||
|
|
||||||
|
console.log(state.features)
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isProjectFinished = (features: Feature[]) =>
|
||||||
|
features.every((feature) => feature.step === 0 && feature.status === 'done')
|
||||||
|
|
||||||
|
export const meanComplexity = (features: Feature[]) => {
|
||||||
|
return (
|
||||||
|
Math.round(
|
||||||
|
100 *
|
||||||
|
(sumElements(features.map((feature) => feature.complexity)) /
|
||||||
|
features.length)
|
||||||
|
) / 100
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const meanLeadTime = (features: Feature[]) => {
|
||||||
|
return (
|
||||||
|
Math.round(
|
||||||
|
100 *
|
||||||
|
(sumElements(features.map((feature) => feature.leadTime)) /
|
||||||
|
features.length)
|
||||||
|
) / 100
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const meanQualityIssue = (features: Feature[]) => {
|
||||||
|
return (
|
||||||
|
Math.round(
|
||||||
|
100 *
|
||||||
|
(sumElements(features.map((feature) => feature.qualityIssue)) /
|
||||||
|
features.length)
|
||||||
|
) / 100
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const simulate = (state: State, strategy: Strategy): State => {
|
||||||
|
let i = 0
|
||||||
|
|
||||||
|
while (!isProjectFinished(state.features) && i++ < HARD_STOP) {
|
||||||
|
if (strategy === 'problem-solving') {
|
||||||
|
if (state.meta.totalDays % 5 === 0) {
|
||||||
|
state = nextDay(state, 'problem-solving')
|
||||||
|
} else {
|
||||||
|
state = nextDay(state, 'pull')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state = nextDay(state, strategy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,6 +30,6 @@ export const featureSteps: FeatureStep[] = [
|
|||||||
{
|
{
|
||||||
title: 'Release',
|
title: 'Release',
|
||||||
stepIndex: 0,
|
stepIndex: 0,
|
||||||
blueBins: Infinity
|
blueBins: 99999
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,37 +1,19 @@
|
|||||||
import { Feature } from '@/modules/feature/feature'
|
import { Feature } from '@/modules/feature/feature'
|
||||||
import { initBoard, newBoard, nextDay } from '@/modules/feature/feature-board'
|
import {
|
||||||
import { FeatureStep, featureSteps } from '@/modules/feature/feature-steps'
|
meanComplexity,
|
||||||
|
meanLeadTime,
|
||||||
|
meanQualityIssue
|
||||||
|
} from '@/modules/feature/feature-board'
|
||||||
|
import { featureSteps } from '@/modules/feature/feature-steps'
|
||||||
|
import { Meta, State } from '@/modules/feature/store-type'
|
||||||
import { Strategy } from '@/modules/lean/strategy'
|
import { Strategy } from '@/modules/lean/strategy'
|
||||||
import { sumElements } from '@/utils'
|
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
type Meta = {
|
const clone = (data: any) => JSON.parse(JSON.stringify(data))
|
||||||
totalDays: number
|
|
||||||
daysWithProblemSolving: number
|
|
||||||
strategy: Record<Strategy, number>
|
|
||||||
}
|
|
||||||
|
|
||||||
type Analysis = {
|
const instance = new ComlinkWorker<typeof import('./feature-board')>(
|
||||||
worstFeature: Feature
|
new URL('./feature-board', import.meta.url)
|
||||||
meanQualityIssue: number
|
)
|
||||||
meanComplexity: number
|
|
||||||
meanLeadTime: number
|
|
||||||
mainStrategy: Strategy | string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Dashboard = Array<{
|
|
||||||
uuid: string
|
|
||||||
meta: Meta
|
|
||||||
analysis: Analysis
|
|
||||||
}>
|
|
||||||
|
|
||||||
type State = {
|
|
||||||
steps: FeatureStep[]
|
|
||||||
features: Feature[]
|
|
||||||
backlog: Feature[]
|
|
||||||
meta: Meta
|
|
||||||
dashboards: Dashboard
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetMeta = (): Meta => ({
|
const resetMeta = (): Meta => ({
|
||||||
totalDays: 0,
|
totalDays: 0,
|
||||||
@@ -52,94 +34,60 @@ export const useFeatureStore = defineStore('feature', {
|
|||||||
dashboards: []
|
dashboards: []
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
initBoard() {
|
async initBoard() {
|
||||||
this.backlog = newBoard()
|
const newBoard = await instance.newBoard()
|
||||||
|
|
||||||
|
this.backlog = newBoard
|
||||||
this.steps = featureSteps
|
this.steps = featureSteps
|
||||||
this.features = initBoard(this.steps, this.backlog)
|
this.features = await instance.initBoard(
|
||||||
|
clone(this.steps),
|
||||||
|
clone(this.backlog)
|
||||||
|
)
|
||||||
|
|
||||||
|
this.backlog = this.backlog.filter(
|
||||||
|
(l) => !this.features.find((f) => f.name === l.name)
|
||||||
|
)
|
||||||
this.meta = resetMeta()
|
this.meta = resetMeta()
|
||||||
},
|
},
|
||||||
nextDay(strategy: Strategy) {
|
async nextDay(strategy: Strategy) {
|
||||||
this.meta.totalDays++
|
const newState = await instance.nextDay(clone(this.$state), strategy)
|
||||||
this.meta.strategy[strategy]++
|
|
||||||
|
|
||||||
if (strategy === 'problem-solving') {
|
this.backlog = newState.backlog
|
||||||
this.meta.daysWithProblemSolving++
|
this.meta = newState.meta
|
||||||
}
|
this.features = newState.features
|
||||||
|
|
||||||
this.features = nextDay({
|
|
||||||
backlog: this.backlog,
|
|
||||||
features: this.features,
|
|
||||||
steps: this.steps,
|
|
||||||
initialStep: this.steps[0].stepIndex,
|
|
||||||
strategy,
|
|
||||||
daysWithProblemSolving: this.meta.daysWithProblemSolving
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
simulate(strategy: Strategy) {
|
async simulate(strategy: Strategy) {
|
||||||
this.initBoard()
|
await this.initBoard()
|
||||||
while (!this.isProjectFinished) {
|
|
||||||
if (strategy === 'problem-solving') {
|
const newState = await instance.simulate(clone(this.$state), strategy)
|
||||||
if (this.meta.totalDays % 5 === 0) {
|
|
||||||
this.nextDay('problem-solving')
|
const [worstFeature] = newState.features.sort((a, b) =>
|
||||||
} else {
|
|
||||||
this.nextDay('pull')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.nextDay(strategy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const [worstFeature] = this.features.sort((a, b) =>
|
|
||||||
a.qualityIssue > b.qualityIssue ? -1 : 1
|
a.qualityIssue > b.qualityIssue ? -1 : 1
|
||||||
)
|
)
|
||||||
|
|
||||||
this.dashboards.push({
|
this.dashboards.push({
|
||||||
uuid: new Date().getTime().toString(),
|
uuid: new Date().getTime().toString(),
|
||||||
meta: this.meta,
|
meta: newState.meta,
|
||||||
analysis: {
|
analysis: {
|
||||||
meanComplexity: this.meanComplexity,
|
meanComplexity: await instance.meanComplexity(newState.features),
|
||||||
meanLeadTime: this.meanLeadTime,
|
meanLeadTime: await instance.meanLeadTime(newState.features),
|
||||||
meanQualityIssue: this.meanQualityIssue,
|
meanQualityIssue: await instance.meanQualityIssue(newState.features),
|
||||||
worstFeature,
|
worstFeature,
|
||||||
mainStrategy: Object.entries(this.meta.strategy).sort((a, b) =>
|
mainStrategy: Object.entries(newState.meta.strategy).sort((a, b) =>
|
||||||
a[1] > b[1] ? -1 : 1
|
a[1] > b[1] ? -1 : 1
|
||||||
)[0][0]
|
)[0][0]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
await this.initBoard()
|
||||||
},
|
},
|
||||||
clearDashboard() {
|
clearDashboard() {
|
||||||
this.dashboards = []
|
this.dashboards = []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
meanComplexity: (state) => {
|
meanComplexity: (state) => meanComplexity(state.features),
|
||||||
return (
|
meanLeadTime: (state) => meanLeadTime(state.features),
|
||||||
Math.round(
|
meanQualityIssue: (state) => meanQualityIssue(state.features),
|
||||||
100 *
|
|
||||||
(sumElements(state.features.map((feature) => feature.complexity)) /
|
|
||||||
state.features.length)
|
|
||||||
) / 100
|
|
||||||
)
|
|
||||||
},
|
|
||||||
meanLeadTime: (state) => {
|
|
||||||
return (
|
|
||||||
Math.round(
|
|
||||||
100 *
|
|
||||||
(sumElements(state.features.map((feature) => feature.leadTime)) /
|
|
||||||
state.features.length)
|
|
||||||
) / 100
|
|
||||||
)
|
|
||||||
},
|
|
||||||
meanQualityIssue: (state) => {
|
|
||||||
return (
|
|
||||||
Math.round(
|
|
||||||
100 *
|
|
||||||
(sumElements(
|
|
||||||
state.features.map((feature) => feature.qualityIssue)
|
|
||||||
) /
|
|
||||||
state.features.length)
|
|
||||||
) / 100
|
|
||||||
)
|
|
||||||
},
|
|
||||||
featuresGroupedByStep: (state) => {
|
featuresGroupedByStep: (state) => {
|
||||||
const groupedByStep: Record<number, Feature[]> = {}
|
const groupedByStep: Record<number, Feature[]> = {}
|
||||||
|
|
||||||
@@ -152,10 +100,6 @@ export const useFeatureStore = defineStore('feature', {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return groupedByStep
|
return groupedByStep
|
||||||
},
|
}
|
||||||
isProjectFinished: (state) =>
|
|
||||||
state.features.every(
|
|
||||||
(feature) => feature.step === 0 && feature.status === 'done'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user