mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(roadtrip): a flight day ends at the stay it lands at, and no drive runs where nobody drives
On a day a flight, train, ferry, cruise or bus leaves and lands, tonight's stay was seated by its check-in time. A hotel in Munich checked into from 15:00 stood ahead of a 15:15 flight out of Hamburg, so the drive went from the Munich hotel to Hamburg airport and reported the flight as nine hours late. A check-in is the earliest arrival, not an appointment: such a stay now waits behind the landing when it lies nearer the arrival than the departure, and its check-in only holds it when the ride lands before the room is ready. A time typed on the stop keeps its seat. With the stays at both ends of a day switched on: - a day with nothing but the next stay's check-in and a ride booked across it gets no drive from one hotel to the other, the same rule the day plan follows since #2476; - a stay is never joined by road to a terminal more than 2000 km away. The check moved to shared/roadtrip so Days and the road trip read one copy. A departure terminal's pin is dated: reaching it more than half a day late no longer moves the flight to the next day without a word, it is a late warning with the full minutes. A check-in evening also carries the stay's check-in time, as a label only. calculate_roadtrip runs the same code and its description says all of the above.
This commit is contained in:
@@ -458,6 +458,7 @@ describe('a booked night at both ends of its days', () => {
|
||||
checkingOut: false,
|
||||
checkingIn: true,
|
||||
checkOut: null,
|
||||
checkIn: '14:00',
|
||||
});
|
||||
expect(back).toMatchObject({ name: 'Getaway', lat: GETAWAY.lat, lng: GETAWAY.lng, stopType: 'hotel', time: null });
|
||||
expect(back.assignmentId).toBe(bookendAssignmentId(days[0].id, 'evening'));
|
||||
@@ -591,4 +592,75 @@ describe('a booked night at both ends of its days', () => {
|
||||
});
|
||||
expect(seated.failures[99].toAssignmentId).toBeLessThan(-6_000_000_000);
|
||||
});
|
||||
|
||||
/**
|
||||
* Trip 45 on the dev instance: a night in Hamburg, then LH 2078 to Munich at 15:15 on the
|
||||
* day the Munich hotel is checked into from 15:00, the booking's slot seeded behind it.
|
||||
*/
|
||||
function trip45(settings?: RoadtripPreferences) {
|
||||
const t = trip(settings);
|
||||
const days = [1, 2, 3, 4, 5].map(() => createDay(db, t.trip.id));
|
||||
const atlantic = visit(t.trip.id, days[0].id, 'Hotel Atlantic Hamburg', { lat: 53.5573, lng: 10.0056 });
|
||||
const munich = visit(t.trip.id, days[2].id, 'Hotel Bayerischer Hof', { lat: 48.1403, lng: 11.5732 });
|
||||
const stays = { check_in: '15:00', check_out: '11:00' };
|
||||
const hamburgStay = createDayAccommodation(db, t.trip.id, atlantic.place.id, days[0].id, days[2].id, stays);
|
||||
createDayAccommodation(db, t.trip.id, munich.place.id, days[2].id, days[4].id, stays);
|
||||
const flight = Number(
|
||||
db
|
||||
.prepare(
|
||||
`INSERT INTO reservations (trip_id, title, type, day_id, end_day_id, reservation_time, reservation_end_time, day_plan_position)
|
||||
VALUES (?, 'LH 2078 HAM-MUC', 'flight', ?, ?, '2026-11-04T15:15', '2026-11-04T17:20', 0.5)`,
|
||||
)
|
||||
.run(t.trip.id, days[2].id, days[2].id).lastInsertRowid,
|
||||
);
|
||||
const endpoint = db.prepare(
|
||||
'INSERT INTO reservation_endpoints (reservation_id, role, sequence, name, code, lat, lng) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
||||
);
|
||||
endpoint.run(flight, 'from', 0, 'Hamburg (HAM)', 'HAM', 53.630402, 9.98823);
|
||||
endpoint.run(flight, 'to', 1, 'Munich (MUC)', 'MUC', 48.353802, 11.7861);
|
||||
return { ...t, flightDay: days[2], hamburgStay };
|
||||
}
|
||||
|
||||
it('flies the day of the Munich check-in out of Hamburg and ends it at the Munich hotel, with no road between the cities (trip 45)', async () => {
|
||||
// Every run the router is asked for lies north of 53 or south of 49.
|
||||
const oneCity = (router: ReturnType<typeof hourlyRouter>) =>
|
||||
lats(router).every((run) => run.every((lat: number) => lat > 53) || run.every((lat: number) => lat < 49));
|
||||
|
||||
const off = trip45({});
|
||||
const stored = await off.plans.calculate(off.trip.id, off.user.id);
|
||||
const offDay = stored.calculated.days.find((d) => d.dayId === off.flightDay.id)!;
|
||||
expect(shape(offDay.stops)).toEqual(['Hamburg (HAM)', 'Munich (MUC)', 'Hotel Bayerischer Hof']);
|
||||
// At the airport by the check-in, at the hotel an hour after landing, and late for nothing.
|
||||
expect(offDay.schedule.entries.map((e) => e.arrival)).toEqual(['14:15', '17:20', '18:20']);
|
||||
expect(offDay.schedule.warnings).toEqual([]);
|
||||
expect(oneCity(off.router)).toBe(true);
|
||||
|
||||
const on = trip45();
|
||||
const seated = await on.plans.calculate(on.trip.id, on.user.id);
|
||||
const onDay = seated.calculated.days.find((d) => d.dayId === on.flightDay.id)!;
|
||||
expect(shape(onDay.stops)).toEqual([
|
||||
`morning:${on.hamburgStay.id}`,
|
||||
'Hamburg (HAM)',
|
||||
'Munich (MUC)',
|
||||
'Hotel Bayerischer Hof',
|
||||
]);
|
||||
expect(oneCity(on.router)).toBe(true);
|
||||
expect(seated.failures).toEqual([]);
|
||||
});
|
||||
|
||||
it('drives no road between two stays on a day a flight saved without its airports moves the traveller (#2476)', async () => {
|
||||
const { user, trip: created, router, plans, days, stayA } = cam();
|
||||
db.prepare(
|
||||
"INSERT INTO reservations (trip_id, title, type, day_id, end_day_id, reservation_time) VALUES (?, 'Flight', 'flight', ?, ?, '12:00')",
|
||||
).run(created.id, days[1].id, days[1].id);
|
||||
|
||||
const { calculated, failures } = await plans.calculate(created.id, user.id);
|
||||
|
||||
expect(calculated.days.map((d) => shape(d.stops))).toEqual([
|
||||
['Getaway', 'Lookout', 'Falls', `evening:${stayA.id}`],
|
||||
['Wallinga'],
|
||||
]);
|
||||
expect(lats(router)).toEqual([[GETAWAY.lat, -33.73, -33.65, GETAWAY.lat]]);
|
||||
expect(failures).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user