@happychef/algorithm 1.2.10 → 1.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci-cd.yml +234 -103
- package/BRANCH_PROTECTION_SETUP.md +167 -0
- package/README.md +144 -144
- package/__tests__/filters.test.js +276 -276
- package/__tests__/isDateAvailable.test.js +175 -175
- package/__tests__/isTimeAvailable.test.js +168 -168
- package/__tests__/restaurantData.test.js +422 -422
- package/__tests__/tableHelpers.test.js +247 -247
- package/assignTables.js +149 -123
- package/changes/2025/December/PR4___.md +16 -0
- package/changes/2025/December/PR5___.md +16 -0
- package/changes/2025/December/PR6__del_.md +18 -0
- package/changes/2025/December/PR7_add__change.md +22 -0
- package/jest.config.js +23 -23
- package/package.json +1 -1
- package/tableHelpers.js +2 -2
- package/test-detailed-filter.js +100 -100
- package/test-lunch-debug.js +110 -110
- package/test-max-arrivals-filter.js +79 -79
- package/test-timezone-debug.js +47 -47
package/assignTables.js
CHANGED
|
@@ -59,7 +59,7 @@ function isTemporaryTableValid(table, reservationDateStr, reservationTimeStr) {
|
|
|
59
59
|
if (!table.startDate || !table.endDate) {
|
|
60
60
|
return false; // Invalid temporary table definition
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
// Basic date string comparison (YYYY-MM-DD format)
|
|
64
64
|
if (reservationDateStr < table.startDate || reservationDateStr > table.endDate) {
|
|
65
65
|
return false;
|
|
@@ -70,7 +70,7 @@ function isTemporaryTableValid(table, reservationDateStr, reservationTimeStr) {
|
|
|
70
70
|
if (!reservationMealType) {
|
|
71
71
|
return false; // Cannot determine meal type for the reservation
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
if (table.application !== reservationMealType) {
|
|
75
75
|
return false;
|
|
76
76
|
}
|
|
@@ -89,7 +89,7 @@ function computeRequiredSlots(timeString, durationMinutes, intervalMinutes) {
|
|
|
89
89
|
if (!timePattern.test(timeString)) {
|
|
90
90
|
throw new Error("Invalid time format. Expected 'HH:MM'.");
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
const [hour, minute] = timeString.split(":").map(Number);
|
|
94
94
|
const startMinutes = hour * 60 + minute;
|
|
95
95
|
const slotCount = Math.ceil(durationMinutes / intervalMinutes);
|
|
@@ -99,7 +99,7 @@ function computeRequiredSlots(timeString, durationMinutes, intervalMinutes) {
|
|
|
99
99
|
}
|
|
100
100
|
return slots;
|
|
101
101
|
}
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
/**
|
|
104
104
|
* isTableFreeForAllSlots(tableNumber, requiredSlots, tableOccupiedSlots)
|
|
105
105
|
* Checks if the given tableNumber is free (no overlapping) for all requiredSlots.
|
|
@@ -109,7 +109,7 @@ function isTableFreeForAllSlots(tableNumber, requiredSlots, tableOccupiedSlots)
|
|
|
109
109
|
// If any slot from requiredSlots is in occupiedSlots, the table is not free
|
|
110
110
|
return !requiredSlots.some(slot => occupiedSlots.has(slot));
|
|
111
111
|
}
|
|
112
|
-
|
|
112
|
+
|
|
113
113
|
/**
|
|
114
114
|
* calculateDistance(tableA, tableB)
|
|
115
115
|
* Euclidean distance between two tables (for multi-table distance minimization).
|
|
@@ -119,7 +119,7 @@ function calculateDistance(tableA, tableB) {
|
|
|
119
119
|
const dy = tableA.y - tableB.y;
|
|
120
120
|
return Math.sqrt(dx * dx + dy * dy);
|
|
121
121
|
}
|
|
122
|
-
|
|
122
|
+
|
|
123
123
|
function distanceSum(set) {
|
|
124
124
|
let sum = 0;
|
|
125
125
|
for (let i = 0; i < set.length; i++) {
|
|
@@ -196,7 +196,20 @@ function findMultiTableCombination(
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Gets the floor ID linked to a seat place from seatAreaFloorLinks.
|
|
201
|
+
* @param {Object} restaurantSettings - The restaurant settings object.
|
|
202
|
+
* @param {string} seatPlace - The seat place identifier.
|
|
203
|
+
* @returns {string|null} The floor ID or null if not found.
|
|
204
|
+
*/
|
|
205
|
+
function getFloorIdForSeatPlace(restaurantSettings, seatPlace) {
|
|
206
|
+
if (!seatPlace || !restaurantSettings) return null;
|
|
207
|
+
const seatAreaFloorLinks = restaurantSettings["general-settings"]?.seatAreaFloorLinks;
|
|
208
|
+
if (!seatAreaFloorLinks || typeof seatAreaFloorLinks !== 'object') return null;
|
|
209
|
+
return seatAreaFloorLinks[seatPlace] || null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
200
213
|
/**
|
|
201
214
|
* assignTablesIfPossible
|
|
202
215
|
* Attempts to assign tables to a reservation based on availability and constraints.
|
|
@@ -211,7 +224,8 @@ async function assignTablesIfPossible({
|
|
|
211
224
|
const date = reservation.date;
|
|
212
225
|
const time = reservation.time;
|
|
213
226
|
const guests = reservation.guests;
|
|
214
|
-
|
|
227
|
+
const zitplaats = reservation.zitplaats;
|
|
228
|
+
|
|
215
229
|
// 1) First, fetch restaurant data (which contains the floors information)
|
|
216
230
|
const restaurantSettings = await db.collection('restaurants').findOne({ _id: restaurantId });
|
|
217
231
|
if (!restaurantSettings) {
|
|
@@ -221,9 +235,9 @@ async function assignTablesIfPossible({
|
|
|
221
235
|
return; // Non-enforcing: skip table assignment
|
|
222
236
|
}
|
|
223
237
|
}
|
|
224
|
-
|
|
238
|
+
|
|
225
239
|
// 2) Get floors data directly from the restaurant document
|
|
226
|
-
|
|
240
|
+
let floorsData = restaurantSettings.floors;
|
|
227
241
|
if (!floorsData || !Array.isArray(floorsData) || floorsData.length === 0) {
|
|
228
242
|
if (enforceTableAvailability) {
|
|
229
243
|
throw new Error('No floors data found in restaurant document.');
|
|
@@ -231,31 +245,51 @@ async function assignTablesIfPossible({
|
|
|
231
245
|
return; // Non-enforcing: skip table assignment
|
|
232
246
|
}
|
|
233
247
|
}
|
|
234
|
-
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
tableNumber: parseInt(tbl.tableNumber.$numberInt || tbl.tableNumber),
|
|
244
|
-
minCapacity: parseInt(tbl.minCapacity.$numberInt || tbl.minCapacity),
|
|
245
|
-
maxCapacity: parseInt(tbl.maxCapacity.$numberInt || tbl.maxCapacity),
|
|
246
|
-
priority: parseInt(tbl.priority.$numberInt || tbl.priority),
|
|
247
|
-
x: parseInt(tbl.x.$numberInt || tbl.x),
|
|
248
|
-
y: parseInt(tbl.y.$numberInt || tbl.y),
|
|
249
|
-
isTemporary: tbl.isTemporary === true, // Added for temporary tables
|
|
250
|
-
startDate: tbl.startDate || null, // Added for temporary tables
|
|
251
|
-
endDate: tbl.endDate || null, // Added for temporary tables
|
|
252
|
-
application: tbl.application || null // Added for temporary tables
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
});
|
|
248
|
+
|
|
249
|
+
// 2.5) If zitplaats is specified and has a floor link, try that floor first
|
|
250
|
+
const linkedFloorId = getFloorIdForSeatPlace(restaurantSettings, zitplaats);
|
|
251
|
+
let preferredFloorOnly = null;
|
|
252
|
+
if (linkedFloorId) {
|
|
253
|
+
const linkedFloor = floorsData.find(f => f.id === linkedFloorId);
|
|
254
|
+
if (linkedFloor) {
|
|
255
|
+
preferredFloorOnly = [linkedFloor];
|
|
256
|
+
console.log(`[Zitplaats] Will try floor '${linkedFloorId}' first for zitplaats '${zitplaats}'`);
|
|
256
257
|
}
|
|
257
|
-
}
|
|
258
|
-
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 3) Helper to collect tables from floors
|
|
261
|
+
function collectTablesFromFloors(floors) {
|
|
262
|
+
const tables = [];
|
|
263
|
+
floors.forEach(floor => {
|
|
264
|
+
if (floor.tables && Array.isArray(floor.tables)) {
|
|
265
|
+
floor.tables.forEach(tbl => {
|
|
266
|
+
if (tbl.objectType === "Tafel") {
|
|
267
|
+
tables.push({
|
|
268
|
+
tableId: tbl.id,
|
|
269
|
+
tableNumber: parseInt(tbl.tableNumber.$numberInt || tbl.tableNumber),
|
|
270
|
+
minCapacity: parseInt(tbl.minCapacity.$numberInt || tbl.minCapacity),
|
|
271
|
+
maxCapacity: parseInt(tbl.maxCapacity.$numberInt || tbl.maxCapacity),
|
|
272
|
+
priority: parseInt(tbl.priority.$numberInt || tbl.priority),
|
|
273
|
+
x: parseInt(tbl.x.$numberInt || tbl.x),
|
|
274
|
+
y: parseInt(tbl.y.$numberInt || tbl.y),
|
|
275
|
+
isTemporary: tbl.isTemporary === true,
|
|
276
|
+
startDate: tbl.startDate || null,
|
|
277
|
+
endDate: tbl.endDate || null,
|
|
278
|
+
application: tbl.application || null
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
return tables;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Collect all tables from all floors
|
|
288
|
+
let allTables = collectTablesFromFloors(floorsData);
|
|
289
|
+
|
|
290
|
+
// Collect tables from preferred floor only (if specified)
|
|
291
|
+
let preferredFloorTables = preferredFloorOnly ? collectTablesFromFloors(preferredFloorOnly) : null;
|
|
292
|
+
|
|
259
293
|
// 4) If no tables found
|
|
260
294
|
if (!allTables.length) {
|
|
261
295
|
if (enforceTableAvailability) {
|
|
@@ -264,44 +298,33 @@ async function assignTablesIfPossible({
|
|
|
264
298
|
return; // Non-enforcing: skip table assignment
|
|
265
299
|
}
|
|
266
300
|
}
|
|
267
|
-
|
|
268
|
-
// 5)
|
|
269
|
-
allTables.sort((a, b) => {
|
|
270
|
-
if (a.maxCapacity !== b.maxCapacity) {
|
|
271
|
-
return a.maxCapacity - b.maxCapacity;
|
|
272
|
-
}
|
|
273
|
-
if (a.priority !== b.priority) {
|
|
274
|
-
return a.priority - b.priority;
|
|
275
|
-
}
|
|
276
|
-
return a.minCapacity - b.minCapacity;
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
// 6) Get duration and interval settings
|
|
301
|
+
|
|
302
|
+
// 5) Get duration and interval settings
|
|
280
303
|
const duurReservatie = parseInt(
|
|
281
304
|
restaurantSettings["general-settings"]?.duurReservatie?.$numberInt || restaurantSettings["general-settings"]?.duurReservatie || 120
|
|
282
305
|
);
|
|
283
306
|
const intervalReservatie = parseInt(
|
|
284
307
|
restaurantSettings["general-settings"]?.intervalReservatie?.$numberInt || restaurantSettings["general-settings"]?.intervalReservatie || 30
|
|
285
308
|
);
|
|
286
|
-
|
|
309
|
+
|
|
287
310
|
// 7) Compute the requiredSlots for this reservation
|
|
288
311
|
const requiredSlots = computeRequiredSlots(time, duurReservatie, intervalReservatie);
|
|
289
|
-
|
|
312
|
+
|
|
290
313
|
// 8) Get overlapping reservations (same date, same restaurant)
|
|
291
314
|
const overlappingReservations = await db.collection('reservations').find({
|
|
292
315
|
restaurantId: restaurantId,
|
|
293
316
|
date: date
|
|
294
317
|
}).toArray();
|
|
295
|
-
|
|
318
|
+
|
|
296
319
|
// 9) Build tableOccupiedSlots map
|
|
297
320
|
let tableOccupiedSlots = {}; // { [tableNumber]: Set([...slots]) }
|
|
298
321
|
for (let r of overlappingReservations) {
|
|
299
322
|
// No need to skip the current reservation as it's not yet inserted
|
|
300
|
-
|
|
323
|
+
|
|
301
324
|
// compute that reservation's time slots
|
|
302
325
|
const rDuration = duurReservatie; // assuming same duration
|
|
303
326
|
const rSlots = computeRequiredSlots(r.time, rDuration, intervalReservatie);
|
|
304
|
-
|
|
327
|
+
|
|
305
328
|
if (r.tables) {
|
|
306
329
|
for (let tn of r.tables) {
|
|
307
330
|
if (!tableOccupiedSlots[tn]) {
|
|
@@ -313,86 +336,89 @@ async function assignTablesIfPossible({
|
|
|
313
336
|
}
|
|
314
337
|
}
|
|
315
338
|
}
|
|
316
|
-
|
|
317
|
-
try
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
requiredSlots,
|
|
325
|
-
tableOccupiedSlots,
|
|
326
|
-
isTemporaryTableValid,
|
|
327
|
-
isTableFreeForAllSlots,
|
|
339
|
+
|
|
340
|
+
// 10) Helper function to try table assignment from a given set of tables
|
|
341
|
+
function tryAssignTables(tables, label) {
|
|
342
|
+
// Sort tables
|
|
343
|
+
const sortedTables = [...tables].sort((a, b) => {
|
|
344
|
+
if (a.maxCapacity !== b.maxCapacity) return a.maxCapacity - b.maxCapacity;
|
|
345
|
+
if (a.priority !== b.priority) return a.priority - b.priority;
|
|
346
|
+
return a.minCapacity - b.minCapacity;
|
|
328
347
|
});
|
|
329
348
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
349
|
+
// Try grouping first
|
|
350
|
+
try {
|
|
351
|
+
const groupingResult = tryGroupTables({
|
|
352
|
+
restaurantSettings,
|
|
353
|
+
allTables: sortedTables,
|
|
354
|
+
guests,
|
|
355
|
+
date,
|
|
356
|
+
time,
|
|
357
|
+
requiredSlots,
|
|
358
|
+
tableOccupiedSlots,
|
|
359
|
+
isTemporaryTableValid,
|
|
360
|
+
isTableFreeForAllSlots,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
if (groupingResult) {
|
|
364
|
+
reservation.tables = groupingResult.tables;
|
|
365
|
+
reservation.tableIds = groupingResult.tableIds;
|
|
366
|
+
reservation._viaGroup = groupingResult.viaGroup;
|
|
367
|
+
console.log(`[${label}] [Grouping] via '${reservation._viaGroup}' -> tables ${reservation.tables.join(",")}`);
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
} catch (err) {
|
|
371
|
+
throw err;
|
|
338
372
|
}
|
|
339
|
-
} catch (err) {
|
|
340
|
-
// strict grouping throws; preserve original behavior
|
|
341
|
-
throw err;
|
|
342
|
-
}
|
|
343
|
-
// ===== end 9.5) Table Grouping Attempt =====
|
|
344
373
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
374
|
+
// Try single-table assignment
|
|
375
|
+
for (let t of sortedTables) {
|
|
376
|
+
if (!isTemporaryTableValid(t, date, time)) continue;
|
|
377
|
+
if (t.minCapacity <= guests && guests <= t.maxCapacity &&
|
|
378
|
+
isTableFreeForAllSlots(t.tableNumber, requiredSlots, tableOccupiedSlots)) {
|
|
379
|
+
reservation.tables = [t.tableNumber];
|
|
380
|
+
reservation.tableIds = [t.tableId];
|
|
381
|
+
console.log(`[${label}] Assigned to table: ${t.tableNumber}`);
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
350
384
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
) {
|
|
357
|
-
|
|
358
|
-
reservation.
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return; // Assignment successful
|
|
385
|
+
|
|
386
|
+
// Try multi-table combination
|
|
387
|
+
let best = { minDistance: Infinity, tables: [] };
|
|
388
|
+
findMultiTableCombination(sortedTables, guests, 0, [], best, requiredSlots, tableOccupiedSlots, date, time);
|
|
389
|
+
|
|
390
|
+
if (best.tables.length > 0) {
|
|
391
|
+
reservation.tables = best.tables.map(t => t.tableNumber);
|
|
392
|
+
reservation.tableIds = best.tables.map(t => t.tableId);
|
|
393
|
+
console.log(`[${label}] Assigned to tables: ${reservation.tables.join(', ')}`);
|
|
394
|
+
return true;
|
|
362
395
|
}
|
|
396
|
+
|
|
397
|
+
return false;
|
|
363
398
|
}
|
|
364
|
-
|
|
365
|
-
// 11)
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
guests,
|
|
371
|
-
0,
|
|
372
|
-
[],
|
|
373
|
-
best,
|
|
374
|
-
requiredSlots,
|
|
375
|
-
tableOccupiedSlots,
|
|
376
|
-
date, // Pass date for temporary table validation
|
|
377
|
-
time // Pass time for temporary table validation
|
|
378
|
-
);
|
|
379
|
-
|
|
380
|
-
if (best.tables.length > 0) {
|
|
381
|
-
// Assign the best combination to the reservation
|
|
382
|
-
reservation.tables = best.tables.map(t => t.tableNumber);
|
|
383
|
-
reservation.tableIds = best.tables.map(t => t.tableId);
|
|
384
|
-
console.log(`Reservation assigned to tables: ${reservation.tables.join(', ')}`);
|
|
385
|
-
return; // Assignment successful
|
|
386
|
-
} else {
|
|
387
|
-
// If no valid table combo found, either fail (if enforce) or do nothing (if not enforce)
|
|
388
|
-
if (enforceTableAvailability) {
|
|
389
|
-
throw new Error('Unable to find enough tables for this reservation with enforcement on.');
|
|
390
|
-
} else {
|
|
391
|
-
console.log('No tables available, but non-enforcing mode => continuing without assignment.');
|
|
399
|
+
|
|
400
|
+
// 11) Try preferred floor first (if specified), then fall back to all floors
|
|
401
|
+
if (preferredFloorTables && preferredFloorTables.length > 0) {
|
|
402
|
+
console.log(`[Zitplaats] Trying preferred floor first (${preferredFloorTables.length} tables)`);
|
|
403
|
+
if (tryAssignTables(preferredFloorTables, 'PreferredFloor')) {
|
|
404
|
+
return; // Success on preferred floor
|
|
392
405
|
}
|
|
406
|
+
console.log(`[Zitplaats] No availability on preferred floor, falling back to all floors`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// 12) Try all floors
|
|
410
|
+
if (tryAssignTables(allTables, 'AllFloors')) {
|
|
411
|
+
return; // Success
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// 13) No valid table combo found
|
|
415
|
+
if (enforceTableAvailability) {
|
|
416
|
+
throw new Error('Unable to find enough tables for this reservation with enforcement on.');
|
|
417
|
+
} else {
|
|
418
|
+
console.log('No tables available, but non-enforcing mode => continuing without assignment.');
|
|
393
419
|
}
|
|
394
420
|
}
|
|
395
|
-
|
|
421
|
+
|
|
396
422
|
module.exports = {
|
|
397
423
|
assignTablesIfPossible
|
|
398
|
-
};
|
|
424
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# PR 4 - TEST: Add intentionally failing tests
|
|
2
|
+
|
|
3
|
+
**Actions:**
|
|
4
|
+
|
|
5
|
+
## Changes Summary
|
|
6
|
+
NO_ADDITIONS
|
|
7
|
+
|
|
8
|
+
NO_REMOVALS
|
|
9
|
+
|
|
10
|
+
NO_CHANGES
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
**Author:** houssammk123
|
|
15
|
+
**Date:** 2025-12-09
|
|
16
|
+
**PR Link:** https://github.com/thibaultvandesompele2/15-happy-algorithm/pull/4
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# PR 5 - TEST: Add passing helper tests
|
|
2
|
+
|
|
3
|
+
**Actions:**
|
|
4
|
+
|
|
5
|
+
## Changes Summary
|
|
6
|
+
NO_ADDITIONS
|
|
7
|
+
|
|
8
|
+
NO_REMOVALS
|
|
9
|
+
|
|
10
|
+
NO_CHANGES
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
**Author:** houssammk123
|
|
15
|
+
**Date:** 2025-12-09
|
|
16
|
+
**PR Link:** https://github.com/thibaultvandesompele2/15-happy-algorithm/pull/5
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# PR 6 - Fix remove meaningless tests
|
|
2
|
+
|
|
3
|
+
**Actions:**
|
|
4
|
+
|
|
5
|
+
## Changes Summary
|
|
6
|
+
NO_ADDITIONS
|
|
7
|
+
|
|
8
|
+
REMOVED:
|
|
9
|
+
- Removed test case `test_remove_meaningless_tests` from the test suite.
|
|
10
|
+
- Removed the entire test file `test_remove_meaningless_tests.py` from the repository.
|
|
11
|
+
|
|
12
|
+
NO_CHANGES
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
**Author:** houssammk123
|
|
17
|
+
**Date:** 2025-12-09
|
|
18
|
+
**PR Link:** https://github.com/thibaultvandesompele2/15-happy-algorithm/pull/6
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# PR 7 - Fix/infinite loop table assignment
|
|
2
|
+
|
|
3
|
+
**Actions:**
|
|
4
|
+
|
|
5
|
+
## Changes Summary
|
|
6
|
+
ADDED:
|
|
7
|
+
- New function `findTableForParty(tables, partySize)` added to handle table assignment logic.
|
|
8
|
+
- New variable `assignedTables` introduced to track which tables have been assigned.
|
|
9
|
+
- New import `itertools` added to support combination generation for table selection.
|
|
10
|
+
|
|
11
|
+
NO_REMOVALS
|
|
12
|
+
|
|
13
|
+
CHANGED:
|
|
14
|
+
- Modified the `assignTables` function in `src/tableAssignment.js` to replace the `while` loop with a `for` loop that iterates over a pre-calculated `availableTables` array to prevent infinite loops.
|
|
15
|
+
- Updated the table selection logic inside the loop from a random index selection (`Math.floor(Math.random() * availableTables.length)`) to a sequential index selection using the loop counter `i`.
|
|
16
|
+
- Changed the condition for breaking out of the assignment loop from checking `remainingGuests > 0` to checking if `availableTables.length > 0` and if the current table's capacity is sufficient for the party size.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
**Author:** houssammk123
|
|
21
|
+
**Date:** 2025-12-12
|
|
22
|
+
**PR Link:** https://github.com/thibaultvandesompele2/15-happy-algorithm/pull/7
|
package/jest.config.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
testEnvironment: 'node',
|
|
3
|
-
coverageDirectory: 'coverage',
|
|
4
|
-
collectCoverageFrom: [
|
|
5
|
-
'**/*.js',
|
|
6
|
-
'!**/node_modules/**',
|
|
7
|
-
'!**/coverage/**',
|
|
8
|
-
'!jest.config.js',
|
|
9
|
-
'!test.js',
|
|
10
|
-
'!test-*.js',
|
|
11
|
-
'!**/__tests__/**'
|
|
12
|
-
],
|
|
13
|
-
testMatch: [
|
|
14
|
-
'**/__tests__/**/*.test.js'
|
|
15
|
-
],
|
|
16
|
-
testPathIgnorePatterns: [
|
|
17
|
-
'/node_modules/',
|
|
18
|
-
'/test\\.js$',
|
|
19
|
-
'/test-.*\\.js$'
|
|
20
|
-
],
|
|
21
|
-
verbose: true,
|
|
22
|
-
testTimeout: 10000
|
|
23
|
-
};
|
|
1
|
+
module.exports = {
|
|
2
|
+
testEnvironment: 'node',
|
|
3
|
+
coverageDirectory: 'coverage',
|
|
4
|
+
collectCoverageFrom: [
|
|
5
|
+
'**/*.js',
|
|
6
|
+
'!**/node_modules/**',
|
|
7
|
+
'!**/coverage/**',
|
|
8
|
+
'!jest.config.js',
|
|
9
|
+
'!test.js',
|
|
10
|
+
'!test-*.js',
|
|
11
|
+
'!**/__tests__/**'
|
|
12
|
+
],
|
|
13
|
+
testMatch: [
|
|
14
|
+
'**/__tests__/**/*.test.js'
|
|
15
|
+
],
|
|
16
|
+
testPathIgnorePatterns: [
|
|
17
|
+
'/node_modules/',
|
|
18
|
+
'/test\\.js$',
|
|
19
|
+
'/test-.*\\.js$'
|
|
20
|
+
],
|
|
21
|
+
verbose: true,
|
|
22
|
+
testTimeout: 10000
|
|
23
|
+
};
|
package/package.json
CHANGED
package/tableHelpers.js
CHANGED
|
@@ -171,8 +171,8 @@ function isTemporaryTableValid(table, reservationDateStr, reservationTimeStr) {
|
|
|
171
171
|
// Use CommonJS exports (adjust if using ES6 modules)
|
|
172
172
|
module.exports = {
|
|
173
173
|
shifts,
|
|
174
|
-
parseTime,
|
|
174
|
+
parseTime,
|
|
175
175
|
getMealTypeByTime,
|
|
176
176
|
getAllTables,
|
|
177
177
|
isTemporaryTableValid
|
|
178
|
-
};
|
|
178
|
+
};
|