feat: enhance schedule import functionality with support for day sections and restrictions

This commit is contained in:
2026-09-16 23:16:35 +03:00
parent 595ad6456b
commit d32c15c85e
7 changed files with 174 additions and 14 deletions

View File

@@ -162,3 +162,40 @@ test('weekend day restrictions split correctly and contradictory day sections ar
const invalid = parseSchedule(fixture('route9').replace('по пятницам', 'по субботам'), annotated.url);
assert.throws(() => buildDirectionPlan(invalid, 'Швейная фабрика', state, '2026-09-16', '2026-09-16'), /противоречит/);
});
test('route 24 splits Monday–Thursday, Friday and weekends without duplicate daily departures', () => {
const source24 = parseSchedule(fixture('route24'), 'https://ap2polotsk.of.by/ap2/m-24/');
assert.deepEqual(source24.warnings, []);
assert.equal(source24.directions.length, 2);
assert.deepEqual(departureStops(source24), ['Автовокзал', 'Полоцк – Стекловолокно']);
assert.deepEqual(source24.rows.map(row => row.daysOfWeek), [[1,2,3,4], [1,2,3,4], [5], [5], [], []]);
for (const direction of source24.directions) {
const snapshot24 = { route: { id: 24, route_number: '24' }, schedules: [], stops: direction.stops.map((name,i) => ({ name, stop_id: i + 1 })) };
const built = buildDirectionPlan(source24, direction.stops[0], snapshot24, '2026-09-16', '2026-09-16');
const weekdayTrips = built.changes.find(change => change.dayType === 'weekday').after;
assert.equal(new Set(weekdayTrips.map(trip => trip.departureTime)).size, weekdayTrips.length);
for (let day = 1; day <= 7; day++) {
const dayType = day <= 5 ? 'weekday' : day === 6 ? 'saturday' : 'sunday';
const active = built.changes.find(change => change.dayType === dayType).after
.filter(trip => (!trip.daysOfWeek.length || trip.daysOfWeek.includes(day)) && !trip.exceptDaysOfWeek.includes(day))
.map(trip => trip.departureTime).sort();
const row = source24.rows.find(row => row.stopName === direction.stops[0] && row.days.includes(dayType)
&& (!row.daysOfWeek.length || row.daysOfWeek.includes(day)));
assert.deepEqual(active, [...row.times].sort(), `${direction.label}, ISO day ${day}`);
}
const again = buildDirectionPlan(source24, direction.stops[0], { ...snapshot24, schedules: built.changes.map((change,i) => ({ id:i+1, day_type:change.dayType, trips:change.after, valid_from:'2026-09-16' })) }, '2026-09-16', '2026-09-16');
assert.equal(again.hasChanges, false);
}
});
test('section restrictions intersect trip notes and overlapping sections are rejected', () => {
const parsed = html => parseSchedule(html, 'https://ap2polotsk.of.by/ap2/m-24/');
const state = { route:{id:24,route_number:'24'}, schedules:[], stops:[{stop_id:1,name:'Автовокзал'},{stop_id:2,name:'Полоцк – Стекловолокно'}] };
const build = html => buildDirectionPlan(parsed(html), 'Автовокзал', state, '2026-09-16', '2026-09-16');
const narrowed = build(fixture('route24').replace('17:00', '17:00 кроме среды'));
assert.deepEqual(narrowed.changes[0].after.find(t => t.departureTime === '17:00:00').daysOfWeek, [1,2,4]);
assert.throws(() => build(fixture('route24').replace('17:00','17:00 по пятницам')), /противоречит/);
assert.throws(() => build(fixture('route24').replace('Понедельник – четверг','Понедельник – пятница')), /повторяющийся/);
for (const label of ['Понедельник-четверг', 'Понедельник — четверг']) assert.deepEqual(parsed(fixture('route24').replace('Понедельник – четверг',label)).warnings, []);
assert.ok(parsed(fixture('route24').replace('Понедельник – четверг','Четверг – понедельник')).warnings.length);
});