feat(algo): slotsPerDay parameter for top-N selection
This commit is contained in:
1 parent
790ea72987
commit
625f9c7037
2 files changed
+39
-2
No files matched your search
+6
-2
@@ -77,7 +77,8 @@ function scoreCandidate(employee, isoDate, allHistory, randomFn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns { assignments: [{ date, employeeId, manual: false }], skippedDates: [] }
|
// Returns { assignments: [{ date, employeeId, manual: false }], skippedDates: [] }
|
||||||
export function generatePlan(workdays, employees, history, randomFn = Math.random) {
|
// slotsPerDay controls how many top-scored candidates are picked per day (default 1).
|
||||||
|
export function generatePlan(workdays, employees, history, randomFn = Math.random, slotsPerDay = 1) {
|
||||||
const monthAssignments = [];
|
const monthAssignments = [];
|
||||||
const skippedDates = [];
|
const skippedDates = [];
|
||||||
|
|
||||||
@@ -91,7 +92,10 @@ export function generatePlan(workdays, employees, history, randomFn = Math.rando
|
|||||||
.map(e => ({ employee: e, score: scoreCandidate(e, date, history, randomFn) }))
|
.map(e => ({ employee: e, score: scoreCandidate(e, date, history, randomFn) }))
|
||||||
.sort((a, b) => b.score - a.score);
|
.sort((a, b) => b.score - a.score);
|
||||||
|
|
||||||
monthAssignments.push({ date, employeeId: scored[0].employee.id, manual: false });
|
const take = Math.min(slotsPerDay, scored.length);
|
||||||
|
for (let i = 0; i < take; i++) {
|
||||||
|
monthAssignments.push({ date, employeeId: scored[i].employee.id, manual: false });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { assignments: monthAssignments, skippedDates };
|
return { assignments: monthAssignments, skippedDates };
|
||||||
|
|||||||
@@ -121,4 +121,37 @@ function emp(id, constraints = {}) {
|
|||||||
assert.equal(result.assignments[0].manual, false, 'auto-assignment sets manual: false');
|
assert.equal(result.assignments[0].manual, false, 'auto-assignment sets manual: false');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// slotsPerDay=2 → 2 different employees per day
|
||||||
|
{
|
||||||
|
const employees = [emp('alice'), emp('bob'), emp('clara')];
|
||||||
|
const result = generatePlan(['2026-04-01'], employees, [], fixed, 2);
|
||||||
|
assert.equal(result.assignments.length, 2, 'slotsPerDay=2 → 2 entries');
|
||||||
|
const ids = result.assignments.map(a => a.employeeId);
|
||||||
|
assert.notEqual(ids[0], ids[1], 'two different employees on same day');
|
||||||
|
}
|
||||||
|
|
||||||
|
// slotsPerDay=2 across multiple days, maxPerMonth still respected
|
||||||
|
{
|
||||||
|
const days = ['2026-04-01','2026-04-02','2026-04-03'];
|
||||||
|
const employees = [
|
||||||
|
emp('alice', { maxPerMonth: 1 }),
|
||||||
|
emp('bob'),
|
||||||
|
emp('clara'),
|
||||||
|
emp('dave')
|
||||||
|
];
|
||||||
|
const result = generatePlan(days, employees, [], fixed, 2);
|
||||||
|
const aliceCount = result.assignments.filter(a => a.employeeId === 'alice').length;
|
||||||
|
assert.ok(aliceCount <= 1, 'maxPerMonth=1 respected with slotsPerDay=2');
|
||||||
|
}
|
||||||
|
|
||||||
|
// slotsPerDay=2: per day, no employee picked twice
|
||||||
|
{
|
||||||
|
const employees = [emp('alice'), emp('bob'), emp('clara')];
|
||||||
|
const result = generatePlan(['2026-04-01','2026-04-02'], employees, [], fixed, 2);
|
||||||
|
for (const date of ['2026-04-01','2026-04-02']) {
|
||||||
|
const ids = result.assignments.filter(a => a.date === date).map(a => a.employeeId);
|
||||||
|
assert.equal(new Set(ids).size, ids.length, 'no employee picked twice on ' + date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log('All algorithm tests passed.');
|
console.log('All algorithm tests passed.');
|
||||||
Reference in new issue
Block a user