diff --git a/index.html b/index.html
index 2135f04..de02646 100644
--- a/index.html
+++ b/index.html
@@ -25,6 +25,10 @@
Average work distribution
+
diff --git a/spec/animation.spec.js b/spec/animation.spec.js
index 4c1c7ad..5d9db15 100644
--- a/spec/animation.spec.js
+++ b/spec/animation.spec.js
@@ -1,7 +1,7 @@
const PubSub = require('pubsub-js');
const animation = require('../src/animation');
-const {Worker, WorkItem} = require('../src/worker');
+const {Worker, WorkItem, SimpleSkillStrategy} = require('../src/worker');
const Board = require('../src/board');
const find = selector => document.querySelector(selector);
@@ -63,7 +63,7 @@ describe('animation', () => {
});
it('a worker picks up an item', () => {
- board.addWorkers(new Worker({dev: 1}));
+ board.addWorkers(new Worker(new SimpleSkillStrategy({dev: 1})));
let workItem = new WorkItem({dev: 1});
board.addWorkItems(workItem);
jest.advanceTimersByTime(0);
@@ -78,7 +78,7 @@ describe('animation', () => {
})
it('shows card amount', () => {
- board.addWorkers(new Worker({dev: 1}));
+ board.addWorkers(new Worker(new SimpleSkillStrategy({dev: 1})));
board.addWorkItems(new WorkItem({dev: 1}));
jest.advanceTimersByTime(0);
@@ -131,7 +131,7 @@ describe('animation', () => {
});
it('adds new workers', () => {
- const worker = new Worker({dev: 0});
+ const worker = new Worker(new SimpleSkillStrategy({dev: 0}));
jest.runAllTimers();
let $worker = find(`.workers [data-worker-id="${worker.id}"]`);
expect($worker.querySelector('.name').innerHTML).toEqual(`${worker.name()}: `);
@@ -139,7 +139,7 @@ describe('animation', () => {
});
it('stat update', () => {
- const worker = new Worker({dev: 0});
+ const worker = new Worker(new SimpleSkillStrategy({dev: 0}));
const newStats = {workerId: worker.id, stats: {efficiency: 0.9500111}};
PubSub.publish('worker.stats.updated', newStats);
jest.runAllTimers();
diff --git a/spec/board.spec.js b/spec/board.spec.js
index d6e9ac0..f65e9dd 100644
--- a/spec/board.spec.js
+++ b/spec/board.spec.js
@@ -1,4 +1,4 @@
-const {Worker, WorkItem, WorkList} = require('../src/worker');
+const {Worker, WorkItem, WorkList, SimpleSkillStrategy} = require('../src/worker');
const Board = require('../src/board');
const PubSub = require('pubsub-js');
@@ -15,7 +15,7 @@ describe('a worker', () => {
describe('without tasks', () => {
it('doesnt crash', () => {
- board.addWorkers(new Worker({dev: 1}));
+ board.addWorkers(new Worker(new SimpleSkillStrategy({dev: 1})));
jest.runAllTimers();
expect(board.items()).toEqual([[], [], []]);
});
@@ -23,7 +23,7 @@ describe('a worker', () => {
describe('with one task', () => {
beforeEach(() => {
- board.addWorkers(new Worker({dev: 1}));
+ board.addWorkers(new Worker(new SimpleSkillStrategy({dev: 1})));
workItem = new WorkItem({dev: 1});
board.addWorkItems(workItem);
});
@@ -66,7 +66,7 @@ describe('a worker', () => {
describe('works on an item added after simulation started', () => {
it('works', () => {
- board.addWorkers(new Worker({dev: 1}));
+ board.addWorkers(new Worker(new SimpleSkillStrategy({dev: 1})));
let workItem = new WorkItem({dev: 1});
workItem.foo = 'bar';
board.addWorkItems(workItem);
@@ -78,7 +78,7 @@ describe('a worker', () => {
describe('with two tasks', () => {
beforeEach(() => {
- board.addWorkers(new Worker({dev: 1}));
+ board.addWorkers(new Worker(new SimpleSkillStrategy({dev: 1})));
workItem1 = new WorkItem({dev: 1});
workItem2 = new WorkItem({dev: 1});
board.addWorkItems(workItem1, workItem2);
@@ -135,7 +135,7 @@ describe('workers work at their own speed', () => {
beforeEach(() => {
workItem = new WorkItem({dev: 1});
board.addWorkItems(workItem);
- board.addWorkers(new Worker({dev: 1}));
+ board.addWorkers(new Worker(new SimpleSkillStrategy({dev: 1})));
});
it('starts instantly', () => {
jest.advanceTimersByTime(0);
@@ -155,7 +155,7 @@ describe('workers work at their own speed', () => {
beforeEach(() => {
workItem = new WorkItem({dev: 1});
board.addWorkItems(workItem);
- board.addWorkers(new Worker({dev: 0.5}));
+ board.addWorkers(new Worker(new SimpleSkillStrategy({dev: 0.5})));
});
it('starts instantly', () => {
jest.advanceTimersByTime(0);
@@ -183,8 +183,8 @@ describe('a typical workflow', () => {
beforeEach(() => {
board = new Board(['dev', 'qa'])
board.addWorkers(
- new Worker({dev: 1}),
- new Worker({qa: 1}),
+ new Worker(new SimpleSkillStrategy({dev: 1})),
+ new Worker(new SimpleSkillStrategy({qa: 1})),
);
workItem1 = new WorkItem({dev: 1, qa: 2});
workItem2 = new WorkItem({dev: 2, qa: 1});
diff --git a/spec/limit-wip.spec.js b/spec/limit-wip.spec.js
index 2eb9c1c..27fd15b 100644
--- a/spec/limit-wip.spec.js
+++ b/spec/limit-wip.spec.js
@@ -1,6 +1,6 @@
const PubSub = require('pubsub-js');
const Board = require('../src/board');
-const {WorkList, Worker, WorkItem} = require('../src/worker');
+const {WorkList, Worker, WorkItem, SimpleSkillStrategy} = require('../src/worker');
const {LimitBoardWip} = require('../src/strategies');
describe('limiting overall wip', () => {
@@ -17,7 +17,7 @@ describe('limiting overall wip', () => {
beforeEach(() => {
board = new Board(['dev', 'qa']);
new LimitBoardWip().initialize(1);
- board.addWorkers(new Worker({dev: 1}), new Worker({qa: 1}));
+ board.addWorkers(new Worker(new SimpleSkillStrategy({dev: 1})), new Worker(new SimpleSkillStrategy({qa: 1})));
item1 = new WorkItem({dev: 1, qa: 1});
item2 = new WorkItem({dev: 1, qa: 1});
diff --git a/spec/parsing.spec.js b/spec/parsing.spec.js
index 36aab47..c73a772 100644
--- a/spec/parsing.spec.js
+++ b/spec/parsing.spec.js
@@ -18,16 +18,16 @@ describe('parseWorkload', () => {
describe('parseWorkers', () => {
it('parses a single worker with a single skill', () => {
- expect(parseWorkers('dev')).toEqual([{skills: ['dev']}]);
- expect(parseWorkers('qa')).toEqual([{skills: ['qa']}]);
+ expect(parseWorkers('dev', {workOnUniqueItems: false})).toEqual([{skills: ['dev'], workOnUniqueItems: false}]);
+ expect(parseWorkers('qa', {workOnUniqueItems: true})).toEqual([{skills: ['qa'], workOnUniqueItems: true}]);
});
it('parses multiple workers with single skills', () => {
- expect(parseWorkers('dev, qa')).toEqual([{skills: ['dev']}, {skills: ['qa']}]);
+ expect(parseWorkers('dev, qa', {workOnUniqueItems: false})).toEqual([{skills: ['dev'], workOnUniqueItems: false}, {skills: ['qa'], workOnUniqueItems: false}]);
});
it('parses a single worker with multiple skills', () => {
- expect(parseWorkers('dev+qa')).toEqual([{skills: ['dev', 'qa']}]);
+ expect(parseWorkers('dev+qa', {workOnUniqueItems: false})).toEqual([{skills: ['dev', 'qa'], workOnUniqueItems: false}]);
});
})
@@ -37,12 +37,17 @@ describe('parseInput', () => {
workload: 'dev: 3, qa: 1',
workers: 'dev, qa, qa',
wipLimit: '3',
- random: true
+ random: true,
+ workOnUniqueItems: true
};
const exampleParsedInput = {
title: 'dev: 3, qa: 1',
- workers: [{skills: ['dev']}, {skills: ['qa']}, {skills: ['qa']}],
+ workers: [
+ {skills: ['dev'], workOnUniqueItems: true},
+ {skills: ['qa'], workOnUniqueItems: true},
+ {skills: ['qa'], workOnUniqueItems: true}
+ ],
wipLimit: "3",
distribution: average,
speed: 20,
diff --git a/spec/scenario.spec.js b/spec/scenario.spec.js
index 4deb29d..2cc4c5d 100644
--- a/spec/scenario.spec.js
+++ b/spec/scenario.spec.js
@@ -51,7 +51,7 @@ describe('scenario', () => {
beforeEach(() => {
run({
id: 1,
- workers: [{ skills: ['dev'] }],
+ workers: [{ skills: ['dev'], workOnUniqueItems: false }],
stories: {
amount: 10,
work: {'dev': 1}
@@ -72,7 +72,10 @@ describe('scenario', () => {
beforeEach(() => {
run({
id: 1,
- workers: [{ skills: ['dev'] }, { skills: ['qa'] }],
+ workers: [
+ { skills: ['dev'], workOnUniqueItems: false },
+ { skills: ['qa'], workOnUniqueItems: false }
+ ],
stories: {
amount: 10,
work: {'dev': 1, 'qa': 1}
@@ -93,7 +96,7 @@ describe('scenario', () => {
beforeEach(() => {
run({
id: 1,
- workers: [{ skills: ['all'] }],
+ workers: [{ skills: ['all'], workOnUniqueItems: false }],
stories: {
amount: 10,
work: {'dev': 1, 'qa': 1}
diff --git a/spec/worker-stats.spec.js b/spec/worker-stats.spec.js
index 44b19b9..839767e 100644
--- a/spec/worker-stats.spec.js
+++ b/spec/worker-stats.spec.js
@@ -1,5 +1,5 @@
const PubSub = require('pubsub-js');
-const {Worker} = require('../src/worker');
+const {Worker, SimpleSkillStrategy} = require('../src/worker');
const WorkerStats = require('../src/worker-stats');
describe('worker stats', () => {
@@ -15,19 +15,19 @@ describe('worker stats', () => {
});
it('when worker is idle', () => {
- const worker = new Worker({dev: 1});
+ const worker = new Worker(new SimpleSkillStrategy({dev: 1}));
publishAt(0, 'worker.idle', worker);
expect(latestStats[worker.id]).toMatchObject({efficiency: 0});
});
it('when worker is working', () => {
- const worker = new Worker({dev: 1});
+ const worker = new Worker(new SimpleSkillStrategy({dev: 1}));
publishAt(0, 'worker.working', worker);
expect(latestStats[worker.id]).toMatchObject({efficiency: 1});
});
it('when worker is working 50%', () => {
- const worker = new Worker({dev: 1});
+ const worker = new Worker(new SimpleSkillStrategy({dev: 1}));
publishAt(0, 'worker.working', worker);
publishAt(1, 'worker.idle', worker);
publishAt(2, 'worker.working', worker);
@@ -36,7 +36,7 @@ describe('worker stats', () => {
});
it('when worker is working 10%', () => {
- const worker = new Worker({dev: 1});
+ const worker = new Worker(new SimpleSkillStrategy({dev: 1}));
publishAt(0, 'worker.working', worker);
publishAt(1, 'worker.idle', worker);
publishAt(10, 'worker.working', worker);
@@ -45,7 +45,7 @@ describe('worker stats', () => {
});
it('when worker is working 90%', () => {
- const worker = new Worker({dev: 1});
+ const worker = new Worker(new SimpleSkillStrategy({dev: 1}));
publishAt(0, 'worker.working', worker);
publishAt(9, 'worker.idle', worker);
publishAt(10, 'worker.working', worker);
@@ -54,8 +54,8 @@ describe('worker stats', () => {
});
it('with 2 workers', () => {
- const worker1 = new Worker({dev: 1});
- const worker2 = new Worker({dev: 1});
+ const worker1 = new Worker(new SimpleSkillStrategy({dev: 1}));
+ const worker2 = new Worker(new SimpleSkillStrategy({dev: 1}));
publishAt(0, 'worker.working', worker1);
expect(latestStats[worker1.id]).toMatchObject({efficiency: 1});
diff --git a/spec/worker.spec.js b/spec/worker.spec.js
index a576f87..bb90b08 100644
--- a/spec/worker.spec.js
+++ b/spec/worker.spec.js
@@ -1,33 +1,137 @@
-const {Worker} = require('../src/worker');
+const {Worker, WorkItem, WorkList, SimpleSkillStrategy, DontWorkOnSameItemStrategy} = require('../src/worker');
+
+const createWorkItem = () => new WorkItem({'ux': 1, 'dev': 1, 'qa': 1});
+const createDeveloper = () => new Worker(new DontWorkOnSameItemStrategy(new SimpleSkillStrategy({'all': 1})));
+const createColumns = (itemsPerColumn = {}) => {
+ const columns = {
+ backlog: new WorkList('Backlog'),
+ ux: new WorkList('ux'),
+ devQueue: new WorkList('-'),
+ dev: new WorkList('dev'),
+ };
+ columns.ordered = () => [ columns.backlog, columns.ux, columns.devQueue, columns.dev ];
+
+ Object.keys(itemsPerColumn)
+ .forEach(columnName => itemsPerColumn[columnName]
+ .forEach(item => columns[columnName].add(item)));
+
+ return columns;
+};
+const startWorkingOn = (item, columnName, developer, columns) => {
+ const column = columns[columnName];
+ const columnList = columns.ordered();
+ const columnIndex = columnList.indexOf(column);
+
+ developer.startWorkingOn({
+ inbox: columnList[columnIndex - 1],
+ inProgress: column,
+ outbox: columnList[columnIndex + 1],
+ item: item
+ });
+};
describe('a specialist', () => {
+ const item = new WorkItem({'dev': 1});
+
it('can work on its skill', () => {
- const developer = new Worker({'dev': 1});
- expect(developer.canWorkOn('dev')).toBeTruthy();
- expect(developer.canWorkOn('dev')).toBe(1);
+ const developer = new Worker(new SimpleSkillStrategy({'dev': 1}));
+ expect(developer.canWorkOn({ skill: 'dev', item })).toBeTruthy();
+ expect(developer.canWorkOn({ skill: 'dev', item })).toBe(1);
});
it('can not work on another skill', () => {
- const developer = new Worker({'dev': 1});
- expect(developer.canWorkOn('qa')).toBeFalsy();
+ const developer = new Worker(new SimpleSkillStrategy({'dev': 1}));
+ expect(developer.canWorkOn({ skill: 'qa', item })).toBeFalsy();
})
it('has a name', () => {
- const developer = new Worker({'dev': 1});
+ const developer = new Worker(new SimpleSkillStrategy({'dev': 1}));
expect(developer.name()).toMatch('dev')
})
});
describe('a generalist', () => {
+ const item = new WorkItem({'ux': 1, 'dev': 1, 'qa': 1});
+
it('can work on any skill', () => {
- const developer = new Worker({'all': 1});
- expect(developer.canWorkOn('ux')).toBe(1);
+ const developer = new Worker(new SimpleSkillStrategy({'all': 1}));
+ expect(developer.canWorkOn({ skill: 'ux', item })).toBe(1);
});
it('can work on any skill', () => {
- const developer = new Worker({'rest': 1});
- expect(developer.canWorkOn('ux')).toBe(1);
+ const developer = new Worker(new SimpleSkillStrategy({'rest': 1}));
+ expect(developer.canWorkOn({ skill: 'ux', item })).toBe(1);
});
it('can can have a specialisation', () => {
- const developer = new Worker({'qa': 1, 'rest': 0.5});
- expect(developer.canWorkOn('qa')).toBe(1);
- expect(developer.canWorkOn('ux')).toBe(0.5);
+ const developer = new Worker(new SimpleSkillStrategy({'qa': 1, 'rest': 0.5}));
+ expect(developer.canWorkOn({ skill: 'qa', item })).toBe(1);
+ expect(developer.canWorkOn({ skill: 'ux', item })).toBe(0.5);
+ });
+});
+
+describe('a generalist with a dont-work-on-same-item strategy', () => {
+ const item1 = createWorkItem();
+ const item2 = createWorkItem();
+
+ let developer;
+
+ describe('who has not worked on item 1', () => {
+ beforeEach(() => {
+ developer = createDeveloper();
+ });
+
+ it('can work item 1 in any skill', () => {
+ expect(developer.canWorkOn({ skill: 'ux', item: item1 })).toBe(1);
+ expect(developer.canWorkOn({ skill: 'dev', item: item1 })).toBe(1);
+ expect(developer.canWorkOn({ skill: 'qa', item: item1 })).toBe(1);
+ });
+ });
+
+ describe('who has worked on item 1 in ux', () => {
+ beforeEach(jest.useFakeTimers);
+ afterEach(jest.runAllTimers);
+
+ beforeEach(() => {
+ const columns = createColumns({ backlog: [item1], dev: [item2] });
+
+ developer = createDeveloper();
+ startWorkingOn(item1, 'ux', developer, columns);
+
+ jest.advanceTimersByTime(1000);
+ });
+
+ it('cannot work on the same item in dev', () => {
+ expect(developer.canWorkOn({ skill: 'dev', item: item1 })).toBe(0);
+ });
+
+ it('can work on item 2 in dev', () => {
+ expect(developer.canWorkOn({ skill: 'dev', item: item2 })).toBe(1);
+ });
+ });
+});
+
+describe('two generalists with a dont-work-on-same-item strategy', () => {
+ const item1 = new WorkItem({'ux': 1, 'dev': 1, 'qa': 1});
+
+ const createDeveloper = () => new Worker(new DontWorkOnSameItemStrategy(new SimpleSkillStrategy({'all': 1})));
+
+ let developer1;
+ let developer2;
+
+ describe('when developer 1 has worked on an item in ux', () => {
+ beforeEach(jest.useFakeTimers);
+ afterEach(jest.runAllTimers);
+
+ beforeEach(() => {
+ const columns = createColumns({ backlog: [item1] });
+
+ developer1 = createDeveloper();
+ developer2 = createDeveloper();
+
+ startWorkingOn(item1, 'ux', developer1, columns);
+
+ jest.advanceTimersByTime(1000);
+ });
+
+ it('developer 2 can work on the same item in dev', () => {
+ expect(developer2.canWorkOn({ skill: 'dev', item: item1 })).toBe(1);
+ });
});
});
diff --git a/src/board.js b/src/board.js
index a489aaa..d496b24 100644
--- a/src/board.js
+++ b/src/board.js
@@ -48,30 +48,49 @@ let Board = function (workColumnNames) {
});
function assignNewWorkIfPossible() {
- const columnWithWork = workColumns()
- .reverse()
- .filter(column => column.inbox.hasWork())
- .filter(column => workers.some(worker => worker.canWorkOn(column.necessarySkill)))[0];
+ const nextWork = findNextWork();
+
+ const columnWithWork = nextWork && nextWork.column;
+ const itemToWorkOn = nextWork && nextWork.item;
if (columnWithWork) {
if (columnWithWork.inbox === backlogColumn() && !allowNewWork)
return;
+ const workCriteria = { skill: columnWithWork.necessarySkill, item: itemToWorkOn };
+
const availableWorker = workers
- .filter(worker => worker.canWorkOn(columnWithWork.necessarySkill))
+ .filter(worker => worker.canWorkOn(workCriteria))
.reduce((bestCandidate, worker) => {
if (!bestCandidate) return worker;
- const bestScore = bestCandidate.canWorkOn(columnWithWork.necessarySkill);
- const currentScore = worker.canWorkOn(columnWithWork.necessarySkill);
+ const bestScore = bestCandidate.canWorkOn(workCriteria);
+ const currentScore = worker.canWorkOn(workCriteria);
return bestScore > currentScore ? bestCandidate : worker;
});
if (availableWorker) {
- availableWorker.startWorkingOn(columnWithWork.inbox, columnWithWork, columnWithWork.outbox);
+ availableWorker.startWorkingOn({
+ inbox: columnWithWork.inbox,
+ inProgress: columnWithWork,
+ outbox: columnWithWork.outbox,
+ item: itemToWorkOn
+ });
}
}
}
+ function findNextWork() {
+ return workColumns()
+ .reverse()
+ .filter(column => column.inbox.hasWork())
+ .map(column => ({ column, item: findFirstWorkableItemIn(column) }))
+ .filter(result => !!result.item)[0];
+ }
+
+ function findFirstWorkableItemIn(column) {
+ return column.inbox.items().filter(item => workers.some(worker => worker.canWorkOn({ skill: column.necessarySkill, item })))[0]
+ }
+
PubSub.subscribe('board.denyNewWork', () => allowNewWork = false);
PubSub.subscribe('workitem.added', (topic, {item, column}) => {
diff --git a/src/parsing.js b/src/parsing.js
index f19909b..9950931 100644
--- a/src/parsing.js
+++ b/src/parsing.js
@@ -2,7 +2,7 @@ const {average} = require("./generator");
function parseInput(rawInput) {
const title = rawInput.title;
- const workers = parseWorkers(rawInput.workers);
+ const workers = parseWorkers(rawInput.workers, {workOnUniqueItems: rawInput.workOnUniqueItems});
const work = parseWorkload(rawInput.workload);
const wipLimit = rawInput.wipLimit;
const speed = (workers.length > 2) ? 20 : 1;
@@ -21,10 +21,10 @@ function parseInput(rawInput) {
return input;
}
-function parseWorkers(input) {
+function parseWorkers(input, params) {
return input
.split(',')
- .map(skillsInput => ({skills: parseSkills(skillsInput)}));
+ .map(skillsInput => ({skills: parseSkills(skillsInput), ...params}));
}
function parseSkills(input) {
diff --git a/src/scenario.js b/src/scenario.js
index 3416b4e..54209b7 100644
--- a/src/scenario.js
+++ b/src/scenario.js
@@ -1,6 +1,6 @@
const Board = require("./board");
const {generateWorkItems} = require("./generator");
-const {Worker} = require('./worker')
+const {Worker, SimpleSkillStrategy, DontWorkOnSameItemStrategy} = require('./worker')
let counter = 1;
@@ -8,10 +8,18 @@ const Scenario = scenario => {
const id = counter++;
const wipLimit = scenario.wipLimit || scenario.stories.amount
- const createWorker = ({ skills: skillNames }, speed = 1) => {
+ const createWorker = ({ skills: skillNames, workOnUniqueItems }, speed = 1) => {
let skills = {};
skillNames.forEach(skillName => skills[skillName] = speed);
- return new Worker(skills);
+ return new Worker(createWorkerStrategy(skills, workOnUniqueItems));
+ };
+
+ const createWorkerStrategy = (skills, workOnUniqueItems) => {
+ const skillStrategy = new SimpleSkillStrategy(skills);
+
+ return workOnUniqueItems
+ ? new DontWorkOnSameItemStrategy(skillStrategy)
+ : skillStrategy;
};
const columnNames = () => Object.keys(scenario.stories.work);
diff --git a/src/setup.js b/src/setup.js
index 65e9eaa..71a8633 100644
--- a/src/setup.js
+++ b/src/setup.js
@@ -19,14 +19,17 @@ function createScenarioContainer(scenario) {
}
function parse(form) {
- const field = fieldName => form.querySelector(`[name="${fieldName}"]`).value;
+ const field = fieldName => form.querySelector(`[name="${fieldName}"]`);
+ const fieldValue = fieldName => field(fieldName).value;
+ const isChecked = fieldName => field(fieldName).checked;
return parseInput({
- title: field('workload'),
- workers: field('workers'),
- workload: field('workload'),
- wipLimit: field('wip-limit'),
- random: form.querySelector('[name="random"]').checked
+ title: fieldValue('workload'),
+ workers: fieldValue('workers'),
+ workload: fieldValue('workload'),
+ wipLimit: fieldValue('wip-limit'),
+ workOnUniqueItems: isChecked('unique-items'),
+ random: isChecked('random'),
});
}
diff --git a/src/worker.js b/src/worker.js
index 0774206..4896b40 100644
--- a/src/worker.js
+++ b/src/worker.js
@@ -3,21 +3,14 @@ const TimeAdjustments = require('./timeAdjustments');
let workerCounter = 1;
-function Worker(skills = {dev: 1}) {
- let idle = true;
- const id = workerCounter++;
-
- function canWorkOn(skill) {
- if (!idle) return 0;
- return workSpeedFor(skill);
+function SimpleSkillStrategy(skills) {
+ function workSpeedFor({ skill }) {
+ return skills[skill] || skills['all'] || skills['rest'] || skills['fs'] || skills['fullstack'];
}
- let worker = {
- canWorkOn,
- startWorkingOn,
- name: renderName,
- id
- };
+ function startWorkOn(workCriteria) {
+ // nothing to do
+ }
function renderName() {
function renderSkills() {
@@ -27,22 +20,64 @@ function Worker(skills = {dev: 1}) {
return `${renderSkills()}`;
}
- function workSpeedFor(skill) {
- return skills[skill] || skills['all'] || skills['rest'] || skills['fs'] || skills['fullstack'];
+ return {
+ workSpeedFor,
+ startWorkOn,
+ renderName
+ };
+}
+
+function DontWorkOnSameItemStrategy(strategy) {
+ const itemsWorkedOn = [];
+
+ function workSpeedFor(workCriteria) {
+ if (itemsWorkedOn.includes(workCriteria.item)) return 0;
+ return strategy.workSpeedFor(workCriteria);
+ }
+
+ function startWorkOn(workCriteria) {
+ itemsWorkedOn.push(workCriteria.item);
+ strategy.startWorkOn(workCriteria);
+ }
+
+ return {
+ workSpeedFor,
+ startWorkOn,
+ renderName: strategy.renderName
+ };
+
+}
+
+function Worker(strategy) {
+ let idle = true;
+ const id = workerCounter++;
+
+ function canWorkOn(workCriteria) {
+ if (!idle) return 0;
+ return strategy.workSpeedFor(workCriteria);
}
- function calculateTimeoutFor(workItem, skill) {
- return 1000 * TimeAdjustments.multiplicator() * workItem.work[skill] / workSpeedFor(skill);
+ let worker = {
+ canWorkOn,
+ startWorkingOn,
+ name: strategy.renderName,
+ id
+ };
+
+ function calculateTimeoutFor(workCriteria) {
+ const { item, skill } = workCriteria;
+ return 1000 * TimeAdjustments.multiplicator() * item.work[skill] / strategy.workSpeedFor(workCriteria);
}
- function startWorkingOn(inbox, inProgress, outbox) {
- let item = inbox.peek();
+ function startWorkingOn({ inbox, inProgress, outbox, item }) {
if (item) {
idle = false;
PubSub.publish('worker.working', worker);
let skill = inProgress.necessarySkill;
inbox.move(inProgress, item);
- let timeout = calculateTimeoutFor(item, skill);
+ const workCriteria = { item, skill };
+ let timeout = calculateTimeoutFor(workCriteria);
+ strategy.startWorkOn(workCriteria);
setTimeout(() => {
idle = true;
inProgress.move(outbox, item);
@@ -107,4 +142,4 @@ function WorkList(skill = "dev") {
return column;
}
-module.exports = {Worker, WorkItem, WorkList};
+module.exports = {Worker, WorkItem, WorkList, SimpleSkillStrategy, DontWorkOnSameItemStrategy};